diff --git a/C/convexhull.c b/C/convexhull.c
new file mode 100644
--- /dev/null
+++ b/C/convexhull.c
@@ -0,0 +1,714 @@
+#define qh_QHimport
+#include "qhull_ra.h"
+#include "convexhull.h"
+#include "utils.h"
+
+// to use the qsort function - sort vertices according to their ids
+int cmpvertices (const void * a, const void * b) {
+   return ( (*((VertexT*)a)).id - (*((VertexT*)b)).id );
+}
+// - sort full vertices
+int cmpfullvertices (const void * a, const void * b) {
+  return ( (*((FullVertexT*)a)).id - (*((FullVertexT*)b)).id );
+}
+// - sort edges
+int cmpedges (const void * a, const void * b) {
+  if((*(unsigned**)a)[0] > (*(unsigned**)b)[0]){
+    return 1;
+  }else if((*(unsigned**)a)[0] == (*(unsigned**)b)[0]){
+    return (*(unsigned**)a)[1] - (*(unsigned**)b)[1];
+  }else{
+    return -1;
+  }
+}
+
+/* test equality of two _sorted_ arrays */
+unsigned equalarraysu(unsigned* array1, unsigned* array2, unsigned length){
+  unsigned i;
+  for(i=0; i < length; i++){
+    if(array1[i] != array2[i]){
+      break;
+    }
+  }
+  return i == length;
+}
+
+/* return ids of a vector of VertexT */
+unsigned* map_vertexid(VertexT* vertices, unsigned nvertices){
+  unsigned* ids = malloc(nvertices * sizeof(unsigned));
+  for(unsigned v=0; v < nvertices; v++){
+    ids[v] = vertices[v].id;
+  }
+  return ids;
+}
+
+/* return ids of a vector of RidgeT */
+unsigned* map_ridgeid(RidgeT* ridges, unsigned nridges){
+  unsigned* ids = malloc(nridges * sizeof(unsigned));
+  for(unsigned r=0; r < nridges; r++){
+    ids[r] = ridges[r].id;
+  }
+  return ids;
+}
+
+// void deepCopyRidge(RidgeT* src, RidgeT* dest) { et dim !
+//     dest = malloc(sizeof(RidgeT));
+//     *dest = *src;
+//     dest->vertices = malloc(src->nvertices * sizeof(VertexT));
+//     for(unsigned v=0; v<src->nvertices; v++){
+//       dest->vertices[v].id = src.vertices[v].id;
+//       memcpy(dest->vertices[v].point, dest->vertices[v].point, dim * sizeof(double));
+//     }
+// }
+
+/* deep copy of a ridge */
+RidgeT copyRidge(RidgeT ridge, unsigned dim){
+  RidgeT out;
+  out.ridgeOf1  = ridge.ridgeOf1;
+  out.ridgeOf2  = ridge.ridgeOf2;
+  out.nvertices = ridge.nvertices;
+  out.nedges    = ridge.nedges;
+  out.vertices  = malloc(out.nvertices * sizeof(VertexT));
+  for(unsigned v=0; v < out.nvertices; v++){
+    out.vertices[v].id    = ridge.vertices[v].id;
+    out.vertices[v].point = malloc(dim * sizeof(double));
+    for(unsigned i=0; i < dim; i++){
+      out.vertices[v].point[i] = ridge.vertices[v].point[i];
+    }
+  }
+  // out.edges = malloc(out.nedges * sizeof(unsigned*));
+  // for(unsigned e=0; e < out.nedges; e++){
+  //   out.edges[e] = malloc(2 * sizeof(unsigned));
+  //   out.edges[e][0] = ridge.edges[e][0];
+  //   out.edges[e][1] = ridge.edges[e][1];
+  // }
+  return out;
+}
+
+/* append to a vector of VertexT */
+void appendv(VertexT x, VertexT** array, unsigned length, unsigned* flag){
+  *flag = 1;
+  for(unsigned i=0; i < length; i++){
+    if(x.id == (*(*array + i)).id){
+      *flag = 0;
+      break;
+    }
+  }
+  if(*flag == 1){
+    *array = realloc(*array, (length+1) * sizeof(VertexT));
+    if(*array == NULL){
+      printf("realloc failure - exiting\n");
+      exit(1);
+    }
+    *(*array + length) = x;
+  }
+}
+
+/* union of two vectors of VertexT */
+void unionv(VertexT** vs1, VertexT* vs2, unsigned l1, unsigned l2, unsigned* l){
+  *l = l1;
+  for(unsigned v=0; v < l2; v++){
+    unsigned pushed;
+    appendv(vs2[v], vs1, *l, &pushed);
+    if(pushed){
+      (*l)++;
+    }
+  }
+  /* sort vertices according to their ids */
+  qsort(*vs1, *l, sizeof(VertexT), cmpvertices);
+}
+
+/* merge ridges with same ridgeOf's */
+RidgeT* mergeRidges(RidgeT* ridges, unsigned nridges, unsigned* newlength){
+  // http://www.c4learn.com/c-programs/to-delete-duplicate-elements-in-array.html
+  *newlength = nridges;
+  unsigned i,j,k;
+  for(i = 0; i < nridges; i++){
+    for(j = i+1; j < nridges; ){
+      if(ridges[i].ridgeOf1 == ridges[j].ridgeOf1 &&
+         ridges[i].ridgeOf2 == ridges[j].ridgeOf2)
+      {
+        unsigned l;
+        unionv(&(ridges[i].vertices), ridges[j].vertices,
+                 ridges[i].nvertices, ridges[j].nvertices, &l);
+        ridges[i].nvertices = l;
+        (*newlength)--;
+        for(k = j; k+1 < nridges; k++){
+          ridges[k] = ridges[k+1];
+        }
+        nridges--;
+      }else{
+        j++;
+      }
+    }
+  }
+  RidgeT* out = malloc(*newlength * sizeof(RidgeT));
+  for(unsigned r=0; r < *newlength; r++){
+    out[r] = ridges[r];
+  }
+  return out;
+}
+
+/* all ridges from the ridges stored in the faces */
+RidgeT* allRidges(FaceT *faces, unsigned nfaces, unsigned dim, unsigned* length){
+  RidgeT* out = malloc(faces[0].nridges * sizeof(RidgeT));
+  for(unsigned i=0; i < faces[0].nridges; i++){
+    out[i] = copyRidge(faces[0].ridges[i], dim);
+    out[i].id = i;
+    out[i].nedges = 0;
+    // RidgeT out[i];
+    // deepCopyRidge(&(faces[0].ridges[i]), &(out[i]));
+  }
+  *length    = faces[0].nridges;
+  unsigned n = faces[0].nridges;
+  for(unsigned f=1; f < nfaces; f++){
+    for(unsigned j=0; j < faces[f].nridges; j++){
+      unsigned count = 0;
+      for(unsigned i=0; i < n; i++){
+        unsigned flag = 0;
+        for(unsigned v=0; v < faces[f].ridges[j].nvertices; v++){
+          if(faces[f].ridges[j].vertices[v].id != out[i].vertices[v].id){
+            flag = 1;
+            break;
+          }
+        }
+        if(flag){
+          count++;
+        }else{
+          break;
+        }
+      }
+      if(count == n){
+        out          = realloc(out, (*length+1) * sizeof(RidgeT));
+        if(out == NULL){
+          printf("realloc failure - exiting\n");
+          exit(1);
+        }
+        out[*length] = copyRidge(faces[f].ridges[j], dim);
+        out[*length].id = *length;
+        out[*length].nedges = 0;
+        // RidgeT out[*length];
+        // deepCopyRidge(&(faces[f].ridges[j]), &(out[*length]));
+        (*length)++;
+      }
+    }
+    n = *length;
+  }
+  return out;
+}
+
+/* assign ids to the ridges stored in the faces */
+void assignRidgesIds(FaceT** faces, unsigned nfaces, RidgeT* allridges,
+                     unsigned nallridges)
+{
+  for(unsigned f=0; f < nfaces; f++){
+    for(unsigned fr=0; fr < (*(*faces + f)).nridges; fr++){
+      for(unsigned r=0; r < nallridges; r++){
+        if((allridges[r].nvertices == (*(*faces + f)).ridges[fr].nvertices) &&
+            equalarraysu(map_vertexid(allridges[r].vertices,
+                                      allridges[r].nvertices),
+                         map_vertexid((*(*faces + f)).ridges[fr].vertices,
+                                      allridges[r].nvertices),
+                         allridges[r].nvertices))
+        {
+          (*(*faces + f)).ridges[fr].id = allridges[r].id;
+          break;
+        }
+      }
+    }
+  }
+}
+
+// double* ridgeCentroid(RidgeT ridge, unsigned dim){
+//   double* out = malloc(dim * sizeof(double));
+//   for(unsigned i=0; i<dim; i++){
+//     out[i] = 0;
+//     for(unsigned v=0; v<dim-1; v++){
+//       out[i] += ridge.vertices[v].point[i];
+//     }
+//     out[i] /= dim - 1;
+//   }
+//   return out;
+// }
+
+/* the threshold distance to detect neighbor vertices */
+double ridgeMaxDistance(RidgeT ridge, unsigned v, unsigned dim){
+  double dists[ridge.nvertices-1];
+  unsigned count = 0;
+  for(unsigned w=0; w < ridge.nvertices; w++){
+    if(w != v){
+      dists[count] = squaredDistance(ridge.vertices[v].point,
+                                     ridge.vertices[w].point, dim);
+      count++;
+    }
+  }
+  qsort(dists, ridge.nvertices-1, sizeof(double), cmpfuncdbl);
+  return dists[1];
+}
+
+/* neighbor vertices of a vertex from all ridges, for dim>2 */
+unsigned* neighVertices(unsigned id, RidgeT* allridges, unsigned nridges,
+                        unsigned dim, unsigned triangulate, unsigned* lengthout)
+{
+  unsigned* neighs = malloc(0);
+  *lengthout = 0;
+  for(unsigned e=0; e < nridges; e++){
+    for(unsigned v=0; v < allridges[e].nvertices; v++){
+      if(id == allridges[e].vertices[v].id){
+        for(unsigned w=0; w < allridges[e].nvertices; w++){
+          if(w != v && (triangulate || dim == 3 || // dim3 pas besoin de tester la distance: il n'y a que deux vertices connectés
+             squaredDistance(allridges[e].vertices[w].point,
+                             allridges[e].vertices[v].point, dim) <=
+              ridgeMaxDistance(allridges[e], v, dim)))
+          {
+            unsigned pushed;
+            appendu(allridges[e].vertices[w].id, &neighs, *lengthout, &pushed);
+            if(pushed){
+              (*lengthout)++;
+            }
+          }
+        }
+        break;
+      }
+    }
+  }
+  return neighs;
+}
+
+/* neighbor ridges of a vertex */
+unsigned* neighRidges(unsigned id, RidgeT* allridges, unsigned nridges,
+                     unsigned* length)
+{
+  unsigned* neighs = malloc(0);
+  *length = 0;
+  for(unsigned e=0; e < nridges; e++){
+    unsigned flag = 0;
+    for(unsigned v=0; v < allridges[e].nvertices; v++){
+      if(id == allridges[e].vertices[v].id){
+        flag = 1;
+        break;
+      }
+    }
+    if(flag){
+      neighs = realloc(neighs, (*length+1)*sizeof(unsigned));
+      if(neighs == NULL){
+        printf("realloc failure - exiting\n");
+        exit(1);
+      }
+      neighs[*length] = e;
+      (*length)++;
+    }
+  }
+  return neighs;
+}
+
+/* whether distinct x1 and x2 belong to array of distinct values */
+unsigned areElementsOf(unsigned x1, unsigned x2, unsigned* array,
+                       unsigned length)
+{
+  unsigned count = 0;
+  for(unsigned i=0; (i < length) && (count < 2); i++){
+    if(x1 == array[i] || x2 == array[i]){
+      count++;
+    }
+  }
+  return count==2;
+}
+
+/* make face/ridge edges from all edges */
+unsigned** makeEdges(SetOfVerticesT face, unsigned** alledges,
+                     unsigned nalledges, unsigned* lengthout)
+{
+  *lengthout = 0;
+  unsigned* faceverticesids = map_vertexid(face.vertices, face.nvertices);
+  unsigned flags[nalledges];
+  for(unsigned e=0; e < nalledges; e++){
+    if(areElementsOf(alledges[e][0], alledges[e][1], faceverticesids,
+                     face.nvertices))
+    {
+      flags[e] = 1;
+      (*lengthout)++;
+    }else{
+      flags[e] = 0;
+    }
+  }
+  unsigned** out = malloc(*lengthout * sizeof(unsigned*));
+  unsigned count = 0;
+  for(unsigned e=0; e < nalledges; e++){
+    if(flags[e] == 1){
+      out[count] = alledges[e];
+      count++;
+    }
+  }
+  return out;
+}
+
+/* all edges from all vertices */
+unsigned** allEdges(FullVertexT* vertices, unsigned nvertices,
+                    unsigned outlength)
+{
+  unsigned** out = malloc(outlength * sizeof(unsigned*));
+  for(unsigned i=0; i < vertices[0].nneighsvertices; i++){
+    out[i] = malloc(2 * sizeof(unsigned));
+    out[i][0] = vertices[0].id;
+    out[i][1] = vertices[0].neighvertices[i];
+    qsortu(out[i], 2);
+  }
+  unsigned n = vertices[0].nneighsvertices;
+  for(unsigned v=1; v < nvertices; v++){
+    unsigned ids[2];
+    for(unsigned i=0; i < vertices[v].nneighsvertices; i++){
+      ids[0] = vertices[v].id;
+      ids[1] = vertices[v].neighvertices[i];
+      qsortu(ids, 2);
+      unsigned j;
+      for(j=0; j < n; j++){
+        if(ids[0] == out[j][0] && ids[1] == out[j][1]){
+          break;
+        }
+      }
+      if(j == n){
+        out[n] = malloc(2 * sizeof(unsigned));
+        out[n][0] = ids[0]; out[n][1] = ids[1];
+        n++;
+      }
+      if(n == outlength){
+        break;
+      }
+    }
+    if(n == outlength){
+      break;
+    }
+  }
+  return out;
+}
+
+// ATTENTION avec Qt le center dans facet->center est le centre de l'union des triangles,
+//  (ainsi que normal et offset mais ça ok)
+
+// un ridge est simplicial ; pour l'hypercube il y a 2 ridges entre 2 faces,
+// ils forment le carré à l'intersection
+
+/* main function */
+ConvexHullT* convexHull(
+	double*   points,
+	unsigned  dim,
+	unsigned  n,
+  unsigned  triangulate,
+  unsigned  print,
+  char*     summaryFile,
+	unsigned* exitcode
+)
+{
+	char opts[250]; /* option flags for qhull, see qh_opt.htm */
+  sprintf(opts, "qhull s FF %s", triangulate ? "Qt" : "");
+	qhT qh_qh;       /* Qhull's data structure */
+  qhT *qh= &qh_qh;
+  QHULL_LIB_CHECK
+  qh_meminit(qh, stderr);
+	boolT ismalloc  = False; /* True if qhull should free points in qh_freeqhull() or reallocation */
+	FILE *errfile   = NULL;
+  FILE* outfile;
+  if(print){
+    outfile = stdout;
+  }else{
+    outfile = NULL;
+  }
+  qh_zero(qh, errfile);
+	exitcode[0] = qh_new_qhull(qh, dim, n, points, ismalloc, opts, outfile,
+                             errfile);
+  //fclose(tmpstdout);
+  printf("exitcode: %u\n", exitcode[0]);
+
+  ConvexHullT* out = malloc(sizeof(ConvexHullT));
+
+	if (!exitcode[0]) {  /* 0 if no error from qhull */
+
+    /* print summary to file */
+    if(*summaryFile != 0){
+      FILE* sfile = fopen(summaryFile, "w");
+    	qh_printsummary(qh, sfile);
+    	fclose(sfile);
+    }
+
+    //qh_getarea(qh, qh->facet_list); // no triowner if I do that; do qh_facetarea, not facet->f.area
+
+    unsigned   nfaces    = qh->num_facets;
+    FaceT*     faces     = malloc(nfaces * sizeof(FaceT));
+    {
+      facetT *facet; unsigned i_facet = 0;
+      FORALLfacets{
+        facet->id                  = i_facet; /* for neighbors and ridgeOf */
+        faces[i_facet].area        = qh_facetarea(qh, facet);
+        double* center             = qh_getcenter(qh, facet->vertices);
+        faces[i_facet].center      = malloc(dim * sizeof(double));
+        for(unsigned i=0; i < dim; i++){
+          faces[i_facet].center[i] = center[i];
+        }
+        double* normal = facet->normal;
+        faces[i_facet].normal      = malloc(dim * sizeof(double));
+        for(unsigned i=0; i < dim; i++){
+          faces[i_facet].normal[i] = normal[i];
+        }
+        faces[i_facet].offset      = facet->offset;
+        faces[i_facet].nvertices   = (unsigned) qh_setsize(qh, facet->vertices);
+        { /* face vertices */
+          faces[i_facet].vertices =
+            (VertexT*) malloc(faces[i_facet].nvertices * sizeof(VertexT));
+          vertexT *vertex, **vertexp;
+          unsigned i_vertex = 0;
+          FOREACHvertex_(facet->vertices){
+            faces[i_facet].vertices[i_vertex].id =
+              (unsigned) qh_pointid(qh, vertex->point);
+            faces[i_facet].vertices[i_vertex].point =
+              malloc(dim * sizeof(double));
+            faces[i_facet].vertices[i_vertex].point =
+              getpoint(points, dim, faces[i_facet].vertices[i_vertex].id);
+            // plante dans Haskell: (faces[i_facet].vertices)[i_vertex].point = vertex->point;
+            i_vertex++;
+          }
+          qsort(faces[i_facet].vertices, faces[i_facet].nvertices,
+                sizeof(VertexT), cmpvertices);
+        }
+        if(dim == 3){ /* orientation of the normals */
+          pointT* onepoint = ((vertexT*)facet->vertices->e[0].p)->point;
+          double thepoint[dim]; /* onepoint+normal */
+          for(unsigned i=0; i < dim; i++){
+            thepoint[i] = onepoint[i] + faces[i_facet].normal[i];
+          }
+          /* we check that these two points are on the same side of the ridge */
+          double h1 = dotproduct(qh->interior_point,
+                                 faces[i_facet].normal, dim) +
+                      faces[i_facet].offset;
+          double h2 = dotproduct(thepoint, faces[i_facet].normal, dim) +
+                      faces[i_facet].offset;
+          if(h1*h2 > 0){
+            for(unsigned i=0; i < dim; i++){
+              faces[i_facet].normal[i] *= -1;
+            }
+            printf("change sign\n"); // seems to never occur
+          }else{
+            printf("not change sign\n");
+          }
+        }
+        /**/
+        i_facet++;
+      }
+    }
+
+    { /* neighbor faces, faces families, and ridges */
+      facetT *facet;
+      unsigned i_facet = 0;
+      FORALLfacets{
+        {
+          faces[i_facet].neighborsize = qh_setsize(qh, facet->neighbors);
+          faces[i_facet].neighbors =
+            malloc(faces[i_facet].neighborsize * sizeof(unsigned));
+          unsigned i_neighbor = 0;
+          facetT *neighbor, **neighborp;
+          FOREACHneighbor_(facet){
+            faces[i_facet].neighbors[i_neighbor] = (unsigned) neighbor->id;
+            i_neighbor++;
+          }
+          qsortu(faces[i_facet].neighbors, faces[i_facet].neighborsize);
+        }
+        { /* face family, when option Qt */
+          if(facet->tricoplanar){
+            faces[i_facet].family = facet->f.triowner->id;
+          }else{
+            faces[i_facet].family = -1;
+          }
+        }
+        { /* face ridges */
+          qh_makeridges(qh, facet);
+          unsigned nridges = qh_setsize(qh, facet->ridges);
+          RidgeT* ridges = malloc(nridges * sizeof(RidgeT));
+          ridgeT *ridge, **ridgep;
+          unsigned i_ridge = 0;
+          FOREACHridge_(facet->ridges){
+            ridges[i_ridge].nedges = 0;
+            unsigned ridgeSize = qh_setsize(qh, ridge->vertices); // dim-1
+//            printf("ridge size: %u\n", ridgeSize);
+            ridges[i_ridge].nvertices = ridgeSize;
+            unsigned ids[ridgeSize];
+            for(unsigned v=0; v < ridgeSize; v++){
+              ids[v] =
+                qh_pointid(qh, ((vertexT*)ridge->vertices->e[v].p)->point);
+            }
+            qsortu(ids, ridgeSize);
+            ridges[i_ridge].vertices = malloc(ridgeSize * sizeof(VertexT));
+            for(unsigned v=0; v < ridgeSize; v++){
+              ridges[i_ridge].vertices[v].id = ids[v];
+              ridges[i_ridge].vertices[v].point = getpoint(points, dim, ids[v]);
+            }
+            unsigned ridgeofs[2];
+            ridgeofs[0] = ridge->bottom->id;
+            ridgeofs[1] = ridge->top->id;
+            qsortu(ridgeofs, 2);
+            ridges[i_ridge].ridgeOf1 = ridgeofs[0];
+            ridges[i_ridge].ridgeOf2 = ridgeofs[1];
+            /**/
+            i_ridge++;
+          }
+          /* merge triangulated ridges */
+          if(dim > 3 && !triangulate){
+            unsigned l;
+            faces[i_facet].ridges  = mergeRidges(ridges, nridges, &l);
+            faces[i_facet].nridges = l;
+          }else{ /* dim 2 or 3, or triangulate option */
+            faces[i_facet].ridges  = ridges;
+            faces[i_facet].nridges = nridges;
+          }
+        }
+        /**/
+        i_facet++;
+      }
+    }
+
+    /* make unique ridges */
+    unsigned n_allridges;
+    RidgeT* allridges = allRidges(faces, nfaces, dim, &n_allridges);
+//    printf("nallridges: %u\n", n_allridges);
+
+    /* assign ridges ids to the ridges stored in the faces */
+    assignRidgesIds(&faces, nfaces, allridges, n_allridges);
+
+    /* all vertices */
+    unsigned nvertices = qh->num_vertices;
+    FullVertexT* vertices = malloc(nvertices * sizeof(FullVertexT));
+    {
+      qh_vertexneighbors(qh); /* make the neighbor facets of the vertices */
+      vertexT *vertex;
+      unsigned i_vertex=0;
+      FORALLvertices{
+        /* vertex id and coordinates */
+        vertices[i_vertex].id    = (unsigned) qh_pointid(qh, vertex->point);
+        vertices[i_vertex].point = getpoint(points, dim, vertices[i_vertex].id);
+
+        /* neighbor facets of the vertex */
+        vertices[i_vertex].nneighfacets = qh_setsize(qh, vertex->neighbors);
+        vertices[i_vertex].neighfacets =
+          malloc(vertices[i_vertex].nneighfacets * sizeof(unsigned));
+        facetT *neighbor, **neighborp;
+        unsigned i_neighbor = 0;
+        FOREACHneighbor_(vertex){
+          vertices[i_vertex].neighfacets[i_neighbor] = neighbor->id;
+          i_neighbor++;
+        }
+        qsortu(vertices[i_vertex].neighfacets, vertices[i_vertex].nneighfacets);
+
+        /* neighbor vertices of the vertex */
+        if(dim > 2){
+          unsigned nneighsvertices;
+          vertices[i_vertex].neighvertices =
+            neighVertices(vertices[i_vertex].id, allridges, n_allridges,
+                          dim, triangulate, &nneighsvertices);
+          vertices[i_vertex].nneighsvertices = nneighsvertices;
+        }else{ /* dim=2 */
+          vertices[i_vertex].nneighsvertices = 2;
+          vertices[i_vertex].neighvertices   = malloc(2 * sizeof(unsigned));
+          unsigned count = 0;
+          for(unsigned f=0; f < nfaces; f++){
+            for(unsigned i=0; i < 2; i++){
+              if(faces[f].vertices[i].id == vertices[i_vertex].id){
+                vertices[i_vertex].neighvertices[count] =
+                  faces[f].vertices[1-i].id;
+                count++;
+                break;
+              }
+            }
+            if(count == 2){
+              break;
+            }
+          }
+        }
+        qsortu(vertices[i_vertex].neighvertices,
+               vertices[i_vertex].nneighsvertices);
+
+        /* neighbor ridges of the vertex */
+        if(dim > 2){
+          unsigned nneighridges;
+          vertices[i_vertex].neighridges =
+            neighRidges(vertices[i_vertex].id, allridges, n_allridges,
+                       &nneighridges);
+          qsortu(vertices[i_vertex].neighridges, nneighridges);
+          vertices[i_vertex].nneighridges = nneighridges;
+        }else{ /* dim=2 */
+          vertices[i_vertex].nneighridges = 0; /* ridge = vertex singleton */
+        }
+        /**/
+        i_vertex++;
+      }
+      /* sort vertices according to their ids */
+      qsort(vertices, nvertices, sizeof(FullVertexT), cmpfullvertices);
+    }
+
+    /* all edges */
+    unsigned nalledges = 0;
+    for(unsigned v=0; v < nvertices; v++){
+      nalledges += vertices[v].nneighsvertices;
+    }
+    nalledges /= 2;
+    unsigned** alledges = allEdges(vertices, nvertices, nalledges);
+    qsort(alledges, nalledges, sizeof(unsigned*), cmpedges);
+
+    { /* faces edges and ridges ids */
+      facetT *facet; unsigned i_facet=0;
+      FORALLfacets{
+        /* facet ridges ids */
+        faces[i_facet].ridgesids =
+          map_ridgeid(faces[i_facet].ridges, faces[i_facet].nridges);
+        qsortu(faces[i_facet].ridgesids, faces[i_facet].nridges);
+        /* facet edges */
+        SetOfVerticesT facet_vset = {.vertices = faces[i_facet].vertices,
+                                     .nvertices = faces[i_facet].nvertices};
+        unsigned nfaceedges;
+        faces[i_facet].edges =
+          makeEdges(facet_vset, alledges, nalledges, &nfaceedges);
+        //qsort(faces[i_facet].edges, nfaceedges, sizeof(unsigned*), cmpedges); useless, I think
+        faces[i_facet].nedges = nfaceedges;
+        /**/
+        i_facet++;
+      }
+    }
+
+    /* ridges edges */
+    if(dim > 3){
+      for(unsigned r=0; r < n_allridges; r++){
+        unsigned facetid = allridges[r].ridgeOf1;
+        SetOfVerticesT vset = {.vertices = allridges[r].vertices,
+                               .nvertices = allridges[r].nvertices};
+        unsigned nedges;
+        allridges[r].edges =
+          makeEdges(vset, faces[facetid].edges, faces[facetid].nedges, &nedges);
+        allridges[r].nedges = nedges;
+      }
+    }
+
+    /* output */
+    out->dim       = dim;
+    out->vertices  = vertices;
+    out->nvertices = nvertices;
+    out->faces     = faces;
+    out->nfaces    = nfaces;
+    out->ridges    = allridges;
+    out->nridges   = n_allridges;
+    out->edges     = alledges;
+    out->nedges    = nalledges;
+
+  } // end if exitcode
+
+  /* Do cleanup regardless of whether there is an error */
+  int curlong, totlong;
+	qh_freeqhull(qh, !qh_ALL);               /* free long memory */
+	qh_memfreeshort(qh, &curlong, &totlong); /* free short memory and memory allocator */
+
+  printf("RETURN\n");
+  if(*exitcode){
+    free(out);
+    return 0;
+  }else{
+    return out;
+  }
+
+}
diff --git a/C/delaunay.c b/C/delaunay.c
new file mode 100644
--- /dev/null
+++ b/C/delaunay.c
@@ -0,0 +1,577 @@
+#define qh_QHimport
+#include "qhull_ra.h"
+#include "delaunay.h"
+#include "utils.h"
+#include <math.h> /* to use NAN */
+
+// void printfacet(qhT* qh, facetT* facet){
+//   vertexT *vertex, **vertexp;
+//   FOREACHvertex_(facet->vertices){
+//     printf("facetid: %d, pointid: %d ", facet->id, qh_pointid(qh, vertex->point));
+//   }
+// }
+
+unsigned facetOK_(facetT* facet, unsigned degenerate){
+  return !facet->upperdelaunay && (degenerate || !facet->degenerate);
+} // && simplicial, && !facet->redundant - pas de simplicial avec Qt
+
+
+
+TesselationT* tesselation(
+	double*   sites,
+	unsigned  dim,
+	unsigned  n,
+  unsigned  atinfinity,
+  unsigned  degenerate,
+  double    vthreshold,
+	unsigned* exitcode
+)
+{
+	char opts[50]; /* option flags for qhull, see qh_opt.htm */
+  sprintf(opts, "qhull d Qt Qbb%s%s",
+          atinfinity ? " Qz" : "", dim>3 ? " Qx" : "");
+	qhT qh_qh; /* Qhull's data structure */
+  qhT *qh= &qh_qh;
+  QHULL_LIB_CHECK
+  qh_meminit(qh, stderr);
+	boolT ismalloc  = False; /* True if qhull should free points in qh_freeqhull() or reallocation */
+	FILE *errfile   = NULL;
+  FILE* outfile   = NULL;
+  qh_zero(qh, errfile);
+	*exitcode = qh_new_qhull(qh, dim, n, sites, ismalloc, opts, outfile, errfile);
+  //fclose(tmpstdout);
+  printf("exitcode: %u\n", *exitcode);
+
+  TesselationT* out = malloc(sizeof(TesselationT)); /* output */
+
+	if (!(*exitcode)) { /* 0 if no error from qhull */
+
+    /* Count the number of facets we keep */
+		unsigned nfacets = 0; /* to store the number of facets */
+    {
+      facetT *facet;  /* set by FORALLfacets */
+  		FORALLfacets {
+  			if(facetOK_(facet, degenerate)){
+          facet->id = nfacets;
+  	      nfacets++;
+        }else{
+  				qh_removefacet(qh, facet);
+  			}
+  		}
+    }
+
+    /* Initialize the tiles */
+    TileT* allfacets = malloc(nfacets * sizeof(TileT));
+
+    { /* tiles families and volumes, and centers of tiles with >0 volume */
+      facetT* facet;
+      unsigned i_facet = 0;
+      FORALLfacets{
+        if(facet->tricoplanar){
+          allfacets[i_facet].family = facet->f.triowner->id;
+          // if(!facet->degenerate){
+          //   if(i_facet == 392){
+          //     printf("area: %f", qh_facetarea(qh, facet));
+          //   }
+          //   allfacets[i_facet].simplex.center =
+          //     qh_facetcenter(qh, facet->vertices);
+          // }else{
+          //   facetT *neighbor, **neighborp;
+          //   FOREACHneighbor_(facet){
+          //     if(facetOK_(neighbor,0) && neighbor->f.triowner->id == facet->f.triowner->id){
+          //       allfacets[i_facet].simplex.center =
+          //         qh_facetcenter(qh, neighbor->vertices);
+          //       break;
+          //     }
+          //   }
+          // }
+        }else{
+          allfacets[i_facet].family = -1;
+        }
+        if(facet->degenerate){ // ?
+          allfacets[i_facet].simplex.volume = 0;
+        }else{
+          allfacets[i_facet].simplex.volume = fmax(0, qh_facetarea(qh, facet));
+        }
+        if(allfacets[i_facet].simplex.volume > vthreshold){
+          allfacets[i_facet].simplex.center = malloc(dim * sizeof(double));
+          double* center = qh_facetcenter(qh, facet->vertices);
+          for(unsigned i=0; i < dim; i++){
+            allfacets[i_facet].simplex.center[i] = center[i];
+          }
+        }
+        i_facet++;
+      }
+    }
+
+  	{ /* facets ids, orientations, centers, sites ids, neighbors */
+      facetT* facet;
+      unsigned i_facet = 0; /* facet counter */
+      FORALLfacets {
+        allfacets[i_facet].id             = facet->id;
+        allfacets[i_facet].orientation    = facet->toporient ? 1 : -1;
+        /* center and circumradius */
+        if(allfacets[i_facet].simplex.volume <= vthreshold){
+          if(facet->tricoplanar){
+            unsigned ok = 0;
+            vertexT* apex = (vertexT*)facet->vertices->e[0].p;
+            facetT *neighbor, **neighborp;
+            FOREACHneighbor_(apex){
+              if(facetOK_(neighbor,degenerate) &&
+                 allfacets[neighbor->id].family == allfacets[i_facet].family &&
+                 allfacets[neighbor->id].simplex.volume > vthreshold)
+              {
+                allfacets[i_facet].simplex.center =
+                  allfacets[neighbor->id].simplex.center;
+                ok = 1;
+                break;
+              }
+            }
+            if(!ok){ /* should not happen */
+              allfacets[i_facet].simplex.center = nanvector(dim);
+            }
+          }else{ /* should not happen */
+            allfacets[i_facet].simplex.center = nanvector(dim);
+          }
+        }
+//        printf("center facet %u: %f %f %f\n", i_facet, allfacets[i_facet].simplex.center[0], allfacets[i_facet].simplex.center[1], allfacets[i_facet].simplex.center[2]);
+        allfacets[i_facet].simplex.radius =
+          sqrt(squaredDistance(((vertexT*)facet->vertices->e[0].p)->point,
+                                allfacets[i_facet].simplex.center, dim));
+        // allfacets[i_facet].simplex.center =
+        //   facet->degenerate ? nanvector(dim)
+        //                       : qh_facetcenter(qh, facet->vertices);
+        // if(!facet->degenerate){
+        //   allfacets[i_facet].simplex.center = //facet->center;
+        //     qh_facetcenter(qh, facet->vertices);
+        //   // faire une première passe : calculer les centres des triowner
+        //   // pour ne pas les calculer pour les facets de la même famille
+        //   printf("center1: %f %f %f\n", facet->center[0], facet->center[1], facet->center[2]);
+        //   printf("center2: %f %f %f\n", allfacets[i_facet].simplex.center[0], allfacets[i_facet].simplex.center[1], allfacets[i_facet].simplex.center[2]);
+        //   pointT* point = ((vertexT*)facet->vertices->e[0].p)->point;
+        //   allfacets[i_facet].simplex.radius =
+        //     sqrt(squaredDistance(point, allfacets[i_facet].simplex.center,
+        //                          dim));
+        // }// }else{
+        // //   allfacets[i_facet].simplex.radius = NAN;
+        // // }
+
+        { /* vertices ids of the facet */
+          allfacets[i_facet].simplex.sitesids =
+            malloc((dim+1) * sizeof(unsigned));
+          vertexT *vertex, **vertexp;
+          unsigned i_vertex = 0;
+          FOREACHvertex_(facet->vertices) {
+            allfacets[i_facet].simplex.sitesids[i_vertex] =
+              qh_pointid(qh, vertex->point);
+            i_vertex++;
+    			}
+          qsortu(allfacets[i_facet].simplex.sitesids, dim+1);
+        }
+
+        { /* neighbors facets of the facet */
+          facetT *neighbor, **neighborp;
+    			unsigned flag[dim+1];
+          allfacets[i_facet].nneighbors = 0;
+          unsigned i_neighbor = 0;
+    			FOREACHneighbor_(facet) {
+            if(flag[i_neighbor] = facetOK_(neighbor, degenerate)){
+              allfacets[i_facet].nneighbors++;
+            }
+            i_neighbor++;
+          }
+          allfacets[i_facet].neighbors =
+            malloc(allfacets[i_facet].nneighbors * sizeof(unsigned));
+          unsigned countok = 0;
+          i_neighbor = 0;
+          FOREACHneighbor_(facet) {
+            if(flag[i_neighbor]){
+              allfacets[i_facet].neighbors[countok] = neighbor->id;
+              countok++;
+            }
+            i_neighbor++;
+          }
+        }
+
+        // /* facet family */
+        // if(facet->tricoplanar){
+        //   allfacets[i_facet].family = facet->f.triowner->id;
+        // }else{
+        //   allfacets[i_facet].family = -1;
+        // }
+
+        /**/
+  			i_facet++;
+  		}
+    }
+
+    //  /* for degenerate facets, take the center of the owner */
+    // if(degenerate){
+    //   facetT *facet;
+    //   unsigned i_facet = 0;
+    //   FORALLfacets{
+    //     if(facet->degenerate){
+    //       allfacets[i_facet].simplex.center =
+    //         allfacets[allfacets[i_facet].family].simplex.center;
+    //       pointT* point = ((vertexT*)facet->vertices->e[0].p)->point;
+    //       allfacets[i_facet].simplex.radius =
+    //         sqrt(squaredDistance(point, allfacets[i_facet].simplex.center,
+    //                              dim));
+    //     }
+    //     i_facet++;
+    //   }
+    // }
+
+		/* neighbor facets and neighbor vertices per vertex */
+    /* --- we will use the following combinations, also used later */
+    /* --- combinations[m] contains all k between 0 and dim but m  */
+    unsigned combinations[dim+1][dim];
+    for(unsigned m=0; m < dim+1; m++){
+      unsigned kk=0;
+      for(unsigned k=0; k < dim+1; k++){
+        if(k != m){
+          combinations[m][kk] = k;
+          kk++;
+        }
+      }
+    }
+    /* --- initialize the sites */
+    SiteT* allsites = malloc(n * sizeof(SiteT));
+    /* --- array to flag neighbors - 0/1 if not neighbour/neighbour */
+    unsigned** verticesFacetsNeighbours = malloc(n * sizeof(unsigned*));
+		  /* unsigned verticesFacetsNeighbours[n][nfacets] => stackoverflow */
+    for(unsigned v=0; v < n; v++){
+      allsites[v].id           = v;
+      allsites[v].nneighsites  = 0;
+      allsites[v].neighsites   = malloc(0); /* will be filled by appending */
+      allsites[v].nneighridges = 0;
+      allsites[v].nneightiles  = 0;
+      verticesFacetsNeighbours[v] = uzeros(nfacets);
+    }
+    /* --- fill verticesFacetsNeighbours, derive number of neighbor facets */
+    /* --- and derive neighbor sites */
+    for(unsigned i_facet=0; i_facet < nfacets; i_facet++){
+      for(unsigned j=0; j < dim+1; j++){
+        unsigned vertexid = allfacets[i_facet].simplex.sitesids[j];
+        if(verticesFacetsNeighbours[vertexid][i_facet] == 0){
+          verticesFacetsNeighbours[vertexid][i_facet] = 1;
+          allsites[vertexid].nneightiles++;
+        }
+        for(unsigned k=0; k < dim; k++){
+          unsigned vertexid2 =
+            allfacets[i_facet].simplex.sitesids[combinations[j][k]];
+          unsigned pushed;
+          appendu(vertexid2, &allsites[vertexid].neighsites,
+                  allsites[vertexid].nneighsites, &pushed);
+          if(pushed){
+            allsites[vertexid].nneighsites++;
+          }
+        }
+      }
+    }
+
+    /************************************************************/
+    /* second pass on facets: ridges and facet volumes          */
+    unsigned n_ridges_dup = nfacets * (dim+1); /* number of ridges with duplicates */
+    SubTileT* allridges_dup = malloc(n_ridges_dup * sizeof(SubTileT));
+    for(unsigned r=0; r < n_ridges_dup; r++){
+      allridges_dup[r].simplex.sitesids = malloc(dim * sizeof(unsigned));
+      allridges_dup[r].flag = 0;
+    }
+//    qh_getarea(qh, qh->facet_list); /* make facets volumes, available in facet->f.area */
+    unsigned n_ridges = 0; /* count distinct ridges */
+
+    { /* loop on facets */
+      facetT *facet;
+      unsigned i_ridge_dup = 0; /* ridge counter */
+      unsigned i_facet = 0; /* facet counter */
+      FORALLfacets {
+
+//        allfacets[i_facet].simplex.volume = facet->f.area;
+        allfacets[i_facet].nridges   = dim+1;
+        allfacets[i_facet].ridgesids = malloc((dim+1) * sizeof(unsigned));
+
+        /* loop on the combinations - it increments i_ridge_dup */
+        for(unsigned m=0; m < dim+1; m++){
+          allridges_dup[i_ridge_dup].ridgeOf1 = facet->id;
+          allridges_dup[i_ridge_dup].ridgeOf2 = -1; /* this means "nothing" */
+          unsigned ids[dim];
+          for(unsigned i=0; i < dim; i++){
+            ids[i] = allfacets[i_facet].simplex.sitesids[combinations[m][i]];
+          }
+          unsigned done = 0; /* flag ridge is already done */
+          for(unsigned r=0; r < i_ridge_dup; r++){
+            if(allridges_dup[r].ridgeOf2 == (int) facet->id &&
+               allridges_dup[r].flag==1)
+            {
+              unsigned ids2[dim];
+              unsigned i;
+              for(i=0; i < dim; i++){
+                ids2[i] = allridges_dup[r].simplex.sitesids[i];
+                if(ids2[i] != ids[i]){
+                  break;
+                }
+              }
+              if(i == dim){
+                allfacets[i_facet].ridgesids[m] = allridges_dup[r].id;
+                done = 1;
+                break;
+              }
+            }
+          }
+          if(done == 0){ /* => then do the ridge */
+            allridges_dup[i_ridge_dup].flag = 1;
+            allridges_dup[i_ridge_dup].id   = n_ridges;
+            allfacets[i_facet].ridgesids[m] = n_ridges;
+            n_ridges++;
+            for(unsigned i=0; i < dim; i++){
+              allridges_dup[i_ridge_dup].simplex.sitesids[i] = ids[i];
+              allsites[ids[i]].nneighridges++;
+            }
+
+            { /* loop on facet neighbors to find ridgeOf2 */
+              facetT *neighbor, **neighborp;
+              FOREACHneighbor_(facet){
+                if(facetOK_(neighbor, degenerate)){
+                  unsigned fnid = neighbor->id;
+                  unsigned ok;
+                  for(unsigned mm=0; mm < dim+1; mm++){
+                    ok = 0;
+                    for(unsigned i=0; i < dim; i++){
+                      if(allfacets[fnid].simplex.sitesids[combinations[mm][i]]
+                          != ids[i])
+                      {
+                        break;
+                      }else{
+                        ok++;
+                      }
+                    }
+                    if(ok==dim){
+                      break;
+                    }
+                  }
+                  if(ok==dim){
+                    allridges_dup[i_ridge_dup].ridgeOf2 = (int) fnid;
+                    break;
+                  }
+                }
+              } /* end FOREACHneighbor_(facet) */
+            }
+
+            pointT* points[dim]; /* the points corresponding to the combination */
+            for(unsigned i=0; i < dim; i++){
+              points[i] = getpoint(sites, dim, ids[i]);
+            }
+            double normal[dim]; /* to store the ridge normal */
+            if(dim == 2){
+              double u1 = points[1][0] - points[0][0];
+              double v1 = points[1][1] - points[0][1];
+              allridges_dup[i_ridge_dup].simplex.volume =
+                sqrt(square(u1)+square(v1));
+              allridges_dup[i_ridge_dup].simplex.center =
+                middle(points[0], points[1], dim);
+              allridges_dup[i_ridge_dup].simplex.radius =
+                sqrt(squaredDistance(allridges_dup[i_ridge_dup].simplex.center,
+                                     points[0], dim));
+              normal[0] = v1; normal[1] = -u1;
+            }else{
+              int parity=1;
+              double squaredNorm = 0;
+              for(unsigned i=0; i < dim; i++){
+                double** rows = malloc((dim-1) * sizeof(double*));
+                for(unsigned j=0; j < dim-1; j++){
+                  rows[j] = (double*) malloc((dim-1) * sizeof(double));
+                  for(unsigned k=0; k < dim-1; k++){
+                    unsigned kk = k<i ? k : k+1;
+                    rows[j][k] = points[j+1][kk] - points[0][kk];
+                  }
+                }
+                boolT nearzero;
+                normal[i] = parity * qh_determinant(qh, rows, dim-1, &nearzero);
+                squaredNorm += square(normal[i]);
+                for(unsigned j=0; j < dim-1; j++){
+                  free(rows[j]);
+                }
+                free(rows);
+                parity = -parity;
+              }
+              double surface = sqrt(squaredNorm);
+              for(unsigned k=2; k < dim-1; k++){
+                surface /= k;
+              }
+              allridges_dup[i_ridge_dup].simplex.volume = surface;
+            }
+            qh_normalize2(qh, normal, dim, 1, NULL, NULL);
+            allridges_dup[i_ridge_dup].normal =
+              malloc(dim * sizeof(double));
+            for(unsigned i=0; i < dim; i++){
+              allridges_dup[i_ridge_dup].normal[i] = normal[i];
+            }
+            allridges_dup[i_ridge_dup].offset =
+              - dotproduct(points[0], normal, dim);
+            if(dim > 2){ /* ridge center is already done if dim 2 */
+              // if(facet->degenerate){
+              //   allridges_dup[i_ridge_dup].simplex.center = nanvector(dim);
+              //   allridges_dup[i_ridge_dup].simplex.radius = NAN;
+              // }else{
+              allridges_dup[i_ridge_dup].simplex.center =
+                malloc(dim * sizeof(double));
+              double scal = 0;
+              for(unsigned i=0; i < dim; i++){
+                scal += (points[0][i]-allfacets[i_facet].simplex.center[i]) *
+                          normal[i];
+              }
+              for(unsigned i=0; i < dim; i++){
+                allridges_dup[i_ridge_dup].simplex.center[i] =
+                  allfacets[i_facet].simplex.center[i] + scal*normal[i];
+              }
+              allridges_dup[i_ridge_dup].simplex.radius =
+                sqrt(squaredDistance(
+                      allridges_dup[i_ridge_dup].simplex.center,
+                      points[0], dim));
+//              }
+            }
+            /* orient the normal (used for plotting unbounded Voronoi cells) */
+            if(allridges_dup[i_ridge_dup].ridgeOf2 == -1)
+               //&& (!facet->degenerate || dim==2))
+            {
+              pointT* otherpoint = /* the remaining vertex of the facet (the one not in the ridge) */
+                qh->interior_point; // getpoint(sites, dim, allfacets[facet->id].simplex.sitesids[m]);
+              double thepoint[dim]; /* the point center+normal */
+              for(unsigned i=0; i < dim; i++){
+                thepoint[i] = allridges_dup[i_ridge_dup].simplex.center[i] +
+                              allridges_dup[i_ridge_dup].normal[i];
+              }
+              /* we check that these two points are on the same side of the ridge */
+              double h1 = dotproduct(otherpoint,
+                                     allridges_dup[i_ridge_dup].normal, dim) +
+                          allridges_dup[i_ridge_dup].offset;
+              double h2 = dotproduct(thepoint,
+                                     allridges_dup[i_ridge_dup].normal, dim) +
+                          allridges_dup[i_ridge_dup].offset;
+              // printf("deg: %u, h1: %f, h2: %f\n", facet->degenerate, h1, h2);
+              // printf("offset: %f\n", allridges_dup[i_ridge_dup].offset);
+              // printf("normal: %f %f %f\n", allridges_dup[i_ridge_dup].normal[0], allridges_dup[i_ridge_dup].normal[1], allridges_dup[i_ridge_dup].normal[2]);
+              if(h1*h2 >= 0){
+                for(unsigned i=0; i < dim; i++){
+                  allridges_dup[i_ridge_dup].normal[i] *= -1;
+                }
+              }
+            }
+            for(unsigned i=0; i < dim; i++){
+              free(points[i]);
+            }
+          }
+          i_ridge_dup++;
+        } // end loop combinations (m)
+        qsortu(allfacets[i_facet].ridgesids, dim+1);
+        /**/
+        i_facet++;
+      } // end FORALLfacets
+    }
+
+    /* extract unique ridges */
+    SubTileT* allridges = malloc(n_ridges * sizeof(SubTileT));
+    unsigned inc_ridge = 0;
+		for(unsigned l=0; l < n_ridges_dup; l++){
+      if(allridges_dup[l].flag){
+        allridges[inc_ridge] = allridges_dup[l];
+        inc_ridge++;
+      }
+		}
+
+    /* make neighbor ridges per vertex */
+    unsigned* i_ridges_per_vertex = uzeros(n);
+		for(unsigned v=0; v < n; v++){
+      allsites[v].neighridgesids =
+        malloc(allsites[v].nneighridges * sizeof(unsigned));
+    }
+    for(unsigned l=0; l < n_ridges_dup; l++){
+      if(allridges_dup[l].flag){
+        for(unsigned i=0; i < dim; i++){
+          unsigned v = allridges_dup[l].simplex.sitesids[i];
+          allsites[v].neighridgesids[i_ridges_per_vertex[v]] =
+            allridges_dup[l].id;
+          i_ridges_per_vertex[v]++;
+        }
+      }
+    }
+
+    /* order vertices neighbor sites and make neighbor tiles per vertex */
+		for(unsigned v=0; v < n; v++){
+      qsortu(allsites[v].neighsites, allsites[v].nneighsites);
+      allsites[v].neightiles =
+        malloc(allsites[v].nneightiles * sizeof(unsigned));
+			unsigned inc_facet = 0; unsigned inc_vfn = 0;
+			while(inc_vfn < allsites[v].nneightiles){
+				if(verticesFacetsNeighbours[v][inc_facet] == 1){
+          allsites[v].neightiles[inc_vfn] = inc_facet;
+					inc_vfn++;
+				}
+				inc_facet++;
+			}
+		}
+
+    /* make the output */
+	  out->sites      = allsites;
+    out->tiles      = allfacets;
+	  out->ntiles     = nfacets;
+	  out->subtiles   = allridges;
+    out->nsubtiles  = n_ridges;
+
+    free(allridges_dup);
+    free(i_ridges_per_vertex);
+    free(verticesFacetsNeighbours);
+
+	}
+
+	/* Do cleanup regardless of whether there is an error */
+  int curlong, totlong;
+	qh_freeqhull(qh, !qh_ALL);                /* free long memory */
+	qh_memfreeshort(qh, &curlong, &totlong);  /* free short memory and memory allocator */
+
+  printf("RETURN\n");
+  if(*exitcode){
+    free(out);
+    return 0;
+  }else{
+    return out;
+  }
+
+}
+
+
+void testdel2(){
+  double sites[27] = {0,0,0, 0,0,1, 0,1,0, 0,1,1, 1,0,0, 1,0,1, 1,1,0, 1,1,1, 0.5,0.5,0.5};
+  unsigned exitcode;
+  unsigned dim = 3;
+  TesselationT* x = tesselation(sites, dim, 9, 0, 0, 0, &exitcode);
+  printf("TESTDEL2 - nfacets:%u\n", x->ntiles);
+  for(unsigned f=0; f < x->ntiles; f++){
+    printf("facet %u - sites:\n", f);
+    for(unsigned i=0; i < dim+1; i++){
+      printf("%u - ", x->tiles[f].simplex.sitesids[i]);
+    }
+    printf("\n");
+    printf("facet %u - ridges:\n", f);
+    for(unsigned i=0; i < dim+1; i++){
+      printf("%u - ", x->tiles[f].ridgesids[i]);
+    }
+    printf("\n");
+    printf("facet %u - neighbors:\n", f);
+    for(unsigned i=0; i < x->tiles[f].nneighbors; i++){
+      printf("%u - ", x->tiles[f].neighbors[i]);
+    }
+    printf("\n");
+  }
+  printf("nallridges:%u\n", x->nsubtiles);
+  for(unsigned r=0; r < x->nsubtiles; r++){
+    printf("ridge %u - id %u:\n", r, x->subtiles[r].id);
+    for(unsigned i=0; i < dim; i++){
+      printf("%u - ", x->subtiles[r].simplex.sitesids[i]);
+    }
+    printf("ridgeOf: %u %d", x->subtiles[r].ridgeOf1, x->subtiles[r].ridgeOf2);
+    printf("\n");
+  }
+  free(x);
+}
diff --git a/C/geom2_r.c b/C/geom2_r.c
new file mode 100644
--- /dev/null
+++ b/C/geom2_r.c
@@ -0,0 +1,2096 @@
+/*<html><pre>  -<a                             href="qh-geom_r.htm"
+  >-------------------------------</a><a name="TOP">-</a>
+
+
+   geom2_r.c
+   infrequently used geometric routines of qhull
+
+   see qh-geom_r.htm and geom_r.h
+
+   Copyright (c) 1993-2015 The Geometry Center.
+   $Id: //main/2015/qhull/src/libqhull_r/geom2_r.c#6 $$Change: 2065 $
+   $DateTime: 2016/01/18 13:51:04 $$Author: bbarber $
+
+   frequently used code goes into geom_r.c
+*/
+
+#include "qhull_ra.h"
+
+/*================== functions in alphabetic order ============*/
+
+/*-<a                             href="qh-geom_r.htm#TOC"
+  >-------------------------------</a><a name="copypoints">-</a>
+
+  qh_copypoints(qh, points, numpoints, dimension)
+    return qh_malloc'd copy of points
+  
+  notes:
+    qh_free the returned points to avoid a memory leak
+*/
+coordT *qh_copypoints(qhT *qh, coordT *points, int numpoints, int dimension) {
+  int size;
+  coordT *newpoints;
+
+  size= numpoints * dimension * (int)sizeof(coordT);
+  if (!(newpoints= (coordT*)qh_malloc((size_t)size))) {
+    qh_fprintf(qh, qh->ferr, 6004, "qhull error: insufficient memory to copy %d points\n",
+        numpoints);
+    qh_errexit(qh, qh_ERRmem, NULL, NULL);
+  }
+  memcpy((char *)newpoints, (char *)points, (size_t)size); /* newpoints!=0 by QH6004 */
+  return newpoints;
+} /* copypoints */
+
+/*-<a                             href="qh-geom_r.htm#TOC"
+  >-------------------------------</a><a name="crossproduct">-</a>
+
+  qh_crossproduct( dim, vecA, vecB, vecC )
+    crossproduct of 2 dim vectors
+    C= A x B
+
+  notes:
+    from Glasner, Graphics Gems I, p. 639
+    only defined for dim==3
+*/
+void qh_crossproduct(int dim, realT vecA[3], realT vecB[3], realT vecC[3]){
+
+  if (dim == 3) {
+    vecC[0]=   det2_(vecA[1], vecA[2],
+                     vecB[1], vecB[2]);
+    vecC[1]= - det2_(vecA[0], vecA[2],
+                     vecB[0], vecB[2]);
+    vecC[2]=   det2_(vecA[0], vecA[1],
+                     vecB[0], vecB[1]);
+  }
+} /* vcross */
+
+/*-<a                             href="qh-geom_r.htm#TOC"
+  >-------------------------------</a><a name="determinant">-</a>
+
+  qh_determinant(qh, rows, dim, nearzero )
+    compute signed determinant of a square matrix
+    uses qh.NEARzero to test for degenerate matrices
+
+  returns:
+    determinant
+    overwrites rows and the matrix
+    if dim == 2 or 3
+      nearzero iff determinant < qh->NEARzero[dim-1]
+      (!quite correct, not critical)
+    if dim >= 4
+      nearzero iff diagonal[k] < qh->NEARzero[k]
+*/
+realT qh_determinant(qhT *qh, realT **rows, int dim, boolT *nearzero) {
+  realT det=0;
+  int i;
+  boolT sign= False;
+
+  *nearzero= False;
+  if (dim < 2) {
+    qh_fprintf(qh, qh->ferr, 6005, "qhull internal error (qh_determinate): only implemented for dimension >= 2\n");
+    qh_errexit(qh, qh_ERRqhull, NULL, NULL);
+  }else if (dim == 2) {
+    det= det2_(rows[0][0], rows[0][1],
+                 rows[1][0], rows[1][1]);
+    if (fabs_(det) < 10*qh->NEARzero[1])  /* not really correct, what should this be? */
+      *nearzero= True;
+  }else if (dim == 3) {
+    det= det3_(rows[0][0], rows[0][1], rows[0][2],
+                 rows[1][0], rows[1][1], rows[1][2],
+                 rows[2][0], rows[2][1], rows[2][2]);
+    if (fabs_(det) < 10*qh->NEARzero[2])  /* what should this be?  det 5.5e-12 was flat for qh_maxsimplex of qdelaunay 0,0 27,27 -36,36 -9,63 */
+      *nearzero= True;
+  }else {
+    qh_gausselim(qh, rows, dim, dim, &sign, nearzero);  /* if nearzero, diagonal still ok*/
+    det= 1.0;
+    for (i=dim; i--; )
+      det *= (rows[i])[i];
+    if (sign)
+      det= -det;
+  }
+  return det;
+} /* determinant */
+
+/*-<a                             href="qh-geom_r.htm#TOC"
+  >-------------------------------</a><a name="detjoggle">-</a>
+
+  qh_detjoggle(qh, points, numpoints, dimension )
+    determine default max joggle for point array
+      as qh_distround * qh_JOGGLEdefault
+
+  returns:
+    initial value for JOGGLEmax from points and REALepsilon
+
+  notes:
+    computes DISTround since qh_maxmin not called yet
+    if qh->SCALElast, last dimension will be scaled later to MAXwidth
+
+    loop duplicated from qh_maxmin
+*/
+realT qh_detjoggle(qhT *qh, pointT *points, int numpoints, int dimension) {
+  realT abscoord, distround, joggle, maxcoord, mincoord;
+  pointT *point, *pointtemp;
+  realT maxabs= -REALmax;
+  realT sumabs= 0;
+  realT maxwidth= 0;
+  int k;
+
+  for (k=0; k < dimension; k++) {
+    if (qh->SCALElast && k == dimension-1)
+      abscoord= maxwidth;
+    else if (qh->DELAUNAY && k == dimension-1) /* will qh_setdelaunay() */
+      abscoord= 2 * maxabs * maxabs;  /* may be low by qh->hull_dim/2 */
+    else {
+      maxcoord= -REALmax;
+      mincoord= REALmax;
+      FORALLpoint_(qh, points, numpoints) {
+        maximize_(maxcoord, point[k]);
+        minimize_(mincoord, point[k]);
+      }
+      maximize_(maxwidth, maxcoord-mincoord);
+      abscoord= fmax_(maxcoord, -mincoord);
+    }
+    sumabs += abscoord;
+    maximize_(maxabs, abscoord);
+  } /* for k */
+  distround= qh_distround(qh, qh->hull_dim, maxabs, sumabs);
+  joggle= distround * qh_JOGGLEdefault;
+  maximize_(joggle, REALepsilon * qh_JOGGLEdefault);
+  trace2((qh, qh->ferr, 2001, "qh_detjoggle: joggle=%2.2g maxwidth=%2.2g\n", joggle, maxwidth));
+  return joggle;
+} /* detjoggle */
+
+/*-<a                             href="qh-geom_r.htm#TOC"
+  >-------------------------------</a><a name="detroundoff">-</a>
+
+  qh_detroundoff(qh)
+    determine maximum roundoff errors from
+      REALepsilon, REALmax, REALmin, qh.hull_dim, qh.MAXabs_coord,
+      qh.MAXsumcoord, qh.MAXwidth, qh.MINdenom_1
+
+    accounts for qh.SETroundoff, qh.RANDOMdist, qh->MERGEexact
+      qh.premerge_cos, qh.postmerge_cos, qh.premerge_centrum,
+      qh.postmerge_centrum, qh.MINoutside,
+      qh_RATIOnearinside, qh_COPLANARratio, qh_WIDEcoplanar
+
+  returns:
+    sets qh.DISTround, etc. (see below)
+    appends precision constants to qh.qhull_options
+
+  see:
+    qh_maxmin() for qh.NEARzero
+
+  design:
+    determine qh.DISTround for distance computations
+    determine minimum denominators for qh_divzero
+    determine qh.ANGLEround for angle computations
+    adjust qh.premerge_cos,... for roundoff error
+    determine qh.ONEmerge for maximum error due to a single merge
+    determine qh.NEARinside, qh.MAXcoplanar, qh.MINvisible,
+      qh.MINoutside, qh.WIDEfacet
+    initialize qh.max_vertex and qh.minvertex
+*/
+void qh_detroundoff(qhT *qh) {
+
+  qh_option(qh, "_max-width", NULL, &qh->MAXwidth);
+  if (!qh->SETroundoff) {
+    qh->DISTround= qh_distround(qh, qh->hull_dim, qh->MAXabs_coord, qh->MAXsumcoord);
+    if (qh->RANDOMdist)
+      qh->DISTround += qh->RANDOMfactor * qh->MAXabs_coord;
+    qh_option(qh, "Error-roundoff", NULL, &qh->DISTround);
+  }
+  qh->MINdenom= qh->MINdenom_1 * qh->MAXabs_coord;
+  qh->MINdenom_1_2= sqrt(qh->MINdenom_1 * qh->hull_dim) ;  /* if will be normalized */
+  qh->MINdenom_2= qh->MINdenom_1_2 * qh->MAXabs_coord;
+                                              /* for inner product */
+  qh->ANGLEround= 1.01 * qh->hull_dim * REALepsilon;
+  if (qh->RANDOMdist)
+    qh->ANGLEround += qh->RANDOMfactor;
+  if (qh->premerge_cos < REALmax/2) {
+    qh->premerge_cos -= qh->ANGLEround;
+    if (qh->RANDOMdist)
+      qh_option(qh, "Angle-premerge-with-random", NULL, &qh->premerge_cos);
+  }
+  if (qh->postmerge_cos < REALmax/2) {
+    qh->postmerge_cos -= qh->ANGLEround;
+    if (qh->RANDOMdist)
+      qh_option(qh, "Angle-postmerge-with-random", NULL, &qh->postmerge_cos);
+  }
+  qh->premerge_centrum += 2 * qh->DISTround;    /*2 for centrum and distplane()*/
+  qh->postmerge_centrum += 2 * qh->DISTround;
+  if (qh->RANDOMdist && (qh->MERGEexact || qh->PREmerge))
+    qh_option(qh, "Centrum-premerge-with-random", NULL, &qh->premerge_centrum);
+  if (qh->RANDOMdist && qh->POSTmerge)
+    qh_option(qh, "Centrum-postmerge-with-random", NULL, &qh->postmerge_centrum);
+  { /* compute ONEmerge, max vertex offset for merging simplicial facets */
+    realT maxangle= 1.0, maxrho;
+
+    minimize_(maxangle, qh->premerge_cos);
+    minimize_(maxangle, qh->postmerge_cos);
+    /* max diameter * sin theta + DISTround for vertex to its hyperplane */
+    qh->ONEmerge= sqrt((realT)qh->hull_dim) * qh->MAXwidth *
+      sqrt(1.0 - maxangle * maxangle) + qh->DISTround;
+    maxrho= qh->hull_dim * qh->premerge_centrum + qh->DISTround;
+    maximize_(qh->ONEmerge, maxrho);
+    maxrho= qh->hull_dim * qh->postmerge_centrum + qh->DISTround;
+    maximize_(qh->ONEmerge, maxrho);
+    if (qh->MERGING)
+      qh_option(qh, "_one-merge", NULL, &qh->ONEmerge);
+  }
+  qh->NEARinside= qh->ONEmerge * qh_RATIOnearinside; /* only used if qh->KEEPnearinside */
+  if (qh->JOGGLEmax < REALmax/2 && (qh->KEEPcoplanar || qh->KEEPinside)) {
+    realT maxdist;             /* adjust qh.NEARinside for joggle */
+    qh->KEEPnearinside= True;
+    maxdist= sqrt((realT)qh->hull_dim) * qh->JOGGLEmax + qh->DISTround;
+    maxdist= 2*maxdist;        /* vertex and coplanar point can joggle in opposite directions */
+    maximize_(qh->NEARinside, maxdist);  /* must agree with qh_nearcoplanar() */
+  }
+  if (qh->KEEPnearinside)
+    qh_option(qh, "_near-inside", NULL, &qh->NEARinside);
+  if (qh->JOGGLEmax < qh->DISTround) {
+    qh_fprintf(qh, qh->ferr, 6006, "qhull error: the joggle for 'QJn', %.2g, is below roundoff for distance computations, %.2g\n",
+         qh->JOGGLEmax, qh->DISTround);
+    qh_errexit(qh, qh_ERRinput, NULL, NULL);
+  }
+  if (qh->MINvisible > REALmax/2) {
+    if (!qh->MERGING)
+      qh->MINvisible= qh->DISTround;
+    else if (qh->hull_dim <= 3)
+      qh->MINvisible= qh->premerge_centrum;
+    else
+      qh->MINvisible= qh_COPLANARratio * qh->premerge_centrum;
+    if (qh->APPROXhull && qh->MINvisible > qh->MINoutside)
+      qh->MINvisible= qh->MINoutside;
+    qh_option(qh, "Visible-distance", NULL, &qh->MINvisible);
+  }
+  if (qh->MAXcoplanar > REALmax/2) {
+    qh->MAXcoplanar= qh->MINvisible;
+    qh_option(qh, "U-coplanar-distance", NULL, &qh->MAXcoplanar);
+  }
+  if (!qh->APPROXhull) {             /* user may specify qh->MINoutside */
+    qh->MINoutside= 2 * qh->MINvisible;
+    if (qh->premerge_cos < REALmax/2)
+      maximize_(qh->MINoutside, (1- qh->premerge_cos) * qh->MAXabs_coord);
+    qh_option(qh, "Width-outside", NULL, &qh->MINoutside);
+  }
+  qh->WIDEfacet= qh->MINoutside;
+  maximize_(qh->WIDEfacet, qh_WIDEcoplanar * qh->MAXcoplanar);
+  maximize_(qh->WIDEfacet, qh_WIDEcoplanar * qh->MINvisible);
+  qh_option(qh, "_wide-facet", NULL, &qh->WIDEfacet);
+  if (qh->MINvisible > qh->MINoutside + 3 * REALepsilon
+  && !qh->BESToutside && !qh->FORCEoutput)
+    qh_fprintf(qh, qh->ferr, 7001, "qhull input warning: minimum visibility V%.2g is greater than \nminimum outside W%.2g.  Flipped facets are likely.\n",
+             qh->MINvisible, qh->MINoutside);
+  qh->max_vertex= qh->DISTround;
+  qh->min_vertex= -qh->DISTround;
+  /* numeric constants reported in printsummary */
+} /* detroundoff */
+
+/*-<a                             href="qh-geom_r.htm#TOC"
+  >-------------------------------</a><a name="detsimplex">-</a>
+
+  qh_detsimplex(qh, apex, points, dim, nearzero )
+    compute determinant of a simplex with point apex and base points
+
+  returns:
+     signed determinant and nearzero from qh_determinant
+
+  notes:
+     uses qh.gm_matrix/qh.gm_row (assumes they're big enough)
+
+  design:
+    construct qm_matrix by subtracting apex from points
+    compute determinate
+*/
+realT qh_detsimplex(qhT *qh, pointT *apex, setT *points, int dim, boolT *nearzero) {
+  pointT *coorda, *coordp, *gmcoord, *point, **pointp;
+  coordT **rows;
+  int k,  i=0;
+  realT det;
+
+  zinc_(Zdetsimplex);
+  gmcoord= qh->gm_matrix;
+  rows= qh->gm_row;
+  FOREACHpoint_(points) {
+    if (i == dim)
+      break;
+    rows[i++]= gmcoord;
+    coordp= point;
+    coorda= apex;
+    for (k=dim; k--; )
+      *(gmcoord++)= *coordp++ - *coorda++;
+  }
+  if (i < dim) {
+    qh_fprintf(qh, qh->ferr, 6007, "qhull internal error (qh_detsimplex): #points %d < dimension %d\n",
+               i, dim);
+    qh_errexit(qh, qh_ERRqhull, NULL, NULL);
+  }
+  det= qh_determinant(qh, rows, dim, nearzero);
+  trace2((qh, qh->ferr, 2002, "qh_detsimplex: det=%2.2g for point p%d, dim %d, nearzero? %d\n",
+          det, qh_pointid(qh, apex), dim, *nearzero));
+  return det;
+} /* detsimplex */
+
+/*-<a                             href="qh-geom_r.htm#TOC"
+  >-------------------------------</a><a name="distnorm">-</a>
+
+  qh_distnorm( dim, point, normal, offset )
+    return distance from point to hyperplane at normal/offset
+
+  returns:
+    dist
+
+  notes:
+    dist > 0 if point is outside of hyperplane
+
+  see:
+    qh_distplane in geom_r.c
+*/
+realT qh_distnorm(int dim, pointT *point, pointT *normal, realT *offsetp) {
+  coordT *normalp= normal, *coordp= point;
+  realT dist;
+  int k;
+
+  dist= *offsetp;
+  for (k=dim; k--; )
+    dist += *(coordp++) * *(normalp++);
+  return dist;
+} /* distnorm */
+
+/*-<a                             href="qh-geom_r.htm#TOC"
+  >-------------------------------</a><a name="distround">-</a>
+
+  qh_distround(qh, dimension, maxabs, maxsumabs )
+    compute maximum round-off error for a distance computation
+      to a normalized hyperplane
+    maxabs is the maximum absolute value of a coordinate
+    maxsumabs is the maximum possible sum of absolute coordinate values
+
+  returns:
+    max dist round for REALepsilon
+
+  notes:
+    calculate roundoff error according to Golub & van Loan, 1983, Lemma 3.2-1, "Rounding Errors"
+    use sqrt(dim) since one vector is normalized
+      or use maxsumabs since one vector is < 1
+*/
+realT qh_distround(qhT *qh, int dimension, realT maxabs, realT maxsumabs) {
+  realT maxdistsum, maxround;
+
+  maxdistsum= sqrt((realT)dimension) * maxabs;
+  minimize_( maxdistsum, maxsumabs);
+  maxround= REALepsilon * (dimension * maxdistsum * 1.01 + maxabs);
+              /* adds maxabs for offset */
+  trace4((qh, qh->ferr, 4008, "qh_distround: %2.2g maxabs %2.2g maxsumabs %2.2g maxdistsum %2.2g\n",
+                 maxround, maxabs, maxsumabs, maxdistsum));
+  return maxround;
+} /* distround */
+
+/*-<a                             href="qh-geom_r.htm#TOC"
+  >-------------------------------</a><a name="divzero">-</a>
+
+  qh_divzero( numer, denom, mindenom1, zerodiv )
+    divide by a number that's nearly zero
+    mindenom1= minimum denominator for dividing into 1.0
+
+  returns:
+    quotient
+    sets zerodiv and returns 0.0 if it would overflow
+
+  design:
+    if numer is nearly zero and abs(numer) < abs(denom)
+      return numer/denom
+    else if numer is nearly zero
+      return 0 and zerodiv
+    else if denom/numer non-zero
+      return numer/denom
+    else
+      return 0 and zerodiv
+*/
+realT qh_divzero(realT numer, realT denom, realT mindenom1, boolT *zerodiv) {
+  realT temp, numerx, denomx;
+
+
+  if (numer < mindenom1 && numer > -mindenom1) {
+    numerx= fabs_(numer);
+    denomx= fabs_(denom);
+    if (numerx < denomx) {
+      *zerodiv= False;
+      return numer/denom;
+    }else {
+      *zerodiv= True;
+      return 0.0;
+    }
+  }
+  temp= denom/numer;
+  if (temp > mindenom1 || temp < -mindenom1) {
+    *zerodiv= False;
+    return numer/denom;
+  }else {
+    *zerodiv= True;
+    return 0.0;
+  }
+} /* divzero */
+
+
+/*-<a                             href="qh-geom_r.htm#TOC"
+  >-------------------------------</a><a name="facetarea">-</a>
+
+  qh_facetarea(qh, facet )
+    return area for a facet
+
+  notes:
+    if non-simplicial,
+      uses centrum to triangulate facet and sums the projected areas.
+    if (qh->DELAUNAY),
+      computes projected area instead for last coordinate
+    assumes facet->normal exists
+    projecting tricoplanar facets to the hyperplane does not appear to make a difference
+
+  design:
+    if simplicial
+      compute area
+    else
+      for each ridge
+        compute area from centrum to ridge
+    negate area if upper Delaunay facet
+*/
+realT qh_facetarea(qhT *qh, facetT *facet) {
+  vertexT *apex;
+  pointT *centrum;
+  realT area= 0.0;
+  ridgeT *ridge, **ridgep;
+
+  if (facet->simplicial) {
+    apex= SETfirstt_(facet->vertices, vertexT);
+    area= qh_facetarea_simplex(qh, qh->hull_dim, apex->point, facet->vertices,
+                    apex, facet->toporient, facet->normal, &facet->offset);
+  }else {
+    if (qh->CENTERtype == qh_AScentrum)
+      centrum= facet->center;
+    else
+      centrum= qh_getcentrum(qh, facet);
+    FOREACHridge_(facet->ridges)
+      area += qh_facetarea_simplex(qh, qh->hull_dim, centrum, ridge->vertices,
+                 NULL, (boolT)(ridge->top == facet),  facet->normal, &facet->offset);
+    if (qh->CENTERtype != qh_AScentrum)
+      qh_memfree(qh, centrum, qh->normal_size);
+  }
+  if (facet->upperdelaunay && qh->DELAUNAY)
+    area= -area;  /* the normal should be [0,...,1] */
+  trace4((qh, qh->ferr, 4009, "qh_facetarea: f%d area %2.2g\n", facet->id, area));
+  return area;
+} /* facetarea */
+
+/*-<a                             href="qh-geom_r.htm#TOC"
+  >-------------------------------</a><a name="facetarea_simplex">-</a>
+
+  qh_facetarea_simplex(qh, dim, apex, vertices, notvertex, toporient, normal, offset )
+    return area for a simplex defined by
+      an apex, a base of vertices, an orientation, and a unit normal
+    if simplicial or tricoplanar facet,
+      notvertex is defined and it is skipped in vertices
+
+  returns:
+    computes area of simplex projected to plane [normal,offset]
+    returns 0 if vertex too far below plane (qh->WIDEfacet)
+      vertex can't be apex of tricoplanar facet
+
+  notes:
+    if (qh->DELAUNAY),
+      computes projected area instead for last coordinate
+    uses qh->gm_matrix/gm_row and qh->hull_dim
+    helper function for qh_facetarea
+
+  design:
+    if Notvertex
+      translate simplex to apex
+    else
+      project simplex to normal/offset
+      translate simplex to apex
+    if Delaunay
+      set last row/column to 0 with -1 on diagonal
+    else
+      set last row to Normal
+    compute determinate
+    scale and flip sign for area
+*/
+realT qh_facetarea_simplex(qhT *qh, int dim, coordT *apex, setT *vertices,
+        vertexT *notvertex,  boolT toporient, coordT *normal, realT *offset) {
+  pointT *coorda, *coordp, *gmcoord;
+  coordT **rows, *normalp;
+  int k,  i=0;
+  realT area, dist;
+  vertexT *vertex, **vertexp;
+  boolT nearzero;
+
+  gmcoord= qh->gm_matrix;
+  rows= qh->gm_row;
+  FOREACHvertex_(vertices) {
+    if (vertex == notvertex)
+      continue;
+    rows[i++]= gmcoord;
+    coorda= apex;
+    coordp= vertex->point;
+    normalp= normal;
+    if (notvertex) {
+      for (k=dim; k--; )
+        *(gmcoord++)= *coordp++ - *coorda++;
+    }else {
+      dist= *offset;
+      for (k=dim; k--; )
+        dist += *coordp++ * *normalp++;
+      if (dist < -qh->WIDEfacet) {
+        zinc_(Znoarea);
+        return 0.0;
+      }
+      coordp= vertex->point;
+      normalp= normal;
+      for (k=dim; k--; )
+        *(gmcoord++)= (*coordp++ - dist * *normalp++) - *coorda++;
+    }
+  }
+  if (i != dim-1) {
+    qh_fprintf(qh, qh->ferr, 6008, "qhull internal error (qh_facetarea_simplex): #points %d != dim %d -1\n",
+               i, dim);
+    qh_errexit(qh, qh_ERRqhull, NULL, NULL);
+  }
+  rows[i]= gmcoord;
+  if (qh->DELAUNAY) {
+    for (i=0; i < dim-1; i++)
+      rows[i][dim-1]= 0.0;
+    for (k=dim; k--; )
+      *(gmcoord++)= 0.0;
+    rows[dim-1][dim-1]= -1.0;
+  }else {
+    normalp= normal;
+    for (k=dim; k--; )
+      *(gmcoord++)= *normalp++;
+  }
+  zinc_(Zdetsimplex);
+  area= qh_determinant(qh, rows, dim, &nearzero);
+  if (toporient)
+    area= -area;
+  area *= qh->AREAfactor;
+  trace4((qh, qh->ferr, 4010, "qh_facetarea_simplex: area=%2.2g for point p%d, toporient %d, nearzero? %d\n",
+          area, qh_pointid(qh, apex), toporient, nearzero));
+  return area;
+} /* facetarea_simplex */
+
+/*-<a                             href="qh-geom_r.htm#TOC"
+  >-------------------------------</a><a name="facetcenter">-</a>
+
+  qh_facetcenter(qh, vertices )
+    return Voronoi center (Voronoi vertex) for a facet's vertices
+
+  returns:
+    return temporary point equal to the center
+
+  see:
+    qh_voronoi_center()
+*/
+pointT *qh_facetcenter(qhT *qh, setT *vertices) {
+  setT *points= qh_settemp(qh, qh_setsize(qh, vertices));
+  vertexT *vertex, **vertexp;
+  pointT *center;
+
+  FOREACHvertex_(vertices)
+    qh_setappend(qh, &points, vertex->point);
+  center= qh_voronoi_center(qh, qh->hull_dim-1, points);
+  qh_settempfree(qh, &points);
+  return center;
+} /* facetcenter */
+
+/*-<a                             href="qh-geom_r.htm#TOC"
+  >-------------------------------</a><a name="findgooddist">-</a>
+
+  qh_findgooddist(qh, point, facetA, dist, facetlist )
+    find best good facet visible for point from facetA
+    assumes facetA is visible from point
+
+  returns:
+    best facet, i.e., good facet that is furthest from point
+      distance to best facet
+      NULL if none
+
+    moves good, visible facets (and some other visible facets)
+      to end of qh->facet_list
+
+  notes:
+    uses qh->visit_id
+
+  design:
+    initialize bestfacet if facetA is good
+    move facetA to end of facetlist
+    for each facet on facetlist
+      for each unvisited neighbor of facet
+        move visible neighbors to end of facetlist
+        update best good neighbor
+        if no good neighbors, update best facet
+*/
+facetT *qh_findgooddist(qhT *qh, pointT *point, facetT *facetA, realT *distp,
+               facetT **facetlist) {
+  realT bestdist= -REALmax, dist;
+  facetT *neighbor, **neighborp, *bestfacet=NULL, *facet;
+  boolT goodseen= False;
+
+  if (facetA->good) {
+    zzinc_(Zcheckpart);  /* calls from check_bestdist occur after print stats */
+    qh_distplane(qh, point, facetA, &bestdist);
+    bestfacet= facetA;
+    goodseen= True;
+  }
+  qh_removefacet(qh, facetA);
+  qh_appendfacet(qh, facetA);
+  *facetlist= facetA;
+  facetA->visitid= ++qh->visit_id;
+  FORALLfacet_(*facetlist) {
+    FOREACHneighbor_(facet) {
+      if (neighbor->visitid == qh->visit_id)
+        continue;
+      neighbor->visitid= qh->visit_id;
+      if (goodseen && !neighbor->good)
+        continue;
+      zzinc_(Zcheckpart);
+      qh_distplane(qh, point, neighbor, &dist);
+      if (dist > 0) {
+        qh_removefacet(qh, neighbor);
+        qh_appendfacet(qh, neighbor);
+        if (neighbor->good) {
+          goodseen= True;
+          if (dist > bestdist) {
+            bestdist= dist;
+            bestfacet= neighbor;
+          }
+        }
+      }
+    }
+  }
+  if (bestfacet) {
+    *distp= bestdist;
+    trace2((qh, qh->ferr, 2003, "qh_findgooddist: p%d is %2.2g above good facet f%d\n",
+      qh_pointid(qh, point), bestdist, bestfacet->id));
+    return bestfacet;
+  }
+  trace4((qh, qh->ferr, 4011, "qh_findgooddist: no good facet for p%d above f%d\n",
+      qh_pointid(qh, point), facetA->id));
+  return NULL;
+}  /* findgooddist */
+
+/*-<a                             href="qh-geom_r.htm#TOC"
+  >-------------------------------</a><a name="getarea">-</a>
+
+  qh_getarea(qh, facetlist )
+    set area of all facets in facetlist
+    collect statistics
+    nop if hasAreaVolume
+
+  returns:
+    sets qh->totarea/totvol to total area and volume of convex hull
+    for Delaunay triangulation, computes projected area of the lower or upper hull
+      ignores upper hull if qh->ATinfinity
+
+  notes:
+    could compute outer volume by expanding facet area by rays from interior
+    the following attempt at perpendicular projection underestimated badly:
+      qh.totoutvol += (-dist + facet->maxoutside + qh->DISTround)
+                            * area/ qh->hull_dim;
+  design:
+    for each facet on facetlist
+      compute facet->area
+      update qh.totarea and qh.totvol
+*/
+void qh_getarea(qhT *qh, facetT *facetlist) {
+  realT area;
+  realT dist;
+  facetT *facet;
+
+  if (qh->hasAreaVolume)
+    return;
+  if (qh->REPORTfreq)
+    qh_fprintf(qh, qh->ferr, 8020, "computing area of each facet and volume of the convex hull\n");
+  else
+    trace1((qh, qh->ferr, 1001, "qh_getarea: computing volume and area for each facet\n"));
+  qh->totarea= qh->totvol= 0.0;
+  FORALLfacet_(facetlist) {
+    if (!facet->normal)
+      continue;
+    if (facet->upperdelaunay && qh->ATinfinity)
+      continue;
+    if (!facet->isarea) {
+      facet->f.area= qh_facetarea(qh, facet);
+      facet->isarea= True;
+    }
+    area= facet->f.area;
+    if (qh->DELAUNAY) {
+      if (facet->upperdelaunay == qh->UPPERdelaunay)
+        qh->totarea += area;
+    }else {
+      qh->totarea += area;
+      qh_distplane(qh, qh->interior_point, facet, &dist);
+      qh->totvol += -dist * area/ qh->hull_dim;
+    }
+    if (qh->PRINTstatistics) {
+      wadd_(Wareatot, area);
+      wmax_(Wareamax, area);
+      wmin_(Wareamin, area);
+    }
+  }
+  qh->hasAreaVolume= True;
+} /* getarea */
+
+/*-<a                             href="qh-geom_r.htm#TOC"
+  >-------------------------------</a><a name="gram_schmidt">-</a>
+
+  qh_gram_schmidt(qh, dim, row )
+    implements Gram-Schmidt orthogonalization by rows
+
+  returns:
+    false if zero norm
+    overwrites rows[dim][dim]
+
+  notes:
+    see Golub & van Loan, 1983, Algorithm 6.2-2, "Modified Gram-Schmidt"
+    overflow due to small divisors not handled
+
+  design:
+    for each row
+      compute norm for row
+      if non-zero, normalize row
+      for each remaining rowA
+        compute inner product of row and rowA
+        reduce rowA by row * inner product
+*/
+boolT qh_gram_schmidt(qhT *qh, int dim, realT **row) {
+  realT *rowi, *rowj, norm;
+  int i, j, k;
+
+  for (i=0; i < dim; i++) {
+    rowi= row[i];
+    for (norm= 0.0, k= dim; k--; rowi++)
+      norm += *rowi * *rowi;
+    norm= sqrt(norm);
+    wmin_(Wmindenom, norm);
+    if (norm == 0.0)  /* either 0 or overflow due to sqrt */
+      return False;
+    for (k=dim; k--; )
+      *(--rowi) /= norm;
+    for (j=i+1; j < dim; j++) {
+      rowj= row[j];
+      for (norm= 0.0, k=dim; k--; )
+        norm += *rowi++ * *rowj++;
+      for (k=dim; k--; )
+        *(--rowj) -= *(--rowi) * norm;
+    }
+  }
+  return True;
+} /* gram_schmidt */
+
+
+/*-<a                             href="qh-geom_r.htm#TOC"
+  >-------------------------------</a><a name="inthresholds">-</a>
+
+  qh_inthresholds(qh, normal, angle )
+    return True if normal within qh.lower_/upper_threshold
+
+  returns:
+    estimate of angle by summing of threshold diffs
+      angle may be NULL
+      smaller "angle" is better
+
+  notes:
+    invalid if qh.SPLITthresholds
+
+  see:
+    qh.lower_threshold in qh_initbuild()
+    qh_initthresholds()
+
+  design:
+    for each dimension
+      test threshold
+*/
+boolT qh_inthresholds(qhT *qh, coordT *normal, realT *angle) {
+  boolT within= True;
+  int k;
+  realT threshold;
+
+  if (angle)
+    *angle= 0.0;
+  for (k=0; k < qh->hull_dim; k++) {
+    threshold= qh->lower_threshold[k];
+    if (threshold > -REALmax/2) {
+      if (normal[k] < threshold)
+        within= False;
+      if (angle) {
+        threshold -= normal[k];
+        *angle += fabs_(threshold);
+      }
+    }
+    if (qh->upper_threshold[k] < REALmax/2) {
+      threshold= qh->upper_threshold[k];
+      if (normal[k] > threshold)
+        within= False;
+      if (angle) {
+        threshold -= normal[k];
+        *angle += fabs_(threshold);
+      }
+    }
+  }
+  return within;
+} /* inthresholds */
+
+
+/*-<a                             href="qh-geom_r.htm#TOC"
+  >-------------------------------</a><a name="joggleinput">-</a>
+
+  qh_joggleinput(qh)
+    randomly joggle input to Qhull by qh.JOGGLEmax
+    initial input is qh.first_point/qh.num_points of qh.hull_dim
+      repeated calls use qh.input_points/qh.num_points
+
+  returns:
+    joggles points at qh.first_point/qh.num_points
+    copies data to qh.input_points/qh.input_malloc if first time
+    determines qh.JOGGLEmax if it was zero
+    if qh.DELAUNAY
+      computes the Delaunay projection of the joggled points
+
+  notes:
+    if qh.DELAUNAY, unnecessarily joggles the last coordinate
+    the initial 'QJn' may be set larger than qh_JOGGLEmaxincrease
+
+  design:
+    if qh.DELAUNAY
+      set qh.SCALElast for reduced precision errors
+    if first call
+      initialize qh.input_points to the original input points
+      if qh.JOGGLEmax == 0
+        determine default qh.JOGGLEmax
+    else
+      increase qh.JOGGLEmax according to qh.build_cnt
+    joggle the input by adding a random number in [-qh.JOGGLEmax,qh.JOGGLEmax]
+    if qh.DELAUNAY
+      sets the Delaunay projection
+*/
+void qh_joggleinput(qhT *qh) {
+  int i, seed, size;
+  coordT *coordp, *inputp;
+  realT randr, randa, randb;
+
+  if (!qh->input_points) { /* first call */
+    qh->input_points= qh->first_point;
+    qh->input_malloc= qh->POINTSmalloc;
+    size= qh->num_points * qh->hull_dim * sizeof(coordT);
+    if (!(qh->first_point=(coordT*)qh_malloc((size_t)size))) {
+      qh_fprintf(qh, qh->ferr, 6009, "qhull error: insufficient memory to joggle %d points\n",
+          qh->num_points);
+      qh_errexit(qh, qh_ERRmem, NULL, NULL);
+    }
+    qh->POINTSmalloc= True;
+    if (qh->JOGGLEmax == 0.0) {
+      qh->JOGGLEmax= qh_detjoggle(qh, qh->input_points, qh->num_points, qh->hull_dim);
+      qh_option(qh, "QJoggle", NULL, &qh->JOGGLEmax);
+    }
+  }else {                 /* repeated call */
+    if (!qh->RERUN && qh->build_cnt > qh_JOGGLEretry) {
+      if (((qh->build_cnt-qh_JOGGLEretry-1) % qh_JOGGLEagain) == 0) {
+        realT maxjoggle= qh->MAXwidth * qh_JOGGLEmaxincrease;
+        if (qh->JOGGLEmax < maxjoggle) {
+          qh->JOGGLEmax *= qh_JOGGLEincrease;
+          minimize_(qh->JOGGLEmax, maxjoggle);
+        }
+      }
+    }
+    qh_option(qh, "QJoggle", NULL, &qh->JOGGLEmax);
+  }
+  if (qh->build_cnt > 1 && qh->JOGGLEmax > fmax_(qh->MAXwidth/4, 0.1)) {
+      qh_fprintf(qh, qh->ferr, 6010, "qhull error: the current joggle for 'QJn', %.2g, is too large for the width\nof the input.  If possible, recompile Qhull with higher-precision reals.\n",
+                qh->JOGGLEmax);
+      qh_errexit(qh, qh_ERRqhull, NULL, NULL);
+  }
+  /* for some reason, using qh->ROTATErandom and qh_RANDOMseed does not repeat the run. Use 'TRn' instead */
+  seed= qh_RANDOMint;
+  qh_option(qh, "_joggle-seed", &seed, NULL);
+  trace0((qh, qh->ferr, 6, "qh_joggleinput: joggle input by %2.2g with seed %d\n",
+    qh->JOGGLEmax, seed));
+  inputp= qh->input_points;
+  coordp= qh->first_point;
+  randa= 2.0 * qh->JOGGLEmax/qh_RANDOMmax;
+  randb= -qh->JOGGLEmax;
+  size= qh->num_points * qh->hull_dim;
+  for (i=size; i--; ) {
+    randr= qh_RANDOMint;
+    *(coordp++)= *(inputp++) + (randr * randa + randb);
+  }
+  if (qh->DELAUNAY) {
+    qh->last_low= qh->last_high= qh->last_newhigh= REALmax;
+    qh_setdelaunay(qh, qh->hull_dim, qh->num_points, qh->first_point);
+  }
+} /* joggleinput */
+
+/*-<a                             href="qh-geom_r.htm#TOC"
+  >-------------------------------</a><a name="maxabsval">-</a>
+
+  qh_maxabsval( normal, dim )
+    return pointer to maximum absolute value of a dim vector
+    returns NULL if dim=0
+*/
+realT *qh_maxabsval(realT *normal, int dim) {
+  realT maxval= -REALmax;
+  realT *maxp= NULL, *colp, absval;
+  int k;
+
+  for (k=dim, colp= normal; k--; colp++) {
+    absval= fabs_(*colp);
+    if (absval > maxval) {
+      maxval= absval;
+      maxp= colp;
+    }
+  }
+  return maxp;
+} /* maxabsval */
+
+
+/*-<a                             href="qh-geom_r.htm#TOC"
+  >-------------------------------</a><a name="maxmin">-</a>
+
+  qh_maxmin(qh, points, numpoints, dimension )
+    return max/min points for each dimension
+    determine max and min coordinates
+
+  returns:
+    returns a temporary set of max and min points
+      may include duplicate points. Does not include qh.GOODpoint
+    sets qh.NEARzero, qh.MAXabs_coord, qh.MAXsumcoord, qh.MAXwidth
+         qh.MAXlastcoord, qh.MINlastcoord
+    initializes qh.max_outside, qh.min_vertex, qh.WAScoplanar, qh.ZEROall_ok
+
+  notes:
+    loop duplicated in qh_detjoggle()
+
+  design:
+    initialize global precision variables
+    checks definition of REAL...
+    for each dimension
+      for each point
+        collect maximum and minimum point
+      collect maximum of maximums and minimum of minimums
+      determine qh.NEARzero for Gaussian Elimination
+*/
+setT *qh_maxmin(qhT *qh, pointT *points, int numpoints, int dimension) {
+  int k;
+  realT maxcoord, temp;
+  pointT *minimum, *maximum, *point, *pointtemp;
+  setT *set;
+
+  qh->max_outside= 0.0;
+  qh->MAXabs_coord= 0.0;
+  qh->MAXwidth= -REALmax;
+  qh->MAXsumcoord= 0.0;
+  qh->min_vertex= 0.0;
+  qh->WAScoplanar= False;
+  if (qh->ZEROcentrum)
+    qh->ZEROall_ok= True;
+  if (REALmin < REALepsilon && REALmin < REALmax && REALmin > -REALmax
+  && REALmax > 0.0 && -REALmax < 0.0)
+    ; /* all ok */
+  else {
+    qh_fprintf(qh, qh->ferr, 6011, "qhull error: floating point constants in user.h are wrong\n\
+REALepsilon %g REALmin %g REALmax %g -REALmax %g\n",
+             REALepsilon, REALmin, REALmax, -REALmax);
+    qh_errexit(qh, qh_ERRinput, NULL, NULL);
+  }
+  set= qh_settemp(qh, 2*dimension);
+  for (k=0; k < dimension; k++) {
+    if (points == qh->GOODpointp)
+      minimum= maximum= points + dimension;
+    else
+      minimum= maximum= points;
+    FORALLpoint_(qh, points, numpoints) {
+      if (point == qh->GOODpointp)
+        continue;
+      if (maximum[k] < point[k])
+        maximum= point;
+      else if (minimum[k] > point[k])
+        minimum= point;
+    }
+    if (k == dimension-1) {
+      qh->MINlastcoord= minimum[k];
+      qh->MAXlastcoord= maximum[k];
+    }
+    if (qh->SCALElast && k == dimension-1)
+      maxcoord= qh->MAXwidth;
+    else {
+      maxcoord= fmax_(maximum[k], -minimum[k]);
+      if (qh->GOODpointp) {
+        temp= fmax_(qh->GOODpointp[k], -qh->GOODpointp[k]);
+        maximize_(maxcoord, temp);
+      }
+      temp= maximum[k] - minimum[k];
+      maximize_(qh->MAXwidth, temp);
+    }
+    maximize_(qh->MAXabs_coord, maxcoord);
+    qh->MAXsumcoord += maxcoord;
+    qh_setappend(qh, &set, maximum);
+    qh_setappend(qh, &set, minimum);
+    /* calculation of qh NEARzero is based on Golub & van Loan, 1983,
+       Eq. 4.4-13 for "Gaussian elimination with complete pivoting".
+       Golub & van Loan say that n^3 can be ignored and 10 be used in
+       place of rho */
+    qh->NEARzero[k]= 80 * qh->MAXsumcoord * REALepsilon;
+  }
+  if (qh->IStracing >=1)
+    qh_printpoints(qh, qh->ferr, "qh_maxmin: found the max and min points(by dim):", set);
+  return(set);
+} /* maxmin */
+
+/*-<a                             href="qh-geom_r.htm#TOC"
+  >-------------------------------</a><a name="maxouter">-</a>
+
+  qh_maxouter(qh)
+    return maximum distance from facet to outer plane
+    normally this is qh.max_outside+qh.DISTround
+    does not include qh.JOGGLEmax
+
+  see:
+    qh_outerinner()
+
+  notes:
+    need to add another qh.DISTround if testing actual point with computation
+
+  for joggle:
+    qh_setfacetplane() updated qh.max_outer for Wnewvertexmax (max distance to vertex)
+    need to use Wnewvertexmax since could have a coplanar point for a high
+      facet that is replaced by a low facet
+    need to add qh.JOGGLEmax if testing input points
+*/
+realT qh_maxouter(qhT *qh) {
+  realT dist;
+
+  dist= fmax_(qh->max_outside, qh->DISTround);
+  dist += qh->DISTround;
+  trace4((qh, qh->ferr, 4012, "qh_maxouter: max distance from facet to outer plane is %2.2g max_outside is %2.2g\n", dist, qh->max_outside));
+  return dist;
+} /* maxouter */
+
+/*-<a                             href="qh-geom_r.htm#TOC"
+  >-------------------------------</a><a name="maxsimplex">-</a>
+
+  qh_maxsimplex(qh, dim, maxpoints, points, numpoints, simplex )
+    determines maximum simplex for a set of points
+    starts from points already in simplex
+    skips qh.GOODpointp (assumes that it isn't in maxpoints)
+
+  returns:
+    simplex with dim+1 points
+
+  notes:
+    assumes at least pointsneeded points in points
+    maximizes determinate for x,y,z,w, etc.
+    uses maxpoints as long as determinate is clearly non-zero
+
+  design:
+    initialize simplex with at least two points
+      (find points with max or min x coordinate)
+    for each remaining dimension
+      add point that maximizes the determinate
+        (use points from maxpoints first)
+*/
+void qh_maxsimplex(qhT *qh, int dim, setT *maxpoints, pointT *points, int numpoints, setT **simplex) {
+  pointT *point, **pointp, *pointtemp, *maxpoint, *minx=NULL, *maxx=NULL;
+  boolT nearzero, maxnearzero= False;
+  int k, sizinit;
+  realT maxdet= -REALmax, det, mincoord= REALmax, maxcoord= -REALmax;
+
+  sizinit= qh_setsize(qh, *simplex);
+  if (sizinit < 2) {
+    if (qh_setsize(qh, maxpoints) >= 2) {
+      FOREACHpoint_(maxpoints) {
+        if (maxcoord < point[0]) {
+          maxcoord= point[0];
+          maxx= point;
+        }
+        if (mincoord > point[0]) {
+          mincoord= point[0];
+          minx= point;
+        }
+      }
+    }else {
+      FORALLpoint_(qh, points, numpoints) {
+        if (point == qh->GOODpointp)
+          continue;
+        if (maxcoord < point[0]) {
+          maxcoord= point[0];
+          maxx= point;
+        }
+        if (mincoord > point[0]) {
+          mincoord= point[0];
+          minx= point;
+        }
+      }
+    }
+    qh_setunique(qh, simplex, minx);
+    if (qh_setsize(qh, *simplex) < 2)
+      qh_setunique(qh, simplex, maxx);
+    sizinit= qh_setsize(qh, *simplex);
+    if (sizinit < 2) {
+      qh_precision(qh, "input has same x coordinate");
+      if (zzval_(Zsetplane) > qh->hull_dim+1) {
+        qh_fprintf(qh, qh->ferr, 6012, "qhull precision error (qh_maxsimplex for voronoi_center):\n%d points with the same x coordinate.\n",
+                 qh_setsize(qh, maxpoints)+numpoints);
+        qh_errexit(qh, qh_ERRprec, NULL, NULL);
+      }else {
+        qh_fprintf(qh, qh->ferr, 6013, "qhull input error: input is less than %d-dimensional since it has the same x coordinate\n", qh->hull_dim);
+        qh_errexit(qh, qh_ERRinput, NULL, NULL);
+      }
+    }
+  }
+  for (k=sizinit; k < dim+1; k++) {
+    maxpoint= NULL;
+    maxdet= -REALmax;
+    FOREACHpoint_(maxpoints) {
+      if (!qh_setin(*simplex, point)) {
+        det= qh_detsimplex(qh, point, *simplex, k, &nearzero);
+        if ((det= fabs_(det)) > maxdet) {
+          maxdet= det;
+          maxpoint= point;
+          maxnearzero= nearzero;
+        }
+      }
+    }
+    if (!maxpoint || maxnearzero) {
+      zinc_(Zsearchpoints);
+      if (!maxpoint) {
+        trace0((qh, qh->ferr, 7, "qh_maxsimplex: searching all points for %d-th initial vertex.\n", k+1));
+      }else {
+        trace0((qh, qh->ferr, 8, "qh_maxsimplex: searching all points for %d-th initial vertex, better than p%d det %2.2g\n",
+                k+1, qh_pointid(qh, maxpoint), maxdet));
+      }
+      FORALLpoint_(qh, points, numpoints) {
+        if (point == qh->GOODpointp)
+          continue;
+        if (!qh_setin(*simplex, point)) {
+          det= qh_detsimplex(qh, point, *simplex, k, &nearzero);
+          if ((det= fabs_(det)) > maxdet) {
+            maxdet= det;
+            maxpoint= point;
+            maxnearzero= nearzero;
+          }
+        }
+      }
+    } /* !maxpoint */
+    if (!maxpoint) {
+      qh_fprintf(qh, qh->ferr, 6014, "qhull internal error (qh_maxsimplex): not enough points available\n");
+      qh_errexit(qh, qh_ERRqhull, NULL, NULL);
+    }
+    qh_setappend(qh, simplex, maxpoint);
+    trace1((qh, qh->ferr, 1002, "qh_maxsimplex: selected point p%d for %d`th initial vertex, det=%2.2g\n",
+            qh_pointid(qh, maxpoint), k+1, maxdet));
+  } /* k */
+} /* maxsimplex */
+
+/*-<a                             href="qh-geom_r.htm#TOC"
+  >-------------------------------</a><a name="minabsval">-</a>
+
+  qh_minabsval( normal, dim )
+    return minimum absolute value of a dim vector
+*/
+realT qh_minabsval(realT *normal, int dim) {
+  realT minval= 0;
+  realT maxval= 0;
+  realT *colp;
+  int k;
+
+  for (k=dim, colp=normal; k--; colp++) {
+    maximize_(maxval, *colp);
+    minimize_(minval, *colp);
+  }
+  return fmax_(maxval, -minval);
+} /* minabsval */
+
+
+/*-<a                             href="qh-geom_r.htm#TOC"
+  >-------------------------------</a><a name="mindiff">-</a>
+
+  qh_mindif(qh, vecA, vecB, dim )
+    return index of min abs. difference of two vectors
+*/
+int qh_mindiff(realT *vecA, realT *vecB, int dim) {
+  realT mindiff= REALmax, diff;
+  realT *vecAp= vecA, *vecBp= vecB;
+  int k, mink= 0;
+
+  for (k=0; k < dim; k++) {
+    diff= *vecAp++ - *vecBp++;
+    diff= fabs_(diff);
+    if (diff < mindiff) {
+      mindiff= diff;
+      mink= k;
+    }
+  }
+  return mink;
+} /* mindiff */
+
+
+
+/*-<a                             href="qh-geom_r.htm#TOC"
+  >-------------------------------</a><a name="orientoutside">-</a>
+
+  qh_orientoutside(qh, facet  )
+    make facet outside oriented via qh.interior_point
+
+  returns:
+    True if facet reversed orientation.
+*/
+boolT qh_orientoutside(qhT *qh, facetT *facet) {
+  int k;
+  realT dist;
+
+  qh_distplane(qh, qh->interior_point, facet, &dist);
+  if (dist > 0) {
+    for (k=qh->hull_dim; k--; )
+      facet->normal[k]= -facet->normal[k];
+    facet->offset= -facet->offset;
+    return True;
+  }
+  return False;
+} /* orientoutside */
+
+/*-<a                             href="qh-geom_r.htm#TOC"
+  >-------------------------------</a><a name="outerinner">-</a>
+
+  qh_outerinner(qh, facet, outerplane, innerplane  )
+    if facet and qh.maxoutdone (i.e., qh_check_maxout)
+      returns outer and inner plane for facet
+    else
+      returns maximum outer and inner plane
+    accounts for qh.JOGGLEmax
+
+  see:
+    qh_maxouter(qh), qh_check_bestdist(), qh_check_points()
+
+  notes:
+    outerplaner or innerplane may be NULL
+    facet is const
+    Does not error (QhullFacet)
+
+    includes qh.DISTround for actual points
+    adds another qh.DISTround if testing with floating point arithmetic
+*/
+void qh_outerinner(qhT *qh, facetT *facet, realT *outerplane, realT *innerplane) {
+  realT dist, mindist;
+  vertexT *vertex, **vertexp;
+
+  if (outerplane) {
+    if (!qh_MAXoutside || !facet || !qh->maxoutdone) {
+      *outerplane= qh_maxouter(qh);       /* includes qh.DISTround */
+    }else { /* qh_MAXoutside ... */
+#if qh_MAXoutside
+      *outerplane= facet->maxoutside + qh->DISTround;
+#endif
+
+    }
+    if (qh->JOGGLEmax < REALmax/2)
+      *outerplane += qh->JOGGLEmax * sqrt((realT)qh->hull_dim);
+  }
+  if (innerplane) {
+    if (facet) {
+      mindist= REALmax;
+      FOREACHvertex_(facet->vertices) {
+        zinc_(Zdistio);
+        qh_distplane(qh, vertex->point, facet, &dist);
+        minimize_(mindist, dist);
+      }
+      *innerplane= mindist - qh->DISTround;
+    }else
+      *innerplane= qh->min_vertex - qh->DISTround;
+    if (qh->JOGGLEmax < REALmax/2)
+      *innerplane -= qh->JOGGLEmax * sqrt((realT)qh->hull_dim);
+  }
+} /* outerinner */
+
+/*-<a                             href="qh-geom_r.htm#TOC"
+  >-------------------------------</a><a name="pointdist">-</a>
+
+  qh_pointdist( point1, point2, dim )
+    return distance between two points
+
+  notes:
+    returns distance squared if 'dim' is negative
+*/
+coordT qh_pointdist(pointT *point1, pointT *point2, int dim) {
+  coordT dist, diff;
+  int k;
+
+  dist= 0.0;
+  for (k= (dim > 0 ? dim : -dim); k--; ) {
+    diff= *point1++ - *point2++;
+    dist += diff * diff;
+  }
+  if (dim > 0)
+    return(sqrt(dist));
+  return dist;
+} /* pointdist */
+
+
+/*-<a                             href="qh-geom_r.htm#TOC"
+  >-------------------------------</a><a name="printmatrix">-</a>
+
+  qh_printmatrix(qh, fp, string, rows, numrow, numcol )
+    print matrix to fp given by row vectors
+    print string as header
+    qh may be NULL if fp is defined
+
+  notes:
+    print a vector by qh_printmatrix(qh, fp, "", &vect, 1, len)
+*/
+void qh_printmatrix(qhT *qh, FILE *fp, const char *string, realT **rows, int numrow, int numcol) {
+  realT *rowp;
+  realT r; /*bug fix*/
+  int i,k;
+
+  qh_fprintf(qh, fp, 9001, "%s\n", string);
+  for (i=0; i < numrow; i++) {
+    rowp= rows[i];
+    for (k=0; k < numcol; k++) {
+      r= *rowp++;
+      qh_fprintf(qh, fp, 9002, "%6.3g ", r);
+    }
+    qh_fprintf(qh, fp, 9003, "\n");
+  }
+} /* printmatrix */
+
+
+/*-<a                             href="qh-geom_r.htm#TOC"
+  >-------------------------------</a><a name="printpoints">-</a>
+
+  qh_printpoints(qh, fp, string, points )
+    print pointids to fp for a set of points
+    if string, prints string and 'p' point ids
+*/
+void qh_printpoints(qhT *qh, FILE *fp, const char *string, setT *points) {
+  pointT *point, **pointp;
+
+  if (string) {
+    qh_fprintf(qh, fp, 9004, "%s", string);
+    FOREACHpoint_(points)
+      qh_fprintf(qh, fp, 9005, " p%d", qh_pointid(qh, point));
+    qh_fprintf(qh, fp, 9006, "\n");
+  }else {
+    FOREACHpoint_(points)
+      qh_fprintf(qh, fp, 9007, " %d", qh_pointid(qh, point));
+    qh_fprintf(qh, fp, 9008, "\n");
+  }
+} /* printpoints */
+
+
+/*-<a                             href="qh-geom_r.htm#TOC"
+  >-------------------------------</a><a name="projectinput">-</a>
+
+  qh_projectinput(qh)
+    project input points using qh.lower_bound/upper_bound and qh->DELAUNAY
+    if qh.lower_bound[k]=qh.upper_bound[k]= 0,
+      removes dimension k
+    if halfspace intersection
+      removes dimension k from qh.feasible_point
+    input points in qh->first_point, num_points, input_dim
+
+  returns:
+    new point array in qh->first_point of qh->hull_dim coordinates
+    sets qh->POINTSmalloc
+    if qh->DELAUNAY
+      projects points to paraboloid
+      lowbound/highbound is also projected
+    if qh->ATinfinity
+      adds point "at-infinity"
+    if qh->POINTSmalloc
+      frees old point array
+
+  notes:
+    checks that qh.hull_dim agrees with qh.input_dim, PROJECTinput, and DELAUNAY
+
+
+  design:
+    sets project[k] to -1 (delete), 0 (keep), 1 (add for Delaunay)
+    determines newdim and newnum for qh->hull_dim and qh->num_points
+    projects points to newpoints
+    projects qh.lower_bound to itself
+    projects qh.upper_bound to itself
+    if qh->DELAUNAY
+      if qh->ATINFINITY
+        projects points to paraboloid
+        computes "infinity" point as vertex average and 10% above all points
+      else
+        uses qh_setdelaunay to project points to paraboloid
+*/
+void qh_projectinput(qhT *qh) {
+  int k,i;
+  int newdim= qh->input_dim, newnum= qh->num_points;
+  signed char *project;
+  int projectsize= (qh->input_dim+1)*sizeof(*project);
+  pointT *newpoints, *coord, *infinity;
+  realT paraboloid, maxboloid= 0;
+
+  project= (signed char*)qh_memalloc(qh, projectsize);
+  memset((char*)project, 0, (size_t)projectsize);
+  for (k=0; k < qh->input_dim; k++) {   /* skip Delaunay bound */
+    if (qh->lower_bound[k] == 0 && qh->upper_bound[k] == 0) {
+      project[k]= -1;
+      newdim--;
+    }
+  }
+  if (qh->DELAUNAY) {
+    project[k]= 1;
+    newdim++;
+    if (qh->ATinfinity)
+      newnum++;
+  }
+  if (newdim != qh->hull_dim) {
+    qh_memfree(qh, project, projectsize);
+    qh_fprintf(qh, qh->ferr, 6015, "qhull internal error (qh_projectinput): dimension after projection %d != hull_dim %d\n", newdim, qh->hull_dim);
+    qh_errexit(qh, qh_ERRqhull, NULL, NULL);
+  }
+  if (!(newpoints= qh->temp_malloc= (coordT*)qh_malloc(newnum*newdim*sizeof(coordT)))){
+    qh_memfree(qh, project, projectsize);
+    qh_fprintf(qh, qh->ferr, 6016, "qhull error: insufficient memory to project %d points\n",
+           qh->num_points);
+    qh_errexit(qh, qh_ERRmem, NULL, NULL);
+  }
+  /* qh_projectpoints throws error if mismatched dimensions */
+  qh_projectpoints(qh, project, qh->input_dim+1, qh->first_point,
+                    qh->num_points, qh->input_dim, newpoints, newdim);
+  trace1((qh, qh->ferr, 1003, "qh_projectinput: updating lower and upper_bound\n"));
+  qh_projectpoints(qh, project, qh->input_dim+1, qh->lower_bound,
+                    1, qh->input_dim+1, qh->lower_bound, newdim+1);
+  qh_projectpoints(qh, project, qh->input_dim+1, qh->upper_bound,
+                    1, qh->input_dim+1, qh->upper_bound, newdim+1);
+  if (qh->HALFspace) {
+    if (!qh->feasible_point) {
+      qh_memfree(qh, project, projectsize);
+      qh_fprintf(qh, qh->ferr, 6017, "qhull internal error (qh_projectinput): HALFspace defined without qh.feasible_point\n");
+      qh_errexit(qh, qh_ERRqhull, NULL, NULL);
+    }
+    qh_projectpoints(qh, project, qh->input_dim, qh->feasible_point,
+                      1, qh->input_dim, qh->feasible_point, newdim);
+  }
+  qh_memfree(qh, project, projectsize);
+  if (qh->POINTSmalloc)
+    qh_free(qh->first_point);
+  qh->first_point= newpoints;
+  qh->POINTSmalloc= True;
+  qh->temp_malloc= NULL;
+  if (qh->DELAUNAY && qh->ATinfinity) {
+    coord= qh->first_point;
+    infinity= qh->first_point + qh->hull_dim * qh->num_points;
+    for (k=qh->hull_dim-1; k--; )
+      infinity[k]= 0.0;
+    for (i=qh->num_points; i--; ) {
+      paraboloid= 0.0;
+      for (k=0; k < qh->hull_dim-1; k++) {
+        paraboloid += *coord * *coord;
+        infinity[k] += *coord;
+        coord++;
+      }
+      *(coord++)= paraboloid;
+      maximize_(maxboloid, paraboloid);
+    }
+    /* coord == infinity */
+    for (k=qh->hull_dim-1; k--; )
+      *(coord++) /= qh->num_points;
+    *(coord++)= maxboloid * 1.1;
+    qh->num_points++;
+    trace0((qh, qh->ferr, 9, "qh_projectinput: projected points to paraboloid for Delaunay\n"));
+  }else if (qh->DELAUNAY)  /* !qh->ATinfinity */
+    qh_setdelaunay(qh, qh->hull_dim, qh->num_points, qh->first_point);
+} /* projectinput */
+
+
+/*-<a                             href="qh-geom_r.htm#TOC"
+  >-------------------------------</a><a name="projectpoints">-</a>
+
+  qh_projectpoints(qh, project, n, points, numpoints, dim, newpoints, newdim )
+    project points/numpoints/dim to newpoints/newdim
+    if project[k] == -1
+      delete dimension k
+    if project[k] == 1
+      add dimension k by duplicating previous column
+    n is size of project
+
+  notes:
+    newpoints may be points if only adding dimension at end
+
+  design:
+    check that 'project' and 'newdim' agree
+    for each dimension
+      if project == -1
+        skip dimension
+      else
+        determine start of column in newpoints
+        determine start of column in points
+          if project == +1, duplicate previous column
+        copy dimension (column) from points to newpoints
+*/
+void qh_projectpoints(qhT *qh, signed char *project, int n, realT *points,
+        int numpoints, int dim, realT *newpoints, int newdim) {
+  int testdim= dim, oldk=0, newk=0, i,j=0,k;
+  realT *newp, *oldp;
+
+  for (k=0; k < n; k++)
+    testdim += project[k];
+  if (testdim != newdim) {
+    qh_fprintf(qh, qh->ferr, 6018, "qhull internal error (qh_projectpoints): newdim %d should be %d after projection\n",
+      newdim, testdim);
+    qh_errexit(qh, qh_ERRqhull, NULL, NULL);
+  }
+  for (j=0; j<n; j++) {
+    if (project[j] == -1)
+      oldk++;
+    else {
+      newp= newpoints+newk++;
+      if (project[j] == +1) {
+        if (oldk >= dim)
+          continue;
+        oldp= points+oldk;
+      }else
+        oldp= points+oldk++;
+      for (i=numpoints; i--; ) {
+        *newp= *oldp;
+        newp += newdim;
+        oldp += dim;
+      }
+    }
+    if (oldk >= dim)
+      break;
+  }
+  trace1((qh, qh->ferr, 1004, "qh_projectpoints: projected %d points from dim %d to dim %d\n",
+    numpoints, dim, newdim));
+} /* projectpoints */
+
+
+/*-<a                             href="qh-geom_r.htm#TOC"
+  >-------------------------------</a><a name="rotateinput">-</a>
+
+  qh_rotateinput(qh, rows )
+    rotate input using row matrix
+    input points given by qh->first_point, num_points, hull_dim
+    assumes rows[dim] is a scratch buffer
+    if qh->POINTSmalloc, overwrites input points, else mallocs a new array
+
+  returns:
+    rotated input
+    sets qh->POINTSmalloc
+
+  design:
+    see qh_rotatepoints
+*/
+void qh_rotateinput(qhT *qh, realT **rows) {
+
+  if (!qh->POINTSmalloc) {
+    qh->first_point= qh_copypoints(qh, qh->first_point, qh->num_points, qh->hull_dim);
+    qh->POINTSmalloc= True;
+  }
+  qh_rotatepoints(qh, qh->first_point, qh->num_points, qh->hull_dim, rows);
+}  /* rotateinput */
+
+/*-<a                             href="qh-geom_r.htm#TOC"
+  >-------------------------------</a><a name="rotatepoints">-</a>
+
+  qh_rotatepoints(qh, points, numpoints, dim, row )
+    rotate numpoints points by a d-dim row matrix
+    assumes rows[dim] is a scratch buffer
+
+  returns:
+    rotated points in place
+
+  design:
+    for each point
+      for each coordinate
+        use row[dim] to compute partial inner product
+      for each coordinate
+        rotate by partial inner product
+*/
+void qh_rotatepoints(qhT *qh, realT *points, int numpoints, int dim, realT **row) {
+  realT *point, *rowi, *coord= NULL, sum, *newval;
+  int i,j,k;
+
+  if (qh->IStracing >= 1)
+    qh_printmatrix(qh, qh->ferr, "qh_rotatepoints: rotate points by", row, dim, dim);
+  for (point= points, j= numpoints; j--; point += dim) {
+    newval= row[dim];
+    for (i=0; i < dim; i++) {
+      rowi= row[i];
+      coord= point;
+      for (sum= 0.0, k= dim; k--; )
+        sum += *rowi++ * *coord++;
+      *(newval++)= sum;
+    }
+    for (k=dim; k--; )
+      *(--coord)= *(--newval);
+  }
+} /* rotatepoints */
+
+
+/*-<a                             href="qh-geom_r.htm#TOC"
+  >-------------------------------</a><a name="scaleinput">-</a>
+
+  qh_scaleinput(qh)
+    scale input points using qh->low_bound/high_bound
+    input points given by qh->first_point, num_points, hull_dim
+    if qh->POINTSmalloc, overwrites input points, else mallocs a new array
+
+  returns:
+    scales coordinates of points to low_bound[k], high_bound[k]
+    sets qh->POINTSmalloc
+
+  design:
+    see qh_scalepoints
+*/
+void qh_scaleinput(qhT *qh) {
+
+  if (!qh->POINTSmalloc) {
+    qh->first_point= qh_copypoints(qh, qh->first_point, qh->num_points, qh->hull_dim);
+    qh->POINTSmalloc= True;
+  }
+  qh_scalepoints(qh, qh->first_point, qh->num_points, qh->hull_dim,
+       qh->lower_bound, qh->upper_bound);
+}  /* scaleinput */
+
+/*-<a                             href="qh-geom_r.htm#TOC"
+  >-------------------------------</a><a name="scalelast">-</a>
+
+  qh_scalelast(qh, points, numpoints, dim, low, high, newhigh )
+    scale last coordinate to [0,m] for Delaunay triangulations
+    input points given by points, numpoints, dim
+
+  returns:
+    changes scale of last coordinate from [low, high] to [0, newhigh]
+    overwrites last coordinate of each point
+    saves low/high/newhigh in qh.last_low, etc. for qh_setdelaunay()
+
+  notes:
+    when called by qh_setdelaunay, low/high may not match actual data
+
+  design:
+    compute scale and shift factors
+    apply to last coordinate of each point
+*/
+void qh_scalelast(qhT *qh, coordT *points, int numpoints, int dim, coordT low,
+                   coordT high, coordT newhigh) {
+  realT scale, shift;
+  coordT *coord;
+  int i;
+  boolT nearzero= False;
+
+  trace4((qh, qh->ferr, 4013, "qh_scalelast: scale last coordinate from [%2.2g, %2.2g] to [0,%2.2g]\n",
+    low, high, newhigh));
+  qh->last_low= low;
+  qh->last_high= high;
+  qh->last_newhigh= newhigh;
+  scale= qh_divzero(newhigh, high - low,
+                  qh->MINdenom_1, &nearzero);
+  if (nearzero) {
+    if (qh->DELAUNAY)
+      qh_fprintf(qh, qh->ferr, 6019, "qhull input error: can not scale last coordinate.  Input is cocircular\n   or cospherical.   Use option 'Qz' to add a point at infinity.\n");
+    else
+      qh_fprintf(qh, qh->ferr, 6020, "qhull input error: can not scale last coordinate.  New bounds [0, %2.2g] are too wide for\nexisting bounds [%2.2g, %2.2g] (width %2.2g)\n",
+                newhigh, low, high, high-low);
+    qh_errexit(qh, qh_ERRinput, NULL, NULL);
+  }
+  shift= - low * newhigh / (high-low);
+  coord= points + dim - 1;
+  for (i=numpoints; i--; coord += dim)
+    *coord= *coord * scale + shift;
+} /* scalelast */
+
+/*-<a                             href="qh-geom_r.htm#TOC"
+  >-------------------------------</a><a name="scalepoints">-</a>
+
+  qh_scalepoints(qh, points, numpoints, dim, newlows, newhighs )
+    scale points to new lowbound and highbound
+    retains old bound when newlow= -REALmax or newhigh= +REALmax
+
+  returns:
+    scaled points
+    overwrites old points
+
+  design:
+    for each coordinate
+      compute current low and high bound
+      compute scale and shift factors
+      scale all points
+      enforce new low and high bound for all points
+*/
+void qh_scalepoints(qhT *qh, pointT *points, int numpoints, int dim,
+        realT *newlows, realT *newhighs) {
+  int i,k;
+  realT shift, scale, *coord, low, high, newlow, newhigh, mincoord, maxcoord;
+  boolT nearzero= False;
+
+  for (k=0; k < dim; k++) {
+    newhigh= newhighs[k];
+    newlow= newlows[k];
+    if (newhigh > REALmax/2 && newlow < -REALmax/2)
+      continue;
+    low= REALmax;
+    high= -REALmax;
+    for (i=numpoints, coord=points+k; i--; coord += dim) {
+      minimize_(low, *coord);
+      maximize_(high, *coord);
+    }
+    if (newhigh > REALmax/2)
+      newhigh= high;
+    if (newlow < -REALmax/2)
+      newlow= low;
+    if (qh->DELAUNAY && k == dim-1 && newhigh < newlow) {
+      qh_fprintf(qh, qh->ferr, 6021, "qhull input error: 'Qb%d' or 'QB%d' inverts paraboloid since high bound %.2g < low bound %.2g\n",
+               k, k, newhigh, newlow);
+      qh_errexit(qh, qh_ERRinput, NULL, NULL);
+    }
+    scale= qh_divzero(newhigh - newlow, high - low,
+                  qh->MINdenom_1, &nearzero);
+    if (nearzero) {
+      qh_fprintf(qh, qh->ferr, 6022, "qhull input error: %d'th dimension's new bounds [%2.2g, %2.2g] too wide for\nexisting bounds [%2.2g, %2.2g]\n",
+              k, newlow, newhigh, low, high);
+      qh_errexit(qh, qh_ERRinput, NULL, NULL);
+    }
+    shift= (newlow * high - low * newhigh)/(high-low);
+    coord= points+k;
+    for (i=numpoints; i--; coord += dim)
+      *coord= *coord * scale + shift;
+    coord= points+k;
+    if (newlow < newhigh) {
+      mincoord= newlow;
+      maxcoord= newhigh;
+    }else {
+      mincoord= newhigh;
+      maxcoord= newlow;
+    }
+    for (i=numpoints; i--; coord += dim) {
+      minimize_(*coord, maxcoord);  /* because of roundoff error */
+      maximize_(*coord, mincoord);
+    }
+    trace0((qh, qh->ferr, 10, "qh_scalepoints: scaled %d'th coordinate [%2.2g, %2.2g] to [%.2g, %.2g] for %d points by %2.2g and shifted %2.2g\n",
+      k, low, high, newlow, newhigh, numpoints, scale, shift));
+  }
+} /* scalepoints */
+
+
+/*-<a                             href="qh-geom_r.htm#TOC"
+  >-------------------------------</a><a name="setdelaunay">-</a>
+
+  qh_setdelaunay(qh, dim, count, points )
+    project count points to dim-d paraboloid for Delaunay triangulation
+
+    dim is one more than the dimension of the input set
+    assumes dim is at least 3 (i.e., at least a 2-d Delaunay triangulation)
+
+    points is a dim*count realT array.  The first dim-1 coordinates
+    are the coordinates of the first input point.  array[dim] is
+    the first coordinate of the second input point.  array[2*dim] is
+    the first coordinate of the third input point.
+
+    if qh.last_low defined (i.e., 'Qbb' called qh_scalelast)
+      calls qh_scalelast to scale the last coordinate the same as the other points
+
+  returns:
+    for each point
+      sets point[dim-1] to sum of squares of coordinates
+    scale points to 'Qbb' if needed
+
+  notes:
+    to project one point, use
+      qh_setdelaunay(qh, qh->hull_dim, 1, point)
+
+    Do not use options 'Qbk', 'QBk', or 'QbB' since they scale
+    the coordinates after the original projection.
+
+*/
+void qh_setdelaunay(qhT *qh, int dim, int count, pointT *points) {
+  int i, k;
+  coordT *coordp, coord;
+  realT paraboloid;
+
+  trace0((qh, qh->ferr, 11, "qh_setdelaunay: project %d points to paraboloid for Delaunay triangulation\n", count));
+  coordp= points;
+  for (i=0; i < count; i++) {
+    coord= *coordp++;
+    paraboloid= coord*coord;
+    for (k=dim-2; k--; ) {
+      coord= *coordp++;
+      paraboloid += coord*coord;
+    }
+    *coordp++ = paraboloid;
+  }
+  if (qh->last_low < REALmax/2)
+    qh_scalelast(qh, points, count, dim, qh->last_low, qh->last_high, qh->last_newhigh);
+} /* setdelaunay */
+
+
+/*-<a                             href="qh-geom_r.htm#TOC"
+  >-------------------------------</a><a name="sethalfspace">-</a>
+
+  qh_sethalfspace(qh, dim, coords, nextp, normal, offset, feasible )
+    set point to dual of halfspace relative to feasible point
+    halfspace is normal coefficients and offset.
+
+  returns:
+    false and prints error if feasible point is outside of hull
+    overwrites coordinates for point at dim coords
+    nextp= next point (coords)
+    does not call qh_errexit
+
+  design:
+    compute distance from feasible point to halfspace
+    divide each normal coefficient by -dist
+*/
+boolT qh_sethalfspace(qhT *qh, int dim, coordT *coords, coordT **nextp,
+         coordT *normal, coordT *offset, coordT *feasible) {
+  coordT *normp= normal, *feasiblep= feasible, *coordp= coords;
+  realT dist;
+  realT r; /*bug fix*/
+  int k;
+  boolT zerodiv;
+
+  dist= *offset;
+  for (k=dim; k--; )
+    dist += *(normp++) * *(feasiblep++);
+  if (dist > 0)
+    goto LABELerroroutside;
+  normp= normal;
+  if (dist < -qh->MINdenom) {
+    for (k=dim; k--; )
+      *(coordp++)= *(normp++) / -dist;
+  }else {
+    for (k=dim; k--; ) {
+      *(coordp++)= qh_divzero(*(normp++), -dist, qh->MINdenom_1, &zerodiv);
+      if (zerodiv)
+        goto LABELerroroutside;
+    }
+  }
+  *nextp= coordp;
+  if (qh->IStracing >= 4) {
+    qh_fprintf(qh, qh->ferr, 8021, "qh_sethalfspace: halfspace at offset %6.2g to point: ", *offset);
+    for (k=dim, coordp=coords; k--; ) {
+      r= *coordp++;
+      qh_fprintf(qh, qh->ferr, 8022, " %6.2g", r);
+    }
+    qh_fprintf(qh, qh->ferr, 8023, "\n");
+  }
+  return True;
+LABELerroroutside:
+  feasiblep= feasible;
+  normp= normal;
+  qh_fprintf(qh, qh->ferr, 6023, "qhull input error: feasible point is not clearly inside halfspace\nfeasible point: ");
+  for (k=dim; k--; )
+    qh_fprintf(qh, qh->ferr, 8024, qh_REAL_1, r=*(feasiblep++));
+  qh_fprintf(qh, qh->ferr, 8025, "\n     halfspace: ");
+  for (k=dim; k--; )
+    qh_fprintf(qh, qh->ferr, 8026, qh_REAL_1, r=*(normp++));
+  qh_fprintf(qh, qh->ferr, 8027, "\n     at offset: ");
+  qh_fprintf(qh, qh->ferr, 8028, qh_REAL_1, *offset);
+  qh_fprintf(qh, qh->ferr, 8029, " and distance: ");
+  qh_fprintf(qh, qh->ferr, 8030, qh_REAL_1, dist);
+  qh_fprintf(qh, qh->ferr, 8031, "\n");
+  return False;
+} /* sethalfspace */
+
+/*-<a                             href="qh-geom_r.htm#TOC"
+  >-------------------------------</a><a name="sethalfspace_all">-</a>
+
+  qh_sethalfspace_all(qh, dim, count, halfspaces, feasible )
+    generate dual for halfspace intersection with feasible point
+    array of count halfspaces
+      each halfspace is normal coefficients followed by offset
+      the origin is inside the halfspace if the offset is negative
+    feasible is a point inside all halfspaces (http://www.qhull.org/html/qhalf.htm#notes)
+
+  returns:
+    malloc'd array of count X dim-1 points
+
+  notes:
+    call before qh_init_B or qh_initqhull_globals
+    free memory when done
+    unused/untested code: please email bradb@shore.net if this works ok for you
+    if using option 'Fp', qh->feasible_point must be set (e.g., to 'feasible')
+    qh->feasible_point is a malloc'd array that is freed by qh_freebuffers.
+
+  design:
+    see qh_sethalfspace
+*/
+coordT *qh_sethalfspace_all(qhT *qh, int dim, int count, coordT *halfspaces, pointT *feasible) {
+  int i, newdim;
+  pointT *newpoints;
+  coordT *coordp, *normalp, *offsetp;
+
+  trace0((qh, qh->ferr, 12, "qh_sethalfspace_all: compute dual for halfspace intersection\n"));
+  newdim= dim - 1;
+  if (!(newpoints=(coordT*)qh_malloc(count*newdim*sizeof(coordT)))){
+    qh_fprintf(qh, qh->ferr, 6024, "qhull error: insufficient memory to compute dual of %d halfspaces\n",
+          count);
+    qh_errexit(qh, qh_ERRmem, NULL, NULL);
+  }
+  coordp= newpoints;
+  normalp= halfspaces;
+  for (i=0; i < count; i++) {
+    offsetp= normalp + newdim;
+    if (!qh_sethalfspace(qh, newdim, coordp, &coordp, normalp, offsetp, feasible)) {
+      qh_free(newpoints);  /* feasible is not inside halfspace as reported by qh_sethalfspace */
+      qh_fprintf(qh, qh->ferr, 8032, "The halfspace was at index %d\n", i);
+      qh_errexit(qh, qh_ERRinput, NULL, NULL);
+    }
+    normalp= offsetp + 1;
+  }
+  return newpoints;
+} /* sethalfspace_all */
+
+
+/*-<a                             href="qh-geom_r.htm#TOC"
+  >-------------------------------</a><a name="sharpnewfacets">-</a>
+
+  qh_sharpnewfacets(qh)
+
+  returns:
+    true if could be an acute angle (facets in different quadrants)
+
+  notes:
+    for qh_findbest
+
+  design:
+    for all facets on qh.newfacet_list
+      if two facets are in different quadrants
+        set issharp
+*/
+boolT qh_sharpnewfacets(qhT *qh) {
+  facetT *facet;
+  boolT issharp = False;
+  int *quadrant, k;
+
+  quadrant= (int*)qh_memalloc(qh, qh->hull_dim * sizeof(int));
+  FORALLfacet_(qh->newfacet_list) {
+    if (facet == qh->newfacet_list) {
+      for (k=qh->hull_dim; k--; )
+        quadrant[ k]= (facet->normal[ k] > 0);
+    }else {
+      for (k=qh->hull_dim; k--; ) {
+        if (quadrant[ k] != (facet->normal[ k] > 0)) {
+          issharp= True;
+          break;
+        }
+      }
+    }
+    if (issharp)
+      break;
+  }
+  qh_memfree(qh, quadrant, qh->hull_dim * sizeof(int));
+  trace3((qh, qh->ferr, 3001, "qh_sharpnewfacets: %d\n", issharp));
+  return issharp;
+} /* sharpnewfacets */
+
+/*-<a                             href="qh-geom_r.htm#TOC"
+  >-------------------------------</a><a name="voronoi_center">-</a>
+
+  qh_voronoi_center(qh, dim, points )
+    return Voronoi center for a set of points
+    dim is the orginal dimension of the points
+    gh.gm_matrix/qh.gm_row are scratch buffers
+
+  returns:
+    center as a temporary point (qh_memalloc)
+    if non-simplicial,
+      returns center for max simplex of points
+
+  notes:
+    only called by qh_facetcenter
+    from Bowyer & Woodwark, A Programmer's Geometry, 1983, p. 65
+
+  design:
+    if non-simplicial
+      determine max simplex for points
+    translate point0 of simplex to origin
+    compute sum of squares of diagonal
+    compute determinate
+    compute Voronoi center (see Bowyer & Woodwark)
+*/
+pointT *qh_voronoi_center(qhT *qh, int dim, setT *points) {
+  pointT *point, **pointp, *point0;
+  pointT *center= (pointT*)qh_memalloc(qh, qh->center_size);
+  setT *simplex;
+  int i, j, k, size= qh_setsize(qh, points);
+  coordT *gmcoord;
+  realT *diffp, sum2, *sum2row, *sum2p, det, factor;
+  boolT nearzero, infinite;
+
+  if (size == dim+1)
+    simplex= points;
+  else if (size < dim+1) {
+    qh_memfree(qh, center, qh->center_size);
+    qh_fprintf(qh, qh->ferr, 6025, "qhull internal error (qh_voronoi_center):\n  need at least %d points to construct a Voronoi center\n",
+             dim+1);
+    qh_errexit(qh, qh_ERRqhull, NULL, NULL);
+    simplex= points;  /* never executed -- avoids warning */
+  }else {
+    simplex= qh_settemp(qh, dim+1);
+    qh_maxsimplex(qh, dim, points, NULL, 0, &simplex);
+  }
+  point0= SETfirstt_(simplex, pointT);
+  gmcoord= qh->gm_matrix;
+  for (k=0; k < dim; k++) {
+    qh->gm_row[k]= gmcoord;
+    FOREACHpoint_(simplex) {
+      if (point != point0)
+        *(gmcoord++)= point[k] - point0[k];
+    }
+  }
+  sum2row= gmcoord;
+  for (i=0; i < dim; i++) {
+    sum2= 0.0;
+    for (k=0; k < dim; k++) {
+      diffp= qh->gm_row[k] + i;
+      sum2 += *diffp * *diffp;
+    }
+    *(gmcoord++)= sum2;
+  }
+  det= qh_determinant(qh, qh->gm_row, dim, &nearzero);
+  factor= qh_divzero(0.5, det, qh->MINdenom, &infinite);
+  if (infinite) {
+    for (k=dim; k--; )
+      center[k]= qh_INFINITE;
+    if (qh->IStracing)
+      qh_printpoints(qh, qh->ferr, "qh_voronoi_center: at infinity for ", simplex);
+  }else {
+    for (i=0; i < dim; i++) {
+      gmcoord= qh->gm_matrix;
+      sum2p= sum2row;
+      for (k=0; k < dim; k++) {
+        qh->gm_row[k]= gmcoord;
+        if (k == i) {
+          for (j=dim; j--; )
+            *(gmcoord++)= *sum2p++;
+        }else {
+          FOREACHpoint_(simplex) {
+            if (point != point0)
+              *(gmcoord++)= point[k] - point0[k];
+          }
+        }
+      }
+      center[i]= qh_determinant(qh, qh->gm_row, dim, &nearzero)*factor + point0[i];
+    }
+#ifndef qh_NOtrace
+    if (qh->IStracing >= 3) {
+      qh_fprintf(qh, qh->ferr, 8033, "qh_voronoi_center: det %2.2g factor %2.2g ", det, factor);
+      qh_printmatrix(qh, qh->ferr, "center:", &center, 1, dim);
+      if (qh->IStracing >= 5) {
+        qh_printpoints(qh, qh->ferr, "points", simplex);
+        FOREACHpoint_(simplex)
+          qh_fprintf(qh, qh->ferr, 8034, "p%d dist %.2g, ", qh_pointid(qh, point),
+                   qh_pointdist(point, center, dim));
+        qh_fprintf(qh, qh->ferr, 8035, "\n");
+      }
+    }
+#endif
+  }
+  if (simplex != points)
+    qh_settempfree(qh, &simplex);
+  return center;
+} /* voronoi_center */
+
diff --git a/C/geom_r.c b/C/geom_r.c
new file mode 100644
--- /dev/null
+++ b/C/geom_r.c
@@ -0,0 +1,1234 @@
+/*<html><pre>  -<a                             href="qh-geom_r.htm"
+  >-------------------------------</a><a name="TOP">-</a>
+
+   geom_r.c
+   geometric routines of qhull
+
+   see qh-geom_r.htm and geom_r.h
+
+   Copyright (c) 1993-2015 The Geometry Center.
+   $Id: //main/2015/qhull/src/libqhull_r/geom_r.c#2 $$Change: 1995 $
+   $DateTime: 2015/10/13 21:59:42 $$Author: bbarber $
+
+   infrequent code goes into geom2_r.c
+*/
+
+#include "qhull_ra.h"
+
+/*-<a                             href="qh-geom_r.htm#TOC"
+  >-------------------------------</a><a name="distplane">-</a>
+
+  qh_distplane(qh, point, facet, dist )
+    return distance from point to facet
+
+  returns:
+    dist
+    if qh.RANDOMdist, joggles result
+
+  notes:
+    dist > 0 if point is above facet (i.e., outside)
+    does not error (for qh_sortfacets, qh_outerinner)
+
+  see:
+    qh_distnorm in geom2_r.c
+    qh_distplane [geom_r.c], QhullFacet::distance, and QhullHyperplane::distance are copies
+*/
+void qh_distplane(qhT *qh, pointT *point, facetT *facet, realT *dist) {
+  coordT *normal= facet->normal, *coordp, randr;
+  int k;
+
+  switch (qh->hull_dim){
+  case 2:
+    *dist= facet->offset + point[0] * normal[0] + point[1] * normal[1];
+    break;
+  case 3:
+    *dist= facet->offset + point[0] * normal[0] + point[1] * normal[1] + point[2] * normal[2];
+    break;
+  case 4:
+    *dist= facet->offset+point[0]*normal[0]+point[1]*normal[1]+point[2]*normal[2]+point[3]*normal[3];
+    break;
+  case 5:
+    *dist= facet->offset+point[0]*normal[0]+point[1]*normal[1]+point[2]*normal[2]+point[3]*normal[3]+point[4]*normal[4];
+    break;
+  case 6:
+    *dist= facet->offset+point[0]*normal[0]+point[1]*normal[1]+point[2]*normal[2]+point[3]*normal[3]+point[4]*normal[4]+point[5]*normal[5];
+    break;
+  case 7:
+    *dist= facet->offset+point[0]*normal[0]+point[1]*normal[1]+point[2]*normal[2]+point[3]*normal[3]+point[4]*normal[4]+point[5]*normal[5]+point[6]*normal[6];
+    break;
+  case 8:
+    *dist= facet->offset+point[0]*normal[0]+point[1]*normal[1]+point[2]*normal[2]+point[3]*normal[3]+point[4]*normal[4]+point[5]*normal[5]+point[6]*normal[6]+point[7]*normal[7];
+    break;
+  default:
+    *dist= facet->offset;
+    coordp= point;
+    for (k=qh->hull_dim; k--; )
+      *dist += *coordp++ * *normal++;
+    break;
+  }
+  zinc_(Zdistplane);
+  if (!qh->RANDOMdist && qh->IStracing < 4)
+    return;
+  if (qh->RANDOMdist) {
+    randr= qh_RANDOMint;
+    *dist += (2.0 * randr / qh_RANDOMmax - 1.0) *
+      qh->RANDOMfactor * qh->MAXabs_coord;
+  }
+  if (qh->IStracing >= 4) {
+    qh_fprintf(qh, qh->ferr, 8001, "qh_distplane: ");
+    qh_fprintf(qh, qh->ferr, 8002, qh_REAL_1, *dist);
+    qh_fprintf(qh, qh->ferr, 8003, "from p%d to f%d\n", qh_pointid(qh, point), facet->id);
+  }
+  return;
+} /* distplane */
+
+
+/*-<a                             href="qh-geom_r.htm#TOC"
+  >-------------------------------</a><a name="findbest">-</a>
+
+  qh_findbest(qh, point, startfacet, bestoutside, qh_ISnewfacets, qh_NOupper, dist, isoutside, numpart )
+    find facet that is furthest below a point
+    for upperDelaunay facets
+      returns facet only if !qh_NOupper and clearly above
+
+  input:
+    starts search at 'startfacet' (can not be flipped)
+    if !bestoutside(qh_ALL), stops at qh.MINoutside
+
+  returns:
+    best facet (reports error if NULL)
+    early out if isoutside defined and bestdist > qh.MINoutside
+    dist is distance to facet
+    isoutside is true if point is outside of facet
+    numpart counts the number of distance tests
+
+  see also:
+    qh_findbestnew()
+
+  notes:
+    If merging (testhorizon), searches horizon facets of coplanar best facets because
+    after qh_distplane, this and qh_partitionpoint are the most expensive in 3-d
+      avoid calls to distplane, function calls, and real number operations.
+    caller traces result
+    Optimized for outside points.   Tried recording a search set for qh_findhorizon.
+    Made code more complicated.
+
+  when called by qh_partitionvisible():
+    indicated by qh_ISnewfacets
+    qh.newfacet_list is list of simplicial, new facets
+    qh_findbestnew set if qh_sharpnewfacets returns True (to use qh_findbestnew)
+    qh.bestfacet_notsharp set if qh_sharpnewfacets returns False
+
+  when called by qh_findfacet(), qh_partitionpoint(), qh_partitioncoplanar(),
+                 qh_check_bestdist(), qh_addpoint()
+    indicated by !qh_ISnewfacets
+    returns best facet in neighborhood of given facet
+      this is best facet overall if dist > -   qh.MAXcoplanar
+        or hull has at least a "spherical" curvature
+
+  design:
+    initialize and test for early exit
+    repeat while there are better facets
+      for each neighbor of facet
+        exit if outside facet found
+        test for better facet
+    if point is inside and partitioning
+      test for new facets with a "sharp" intersection
+      if so, future calls go to qh_findbestnew()
+    test horizon facets
+*/
+facetT *qh_findbest(qhT *qh, pointT *point, facetT *startfacet,
+                     boolT bestoutside, boolT isnewfacets, boolT noupper,
+                     realT *dist, boolT *isoutside, int *numpart) {
+  realT bestdist= -REALmax/2 /* avoid underflow */;
+  facetT *facet, *neighbor, **neighborp;
+  facetT *bestfacet= NULL, *lastfacet= NULL;
+  int oldtrace= qh->IStracing;
+  unsigned int visitid= ++qh->visit_id;
+  int numpartnew=0;
+  boolT testhorizon = True; /* needed if precise, e.g., rbox c D6 | qhull Q0 Tv */
+
+  zinc_(Zfindbest);
+  if (qh->IStracing >= 3 || (qh->TRACElevel && qh->TRACEpoint >= 0 && qh->TRACEpoint == qh_pointid(qh, point))) {
+    if (qh->TRACElevel > qh->IStracing)
+      qh->IStracing= qh->TRACElevel;
+    qh_fprintf(qh, qh->ferr, 8004, "qh_findbest: point p%d starting at f%d isnewfacets? %d, unless %d exit if > %2.2g\n",
+             qh_pointid(qh, point), startfacet->id, isnewfacets, bestoutside, qh->MINoutside);
+    qh_fprintf(qh, qh->ferr, 8005, "  testhorizon? %d noupper? %d", testhorizon, noupper);
+    qh_fprintf(qh, qh->ferr, 8006, "  Last point added was p%d.", qh->furthest_id);
+    qh_fprintf(qh, qh->ferr, 8007, "  Last merge was #%d.  max_outside %2.2g\n", zzval_(Ztotmerge), qh->max_outside);
+  }
+  if (isoutside)
+    *isoutside= True;
+  if (!startfacet->flipped) {  /* test startfacet */
+    *numpart= 1;
+    qh_distplane(qh, point, startfacet, dist);  /* this code is duplicated below */
+    if (!bestoutside && *dist >= qh->MINoutside
+    && (!startfacet->upperdelaunay || !noupper)) {
+      bestfacet= startfacet;
+      goto LABELreturn_best;
+    }
+    bestdist= *dist;
+    if (!startfacet->upperdelaunay) {
+      bestfacet= startfacet;
+    }
+  }else
+    *numpart= 0;
+  startfacet->visitid= visitid;
+  facet= startfacet;
+  while (facet) {
+    trace4((qh, qh->ferr, 4001, "qh_findbest: neighbors of f%d, bestdist %2.2g f%d\n",
+                facet->id, bestdist, getid_(bestfacet)));
+    lastfacet= facet;
+    FOREACHneighbor_(facet) {
+      if (!neighbor->newfacet && isnewfacets)
+        continue;
+      if (neighbor->visitid == visitid)
+        continue;
+      neighbor->visitid= visitid;
+      if (!neighbor->flipped) {  /* code duplicated above */
+        (*numpart)++;
+        qh_distplane(qh, point, neighbor, dist);
+        if (*dist > bestdist) {
+          if (!bestoutside && *dist >= qh->MINoutside
+          && (!neighbor->upperdelaunay || !noupper)) {
+            bestfacet= neighbor;
+            goto LABELreturn_best;
+          }
+          if (!neighbor->upperdelaunay) {
+            bestfacet= neighbor;
+            bestdist= *dist;
+            break; /* switch to neighbor */
+          }else if (!bestfacet) {
+            bestdist= *dist;
+            break; /* switch to neighbor */
+          }
+        } /* end of *dist>bestdist */
+      } /* end of !flipped */
+    } /* end of FOREACHneighbor */
+    facet= neighbor;  /* non-NULL only if *dist>bestdist */
+  } /* end of while facet (directed search) */
+  if (isnewfacets) {
+    if (!bestfacet) {
+      bestdist= -REALmax/2;
+      bestfacet= qh_findbestnew(qh, point, startfacet->next, &bestdist, bestoutside, isoutside, &numpartnew);
+      testhorizon= False; /* qh_findbestnew calls qh_findbesthorizon */
+    }else if (!qh->findbest_notsharp && bestdist < - qh->DISTround) {
+      if (qh_sharpnewfacets(qh)) {
+        /* seldom used, qh_findbestnew will retest all facets */
+        zinc_(Zfindnewsharp);
+        bestfacet= qh_findbestnew(qh, point, bestfacet, &bestdist, bestoutside, isoutside, &numpartnew);
+        testhorizon= False; /* qh_findbestnew calls qh_findbesthorizon */
+        qh->findbestnew= True;
+      }else
+        qh->findbest_notsharp= True;
+    }
+  }
+  if (!bestfacet)
+    bestfacet= qh_findbestlower(qh, lastfacet, point, &bestdist, numpart);
+  if (testhorizon)
+    bestfacet= qh_findbesthorizon(qh, !qh_IScheckmax, point, bestfacet, noupper, &bestdist, &numpartnew);
+  *dist= bestdist;
+  if (isoutside && bestdist < qh->MINoutside)
+    *isoutside= False;
+LABELreturn_best:
+  zadd_(Zfindbesttot, *numpart);
+  zmax_(Zfindbestmax, *numpart);
+  (*numpart) += numpartnew;
+  qh->IStracing= oldtrace;
+  return bestfacet;
+}  /* findbest */
+
+
+/*-<a                             href="qh-geom_r.htm#TOC"
+  >-------------------------------</a><a name="findbesthorizon">-</a>
+
+  qh_findbesthorizon(qh, qh_IScheckmax, point, startfacet, qh_NOupper, &bestdist, &numpart )
+    search coplanar and better horizon facets from startfacet/bestdist
+    ischeckmax turns off statistics and minsearch update
+    all arguments must be initialized
+  returns(ischeckmax):
+    best facet
+  returns(!ischeckmax):
+    best facet that is not upperdelaunay
+    allows upperdelaunay that is clearly outside
+  returns:
+    bestdist is distance to bestfacet
+    numpart -- updates number of distance tests
+
+  notes:
+    no early out -- use qh_findbest() or qh_findbestnew()
+    Searches coplanar or better horizon facets
+
+  when called by qh_check_maxout() (qh_IScheckmax)
+    startfacet must be closest to the point
+      Otherwise, if point is beyond and below startfacet, startfacet may be a local minimum
+      even though other facets are below the point.
+    updates facet->maxoutside for good, visited facets
+    may return NULL
+
+    searchdist is qh.max_outside + 2 * DISTround
+      + max( MINvisible('Vn'), MAXcoplanar('Un'));
+    This setting is a guess.  It must be at least max_outside + 2*DISTround
+    because a facet may have a geometric neighbor across a vertex
+
+  design:
+    for each horizon facet of coplanar best facets
+      continue if clearly inside
+      unless upperdelaunay or clearly outside
+         update best facet
+*/
+facetT *qh_findbesthorizon(qhT *qh, boolT ischeckmax, pointT* point, facetT *startfacet, boolT noupper, realT *bestdist, int *numpart) {
+  facetT *bestfacet= startfacet;
+  realT dist;
+  facetT *neighbor, **neighborp, *facet;
+  facetT *nextfacet= NULL; /* optimize last facet of coplanarfacetset */
+  int numpartinit= *numpart, coplanarfacetset_size;
+  unsigned int visitid= ++qh->visit_id;
+  boolT newbest= False; /* for tracing */
+  realT minsearch, searchdist;  /* skip facets that are too far from point */
+
+  if (!ischeckmax) {
+    zinc_(Zfindhorizon);
+  }else {
+#if qh_MAXoutside
+    if ((!qh->ONLYgood || startfacet->good) && *bestdist > startfacet->maxoutside)
+      startfacet->maxoutside= *bestdist;
+#endif
+  }
+  searchdist= qh_SEARCHdist; /* multiple of qh.max_outside and precision constants */
+  minsearch= *bestdist - searchdist;
+  if (ischeckmax) {
+    /* Always check coplanar facets.  Needed for RBOX 1000 s Z1 G1e-13 t996564279 | QHULL Tv */
+    minimize_(minsearch, -searchdist);
+  }
+  coplanarfacetset_size= 0;
+  facet= startfacet;
+  while (True) {
+    trace4((qh, qh->ferr, 4002, "qh_findbesthorizon: neighbors of f%d bestdist %2.2g f%d ischeckmax? %d noupper? %d minsearch %2.2g searchdist %2.2g\n",
+                facet->id, *bestdist, getid_(bestfacet), ischeckmax, noupper,
+                minsearch, searchdist));
+    FOREACHneighbor_(facet) {
+      if (neighbor->visitid == visitid)
+        continue;
+      neighbor->visitid= visitid;
+      if (!neighbor->flipped) {
+        qh_distplane(qh, point, neighbor, &dist);
+        (*numpart)++;
+        if (dist > *bestdist) {
+          if (!neighbor->upperdelaunay || ischeckmax || (!noupper && dist >= qh->MINoutside)) {
+            bestfacet= neighbor;
+            *bestdist= dist;
+            newbest= True;
+            if (!ischeckmax) {
+              minsearch= dist - searchdist;
+              if (dist > *bestdist + searchdist) {
+                zinc_(Zfindjump);  /* everything in qh.coplanarfacetset at least searchdist below */
+                coplanarfacetset_size= 0;
+              }
+            }
+          }
+        }else if (dist < minsearch)
+          continue;  /* if ischeckmax, dist can't be positive */
+#if qh_MAXoutside
+        if (ischeckmax && dist > neighbor->maxoutside)
+          neighbor->maxoutside= dist;
+#endif
+      } /* end of !flipped */
+      if (nextfacet) {
+        if (!coplanarfacetset_size++) {
+          SETfirst_(qh->coplanarfacetset)= nextfacet;
+          SETtruncate_(qh->coplanarfacetset, 1);
+        }else
+          qh_setappend(qh, &qh->coplanarfacetset, nextfacet); /* Was needed for RBOX 1000 s W1e-13 P0 t996547055 | QHULL d Qbb Qc Tv
+                                                 and RBOX 1000 s Z1 G1e-13 t996564279 | qhull Tv  */
+      }
+      nextfacet= neighbor;
+    } /* end of EACHneighbor */
+    facet= nextfacet;
+    if (facet)
+      nextfacet= NULL;
+    else if (!coplanarfacetset_size)
+      break;
+    else if (!--coplanarfacetset_size) {
+      facet= SETfirstt_(qh->coplanarfacetset, facetT);
+      SETtruncate_(qh->coplanarfacetset, 0);
+    }else
+      facet= (facetT*)qh_setdellast(qh->coplanarfacetset);
+  } /* while True, for each facet in qh.coplanarfacetset */
+  if (!ischeckmax) {
+    zadd_(Zfindhorizontot, *numpart - numpartinit);
+    zmax_(Zfindhorizonmax, *numpart - numpartinit);
+    if (newbest)
+      zinc_(Zparthorizon);
+  }
+  trace4((qh, qh->ferr, 4003, "qh_findbesthorizon: newbest? %d bestfacet f%d bestdist %2.2g\n", newbest, getid_(bestfacet), *bestdist));
+  return bestfacet;
+}  /* findbesthorizon */
+
+/*-<a                             href="qh-geom_r.htm#TOC"
+  >-------------------------------</a><a name="findbestnew">-</a>
+
+  qh_findbestnew(qh, point, startfacet, dist, isoutside, numpart )
+    find best newfacet for point
+    searches all of qh.newfacet_list starting at startfacet
+    searches horizon facets of coplanar best newfacets
+    searches all facets if startfacet == qh.facet_list
+  returns:
+    best new or horizon facet that is not upperdelaunay
+    early out if isoutside and not 'Qf'
+    dist is distance to facet
+    isoutside is true if point is outside of facet
+    numpart is number of distance tests
+
+  notes:
+    Always used for merged new facets (see qh_USEfindbestnew)
+    Avoids upperdelaunay facet unless (isoutside and outside)
+
+    Uses qh.visit_id, qh.coplanarfacetset.
+    If share visit_id with qh_findbest, coplanarfacetset is incorrect.
+
+    If merging (testhorizon), searches horizon facets of coplanar best facets because
+    a point maybe coplanar to the bestfacet, below its horizon facet,
+    and above a horizon facet of a coplanar newfacet.  For example,
+      rbox 1000 s Z1 G1e-13 | qhull
+      rbox 1000 s W1e-13 P0 t992110337 | QHULL d Qbb Qc
+
+    qh_findbestnew() used if
+       qh_sharpnewfacets -- newfacets contains a sharp angle
+       if many merges, qh_premerge found a merge, or 'Qf' (qh.findbestnew)
+
+  see also:
+    qh_partitionall() and qh_findbest()
+
+  design:
+    for each new facet starting from startfacet
+      test distance from point to facet
+      return facet if clearly outside
+      unless upperdelaunay and a lowerdelaunay exists
+         update best facet
+    test horizon facets
+*/
+facetT *qh_findbestnew(qhT *qh, pointT *point, facetT *startfacet,
+           realT *dist, boolT bestoutside, boolT *isoutside, int *numpart) {
+  realT bestdist= -REALmax/2;
+  facetT *bestfacet= NULL, *facet;
+  int oldtrace= qh->IStracing, i;
+  unsigned int visitid= ++qh->visit_id;
+  realT distoutside= 0.0;
+  boolT isdistoutside; /* True if distoutside is defined */
+  boolT testhorizon = True; /* needed if precise, e.g., rbox c D6 | qhull Q0 Tv */
+
+  if (!startfacet) {
+    if (qh->MERGING)
+      qh_fprintf(qh, qh->ferr, 6001, "qhull precision error (qh_findbestnew): merging has formed and deleted a cone of new facets.  Can not continue.\n");
+    else
+      qh_fprintf(qh, qh->ferr, 6002, "qhull internal error (qh_findbestnew): no new facets for point p%d\n",
+              qh->furthest_id);
+    qh_errexit(qh, qh_ERRqhull, NULL, NULL);
+  }
+  zinc_(Zfindnew);
+  if (qh->BESToutside || bestoutside)
+    isdistoutside= False;
+  else {
+    isdistoutside= True;
+    distoutside= qh_DISToutside; /* multiple of qh.MINoutside & qh.max_outside, see user.h */
+  }
+  if (isoutside)
+    *isoutside= True;
+  *numpart= 0;
+  if (qh->IStracing >= 3 || (qh->TRACElevel && qh->TRACEpoint >= 0 && qh->TRACEpoint == qh_pointid(qh, point))) {
+    if (qh->TRACElevel > qh->IStracing)
+      qh->IStracing= qh->TRACElevel;
+    qh_fprintf(qh, qh->ferr, 8008, "qh_findbestnew: point p%d facet f%d. Stop? %d if dist > %2.2g\n",
+             qh_pointid(qh, point), startfacet->id, isdistoutside, distoutside);
+    qh_fprintf(qh, qh->ferr, 8009, "  Last point added p%d visitid %d.",  qh->furthest_id, visitid);
+    qh_fprintf(qh, qh->ferr, 8010, "  Last merge was #%d.\n", zzval_(Ztotmerge));
+  }
+  /* visit all new facets starting with startfacet, maybe qh->facet_list */
+  for (i=0, facet=startfacet; i < 2; i++, facet= qh->newfacet_list) {
+    FORALLfacet_(facet) {
+      if (facet == startfacet && i)
+        break;
+      facet->visitid= visitid;
+      if (!facet->flipped) {
+        qh_distplane(qh, point, facet, dist);
+        (*numpart)++;
+        if (*dist > bestdist) {
+          if (!facet->upperdelaunay || *dist >= qh->MINoutside) {
+            bestfacet= facet;
+            if (isdistoutside && *dist >= distoutside)
+              goto LABELreturn_bestnew;
+            bestdist= *dist;
+          }
+        }
+      } /* end of !flipped */
+    } /* FORALLfacet from startfacet or qh->newfacet_list */
+  }
+  if (testhorizon || !bestfacet) /* testhorizon is always True.  Keep the same code as qh_findbest */
+    bestfacet= qh_findbesthorizon(qh, !qh_IScheckmax, point, bestfacet ? bestfacet : startfacet,
+                                        !qh_NOupper, &bestdist, numpart);
+  *dist= bestdist;
+  if (isoutside && *dist < qh->MINoutside)
+    *isoutside= False;
+LABELreturn_bestnew:
+  zadd_(Zfindnewtot, *numpart);
+  zmax_(Zfindnewmax, *numpart);
+  trace4((qh, qh->ferr, 4004, "qh_findbestnew: bestfacet f%d bestdist %2.2g\n", getid_(bestfacet), *dist));
+  qh->IStracing= oldtrace;
+  return bestfacet;
+}  /* findbestnew */
+
+/* ============ hyperplane functions -- keep code together [?] ============ */
+
+/*-<a                             href="qh-geom_r.htm#TOC"
+  >-------------------------------</a><a name="backnormal">-</a>
+
+  qh_backnormal(qh, rows, numrow, numcol, sign, normal, nearzero )
+    given an upper-triangular rows array and a sign,
+    solve for normal equation x using back substitution over rows U
+
+  returns:
+     normal= x
+
+     if will not be able to divzero() when normalized(qh.MINdenom_2 and qh.MINdenom_1_2),
+       if fails on last row
+         this means that the hyperplane intersects [0,..,1]
+         sets last coordinate of normal to sign
+       otherwise
+         sets tail of normal to [...,sign,0,...], i.e., solves for b= [0...0]
+         sets nearzero
+
+  notes:
+     assumes numrow == numcol-1
+
+     see Golub & van Loan, 1983, Eq. 4.4-9 for "Gaussian elimination with complete pivoting"
+
+     solves Ux=b where Ax=b and PA=LU
+     b= [0,...,0,sign or 0]  (sign is either -1 or +1)
+     last row of A= [0,...,0,1]
+
+     1) Ly=Pb == y=b since P only permutes the 0's of   b
+
+  design:
+    for each row from end
+      perform back substitution
+      if near zero
+        use qh_divzero for division
+        if zero divide and not last row
+          set tail of normal to 0
+*/
+void qh_backnormal(qhT *qh, realT **rows, int numrow, int numcol, boolT sign,
+        coordT *normal, boolT *nearzero) {
+  int i, j;
+  coordT *normalp, *normal_tail, *ai, *ak;
+  realT diagonal;
+  boolT waszero;
+  int zerocol= -1;
+
+  normalp= normal + numcol - 1;
+  *normalp--= (sign ? -1.0 : 1.0);
+  for (i=numrow; i--; ) {
+    *normalp= 0.0;
+    ai= rows[i] + i + 1;
+    ak= normalp+1;
+    for (j=i+1; j < numcol; j++)
+      *normalp -= *ai++ * *ak++;
+    diagonal= (rows[i])[i];
+    if (fabs_(diagonal) > qh->MINdenom_2)
+      *(normalp--) /= diagonal;
+    else {
+      waszero= False;
+      *normalp= qh_divzero(*normalp, diagonal, qh->MINdenom_1_2, &waszero);
+      if (waszero) {
+        zerocol= i;
+        *(normalp--)= (sign ? -1.0 : 1.0);
+        for (normal_tail= normalp+2; normal_tail < normal + numcol; normal_tail++)
+          *normal_tail= 0.0;
+      }else
+        normalp--;
+    }
+  }
+  if (zerocol != -1) {
+    zzinc_(Zback0);
+    *nearzero= True;
+    trace4((qh, qh->ferr, 4005, "qh_backnormal: zero diagonal at column %d.\n", i));
+    qh_precision(qh, "zero diagonal on back substitution");
+  }
+} /* backnormal */
+
+/*-<a                             href="qh-geom_r.htm#TOC"
+  >-------------------------------</a><a name="gausselim">-</a>
+
+  qh_gausselim(qh, rows, numrow, numcol, sign )
+    Gaussian elimination with partial pivoting
+
+  returns:
+    rows is upper triangular (includes row exchanges)
+    flips sign for each row exchange
+    sets nearzero if pivot[k] < qh.NEARzero[k], else clears it
+
+  notes:
+    if nearzero, the determinant's sign may be incorrect.
+    assumes numrow <= numcol
+
+  design:
+    for each row
+      determine pivot and exchange rows if necessary
+      test for near zero
+      perform gaussian elimination step
+*/
+void qh_gausselim(qhT *qh, realT **rows, int numrow, int numcol, boolT *sign, boolT *nearzero) {
+  realT *ai, *ak, *rowp, *pivotrow;
+  realT n, pivot, pivot_abs= 0.0, temp;
+  int i, j, k, pivoti, flip=0;
+
+  *nearzero= False;
+  for (k=0; k < numrow; k++) {
+    pivot_abs= fabs_((rows[k])[k]);
+    pivoti= k;
+    for (i=k+1; i < numrow; i++) {
+      if ((temp= fabs_((rows[i])[k])) > pivot_abs) {
+        pivot_abs= temp;
+        pivoti= i;
+      }
+    }
+    if (pivoti != k) {
+      rowp= rows[pivoti];
+      rows[pivoti]= rows[k];
+      rows[k]= rowp;
+      *sign ^= 1;
+      flip ^= 1;
+    }
+    if (pivot_abs <= qh->NEARzero[k]) {
+      *nearzero= True;
+      if (pivot_abs == 0.0) {   /* remainder of column == 0 */
+        if (qh->IStracing >= 4) {
+          qh_fprintf(qh, qh->ferr, 8011, "qh_gausselim: 0 pivot at column %d. (%2.2g < %2.2g)\n", k, pivot_abs, qh->DISTround);
+          qh_printmatrix(qh, qh->ferr, "Matrix:", rows, numrow, numcol);
+        }
+        zzinc_(Zgauss0);
+        qh_precision(qh, "zero pivot for Gaussian elimination");
+        goto LABELnextcol;
+      }
+    }
+    pivotrow= rows[k] + k;
+    pivot= *pivotrow++;  /* signed value of pivot, and remainder of row */
+    for (i=k+1; i < numrow; i++) {
+      ai= rows[i] + k;
+      ak= pivotrow;
+      n= (*ai++)/pivot;   /* divzero() not needed since |pivot| >= |*ai| */
+      for (j= numcol - (k+1); j--; )
+        *ai++ -= n * *ak++;
+    }
+  LABELnextcol:
+    ;
+  }
+  wmin_(Wmindenom, pivot_abs);  /* last pivot element */
+  if (qh->IStracing >= 5)
+    qh_printmatrix(qh, qh->ferr, "qh_gausselem: result", rows, numrow, numcol);
+} /* gausselim */
+
+
+/*-<a                             href="qh-geom_r.htm#TOC"
+  >-------------------------------</a><a name="getangle">-</a>
+
+  qh_getangle(qh, vect1, vect2 )
+    returns the dot product of two vectors
+    if qh.RANDOMdist, joggles result
+
+  notes:
+    the angle may be > 1.0 or < -1.0 because of roundoff errors
+
+*/
+realT qh_getangle(qhT *qh, pointT *vect1, pointT *vect2) {
+  realT angle= 0, randr;
+  int k;
+
+  for (k=qh->hull_dim; k--; )
+    angle += *vect1++ * *vect2++;
+  if (qh->RANDOMdist) {
+    randr= qh_RANDOMint;
+    angle += (2.0 * randr / qh_RANDOMmax - 1.0) *
+      qh->RANDOMfactor;
+  }
+  trace4((qh, qh->ferr, 4006, "qh_getangle: %2.2g\n", angle));
+  return(angle);
+} /* getangle */
+
+
+/*-<a                             href="qh-geom_r.htm#TOC"
+  >-------------------------------</a><a name="getcenter">-</a>
+
+  qh_getcenter(qh, vertices )
+    returns arithmetic center of a set of vertices as a new point
+
+  notes:
+    allocates point array for center
+*/
+pointT *qh_getcenter(qhT *qh, setT *vertices) {
+  int k;
+  pointT *center, *coord;
+  vertexT *vertex, **vertexp;
+  int count= qh_setsize(qh, vertices);
+
+  if (count < 2) {
+    qh_fprintf(qh, qh->ferr, 6003, "qhull internal error (qh_getcenter): not defined for %d points\n", count);
+    qh_errexit(qh, qh_ERRqhull, NULL, NULL);
+  }
+  center= (pointT *)qh_memalloc(qh, qh->normal_size);
+  for (k=0; k < qh->hull_dim; k++) {
+    coord= center+k;
+    *coord= 0.0;
+    FOREACHvertex_(vertices)
+      *coord += vertex->point[k];
+    *coord /= count;  /* count>=2 by QH6003 */
+  }
+  return(center);
+} /* getcenter */
+
+
+/*-<a                             href="qh-geom_r.htm#TOC"
+  >-------------------------------</a><a name="getcentrum">-</a>
+
+  qh_getcentrum(qh, facet )
+    returns the centrum for a facet as a new point
+
+  notes:
+    allocates the centrum
+*/
+pointT *qh_getcentrum(qhT *qh, facetT *facet) {
+  realT dist;
+  pointT *centrum, *point;
+
+  point= qh_getcenter(qh, facet->vertices);
+  zzinc_(Zcentrumtests);
+  qh_distplane(qh, point, facet, &dist);
+  centrum= qh_projectpoint(qh, point, facet, dist);
+  qh_memfree(qh, point, qh->normal_size);
+  trace4((qh, qh->ferr, 4007, "qh_getcentrum: for f%d, %d vertices dist= %2.2g\n",
+          facet->id, qh_setsize(qh, facet->vertices), dist));
+  return centrum;
+} /* getcentrum */
+
+
+/*-<a                             href="qh-geom_r.htm#TOC"
+  >-------------------------------</a><a name="getdistance">-</a>
+
+  qh_getdistance(qh, facet, neighbor, mindist, maxdist )
+    returns the maxdist and mindist distance of any vertex from neighbor
+
+  returns:
+    the max absolute value
+
+  design:
+    for each vertex of facet that is not in neighbor
+      test the distance from vertex to neighbor
+*/
+realT qh_getdistance(qhT *qh, facetT *facet, facetT *neighbor, realT *mindist, realT *maxdist) {
+  vertexT *vertex, **vertexp;
+  realT dist, maxd, mind;
+
+  FOREACHvertex_(facet->vertices)
+    vertex->seen= False;
+  FOREACHvertex_(neighbor->vertices)
+    vertex->seen= True;
+  mind= 0.0;
+  maxd= 0.0;
+  FOREACHvertex_(facet->vertices) {
+    if (!vertex->seen) {
+      zzinc_(Zbestdist);
+      qh_distplane(qh, vertex->point, neighbor, &dist);
+      if (dist < mind)
+        mind= dist;
+      else if (dist > maxd)
+        maxd= dist;
+    }
+  }
+  *mindist= mind;
+  *maxdist= maxd;
+  mind= -mind;
+  if (maxd > mind)
+    return maxd;
+  else
+    return mind;
+} /* getdistance */
+
+
+/*-<a                             href="qh-geom_r.htm#TOC"
+  >-------------------------------</a><a name="normalize">-</a>
+
+  qh_normalize(qh, normal, dim, toporient )
+    normalize a vector and report if too small
+    does not use min norm
+
+  see:
+    qh_normalize2
+*/
+void qh_normalize(qhT *qh, coordT *normal, int dim, boolT toporient) {
+  qh_normalize2(qh, normal, dim, toporient, NULL, NULL);
+} /* normalize */
+
+/*-<a                             href="qh-geom_r.htm#TOC"
+  >-------------------------------</a><a name="normalize2">-</a>
+
+  qh_normalize2(qh, normal, dim, toporient, minnorm, ismin )
+    normalize a vector and report if too small
+    qh.MINdenom/MINdenom1 are the upper limits for divide overflow
+
+  returns:
+    normalized vector
+    flips sign if !toporient
+    if minnorm non-NULL,
+      sets ismin if normal < minnorm
+
+  notes:
+    if zero norm
+       sets all elements to sqrt(1.0/dim)
+    if divide by zero (divzero())
+       sets largest element to   +/-1
+       bumps Znearlysingular
+
+  design:
+    computes norm
+    test for minnorm
+    if not near zero
+      normalizes normal
+    else if zero norm
+      sets normal to standard value
+    else
+      uses qh_divzero to normalize
+      if nearzero
+        sets norm to direction of maximum value
+*/
+void qh_normalize2(qhT *qh, coordT *normal, int dim, boolT toporient,
+            realT *minnorm, boolT *ismin) {
+  int k;
+  realT *colp, *maxp, norm= 0, temp, *norm1, *norm2, *norm3;
+  boolT zerodiv;
+
+  norm1= normal+1;
+  norm2= normal+2;
+  norm3= normal+3;
+  if (dim == 2)
+    norm= sqrt((*normal)*(*normal) + (*norm1)*(*norm1));
+  else if (dim == 3)
+    norm= sqrt((*normal)*(*normal) + (*norm1)*(*norm1) + (*norm2)*(*norm2));
+  else if (dim == 4) {
+    norm= sqrt((*normal)*(*normal) + (*norm1)*(*norm1) + (*norm2)*(*norm2)
+               + (*norm3)*(*norm3));
+  }else if (dim > 4) {
+    norm= (*normal)*(*normal) + (*norm1)*(*norm1) + (*norm2)*(*norm2)
+               + (*norm3)*(*norm3);
+    for (k=dim-4, colp=normal+4; k--; colp++)
+      norm += (*colp) * (*colp);
+    norm= sqrt(norm);
+  }
+  if (minnorm) {
+    if (norm < *minnorm)
+      *ismin= True;
+    else
+      *ismin= False;
+  }
+  wmin_(Wmindenom, norm);
+  if (norm > qh->MINdenom) {
+    if (!toporient)
+      norm= -norm;
+    *normal /= norm;
+    *norm1 /= norm;
+    if (dim == 2)
+      ; /* all done */
+    else if (dim == 3)
+      *norm2 /= norm;
+    else if (dim == 4) {
+      *norm2 /= norm;
+      *norm3 /= norm;
+    }else if (dim >4) {
+      *norm2 /= norm;
+      *norm3 /= norm;
+      for (k=dim-4, colp=normal+4; k--; )
+        *colp++ /= norm;
+    }
+  }else if (norm == 0.0) {
+    temp= sqrt(1.0/dim);
+    for (k=dim, colp=normal; k--; )
+      *colp++ = temp;
+  }else {
+    if (!toporient)
+      norm= -norm;
+    for (k=dim, colp=normal; k--; colp++) { /* k used below */
+      temp= qh_divzero(*colp, norm, qh->MINdenom_1, &zerodiv);
+      if (!zerodiv)
+        *colp= temp;
+      else {
+        maxp= qh_maxabsval(normal, dim);
+        temp= ((*maxp * norm >= 0.0) ? 1.0 : -1.0);
+        for (k=dim, colp=normal; k--; colp++)
+          *colp= 0.0;
+        *maxp= temp;
+        zzinc_(Znearlysingular);
+        trace0((qh, qh->ferr, 1, "qh_normalize: norm=%2.2g too small during p%d\n",
+               norm, qh->furthest_id));
+        return;
+      }
+    }
+  }
+} /* normalize */
+
+
+/*-<a                             href="qh-geom_r.htm#TOC"
+  >-------------------------------</a><a name="projectpoint">-</a>
+
+  qh_projectpoint(qh, point, facet, dist )
+    project point onto a facet by dist
+
+  returns:
+    returns a new point
+
+  notes:
+    if dist= distplane(point,facet)
+      this projects point to hyperplane
+    assumes qh_memfree_() is valid for normal_size
+*/
+pointT *qh_projectpoint(qhT *qh, pointT *point, facetT *facet, realT dist) {
+  pointT *newpoint, *np, *normal;
+  int normsize= qh->normal_size;
+  int k;
+  void **freelistp; /* used if !qh_NOmem by qh_memalloc_() */
+
+  qh_memalloc_(qh, normsize, freelistp, newpoint, pointT);
+  np= newpoint;
+  normal= facet->normal;
+  for (k=qh->hull_dim; k--; )
+    *(np++)= *point++ - dist * *normal++;
+  return(newpoint);
+} /* projectpoint */
+
+
+/*-<a                             href="qh-geom_r.htm#TOC"
+  >-------------------------------</a><a name="setfacetplane">-</a>
+
+  qh_setfacetplane(qh, facet )
+    sets the hyperplane for a facet
+    if qh.RANDOMdist, joggles hyperplane
+
+  notes:
+    uses global buffers qh.gm_matrix and qh.gm_row
+    overwrites facet->normal if already defined
+    updates Wnewvertex if PRINTstatistics
+    sets facet->upperdelaunay if upper envelope of Delaunay triangulation
+
+  design:
+    copy vertex coordinates to qh.gm_matrix/gm_row
+    compute determinate
+    if nearzero
+      recompute determinate with gaussian elimination
+      if nearzero
+        force outside orientation by testing interior point
+*/
+void qh_setfacetplane(qhT *qh, facetT *facet) {
+  pointT *point;
+  vertexT *vertex, **vertexp;
+  int normsize= qh->normal_size;
+  int k,i, oldtrace= 0;
+  realT dist;
+  void **freelistp; /* used if !qh_NOmem by qh_memalloc_() */
+  coordT *coord, *gmcoord;
+  pointT *point0= SETfirstt_(facet->vertices, vertexT)->point;
+  boolT nearzero= False;
+
+  zzinc_(Zsetplane);
+  if (!facet->normal)
+    qh_memalloc_(qh, normsize, freelistp, facet->normal, coordT);
+  if (facet == qh->tracefacet) {
+    oldtrace= qh->IStracing;
+    qh->IStracing= 5;
+    qh_fprintf(qh, qh->ferr, 8012, "qh_setfacetplane: facet f%d created.\n", facet->id);
+    qh_fprintf(qh, qh->ferr, 8013, "  Last point added to hull was p%d.", qh->furthest_id);
+    if (zzval_(Ztotmerge))
+      qh_fprintf(qh, qh->ferr, 8014, "  Last merge was #%d.", zzval_(Ztotmerge));
+    qh_fprintf(qh, qh->ferr, 8015, "\n\nCurrent summary is:\n");
+      qh_printsummary(qh, qh->ferr);
+  }
+  if (qh->hull_dim <= 4) {
+    i= 0;
+    if (qh->RANDOMdist) {
+      gmcoord= qh->gm_matrix;
+      FOREACHvertex_(facet->vertices) {
+        qh->gm_row[i++]= gmcoord;
+        coord= vertex->point;
+        for (k=qh->hull_dim; k--; )
+          *(gmcoord++)= *coord++ * qh_randomfactor(qh, qh->RANDOMa, qh->RANDOMb);
+      }
+    }else {
+      FOREACHvertex_(facet->vertices)
+       qh->gm_row[i++]= vertex->point;
+    }
+    qh_sethyperplane_det(qh, qh->hull_dim, qh->gm_row, point0, facet->toporient,
+                facet->normal, &facet->offset, &nearzero);
+  }
+  if (qh->hull_dim > 4 || nearzero) {
+    i= 0;
+    gmcoord= qh->gm_matrix;
+    FOREACHvertex_(facet->vertices) {
+      if (vertex->point != point0) {
+        qh->gm_row[i++]= gmcoord;
+        coord= vertex->point;
+        point= point0;
+        for (k=qh->hull_dim; k--; )
+          *(gmcoord++)= *coord++ - *point++;
+      }
+    }
+    qh->gm_row[i]= gmcoord;  /* for areasimplex */
+    if (qh->RANDOMdist) {
+      gmcoord= qh->gm_matrix;
+      for (i=qh->hull_dim-1; i--; ) {
+        for (k=qh->hull_dim; k--; )
+          *(gmcoord++) *= qh_randomfactor(qh, qh->RANDOMa, qh->RANDOMb);
+      }
+    }
+    qh_sethyperplane_gauss(qh, qh->hull_dim, qh->gm_row, point0, facet->toporient,
+                facet->normal, &facet->offset, &nearzero);
+    if (nearzero) {
+      if (qh_orientoutside(qh, facet)) {
+        trace0((qh, qh->ferr, 2, "qh_setfacetplane: flipped orientation after testing interior_point during p%d\n", qh->furthest_id));
+      /* this is part of using Gaussian Elimination.  For example in 5-d
+           1 1 1 1 0
+           1 1 1 1 1
+           0 0 0 1 0
+           0 1 0 0 0
+           1 0 0 0 0
+           norm= 0.38 0.38 -0.76 0.38 0
+         has a determinate of 1, but g.e. after subtracting pt. 0 has
+         0's in the diagonal, even with full pivoting.  It does work
+         if you subtract pt. 4 instead. */
+      }
+    }
+  }
+  facet->upperdelaunay= False;
+  if (qh->DELAUNAY) {
+    if (qh->UPPERdelaunay) {     /* matches qh_triangulate_facet and qh.lower_threshold in qh_initbuild */
+      if (facet->normal[qh->hull_dim -1] >= qh->ANGLEround * qh_ZEROdelaunay)
+        facet->upperdelaunay= True;
+    }else {
+      if (facet->normal[qh->hull_dim -1] > -qh->ANGLEround * qh_ZEROdelaunay)
+        facet->upperdelaunay= True;
+    }
+  }
+  if (qh->PRINTstatistics || qh->IStracing || qh->TRACElevel || qh->JOGGLEmax < REALmax) {
+    qh->old_randomdist= qh->RANDOMdist;
+    qh->RANDOMdist= False;
+    FOREACHvertex_(facet->vertices) {
+      if (vertex->point != point0) {
+        boolT istrace= False;
+        zinc_(Zdiststat);
+        qh_distplane(qh, vertex->point, facet, &dist);
+        dist= fabs_(dist);
+        zinc_(Znewvertex);
+        wadd_(Wnewvertex, dist);
+        if (dist > wwval_(Wnewvertexmax)) {
+          wwval_(Wnewvertexmax)= dist;
+          if (dist > qh->max_outside) {
+            qh->max_outside= dist;  /* used by qh_maxouter(qh) */
+            if (dist > qh->TRACEdist)
+              istrace= True;
+          }
+        }else if (-dist > qh->TRACEdist)
+          istrace= True;
+        if (istrace) {
+          qh_fprintf(qh, qh->ferr, 8016, "qh_setfacetplane: ====== vertex p%d(v%d) increases max_outside to %2.2g for new facet f%d last p%d\n",
+                qh_pointid(qh, vertex->point), vertex->id, dist, facet->id, qh->furthest_id);
+          qh_errprint(qh, "DISTANT", facet, NULL, NULL, NULL);
+        }
+      }
+    }
+    qh->RANDOMdist= qh->old_randomdist;
+  }
+  if (qh->IStracing >= 3) {
+    qh_fprintf(qh, qh->ferr, 8017, "qh_setfacetplane: f%d offset %2.2g normal: ",
+             facet->id, facet->offset);
+    for (k=0; k < qh->hull_dim; k++)
+      qh_fprintf(qh, qh->ferr, 8018, "%2.2g ", facet->normal[k]);
+    qh_fprintf(qh, qh->ferr, 8019, "\n");
+  }
+  if (facet == qh->tracefacet)
+    qh->IStracing= oldtrace;
+} /* setfacetplane */
+
+
+/*-<a                             href="qh-geom_r.htm#TOC"
+  >-------------------------------</a><a name="sethyperplane_det">-</a>
+
+  qh_sethyperplane_det(qh, dim, rows, point0, toporient, normal, offset, nearzero )
+    given dim X dim array indexed by rows[], one row per point,
+        toporient(flips all signs),
+        and point0 (any row)
+    set normalized hyperplane equation from oriented simplex
+
+  returns:
+    normal (normalized)
+    offset (places point0 on the hyperplane)
+    sets nearzero if hyperplane not through points
+
+  notes:
+    only defined for dim == 2..4
+    rows[] is not modified
+    solves det(P-V_0, V_n-V_0, ..., V_1-V_0)=0, i.e. every point is on hyperplane
+    see Bower & Woodworth, A programmer's geometry, Butterworths 1983.
+
+  derivation of 3-d minnorm
+    Goal: all vertices V_i within qh.one_merge of hyperplane
+    Plan: exactly translate the facet so that V_0 is the origin
+          exactly rotate the facet so that V_1 is on the x-axis and y_2=0.
+          exactly rotate the effective perturbation to only effect n_0
+             this introduces a factor of sqrt(3)
+    n_0 = ((y_2-y_0)*(z_1-z_0) - (z_2-z_0)*(y_1-y_0)) / norm
+    Let M_d be the max coordinate difference
+    Let M_a be the greater of M_d and the max abs. coordinate
+    Let u be machine roundoff and distround be max error for distance computation
+    The max error for n_0 is sqrt(3) u M_a M_d / norm.  n_1 is approx. 1 and n_2 is approx. 0
+    The max error for distance of V_1 is sqrt(3) u M_a M_d M_d / norm.  Offset=0 at origin
+    Then minnorm = 1.8 u M_a M_d M_d / qh.ONEmerge
+    Note that qh.one_merge is approx. 45.5 u M_a and norm is usually about M_d M_d
+
+  derivation of 4-d minnorm
+    same as above except rotate the facet so that V_1 on x-axis and w_2, y_3, w_3=0
+     [if two vertices fixed on x-axis, can rotate the other two in yzw.]
+    n_0 = det3_(...) = y_2 det2_(z_1, w_1, z_3, w_3) = - y_2 w_1 z_3
+     [all other terms contain at least two factors nearly zero.]
+    The max error for n_0 is sqrt(4) u M_a M_d M_d / norm
+    Then minnorm = 2 u M_a M_d M_d M_d / qh.ONEmerge
+    Note that qh.one_merge is approx. 82 u M_a and norm is usually about M_d M_d M_d
+*/
+void qh_sethyperplane_det(qhT *qh, int dim, coordT **rows, coordT *point0,
+          boolT toporient, coordT *normal, realT *offset, boolT *nearzero) {
+  realT maxround, dist;
+  int i;
+  pointT *point;
+
+
+  if (dim == 2) {
+    normal[0]= dY(1,0);
+    normal[1]= dX(0,1);
+    qh_normalize2(qh, normal, dim, toporient, NULL, NULL);
+    *offset= -(point0[0]*normal[0]+point0[1]*normal[1]);
+    *nearzero= False;  /* since nearzero norm => incident points */
+  }else if (dim == 3) {
+    normal[0]= det2_(dY(2,0), dZ(2,0),
+                     dY(1,0), dZ(1,0));
+    normal[1]= det2_(dX(1,0), dZ(1,0),
+                     dX(2,0), dZ(2,0));
+    normal[2]= det2_(dX(2,0), dY(2,0),
+                     dX(1,0), dY(1,0));
+    qh_normalize2(qh, normal, dim, toporient, NULL, NULL);
+    *offset= -(point0[0]*normal[0] + point0[1]*normal[1]
+               + point0[2]*normal[2]);
+    maxround= qh->DISTround;
+    for (i=dim; i--; ) {
+      point= rows[i];
+      if (point != point0) {
+        dist= *offset + (point[0]*normal[0] + point[1]*normal[1]
+               + point[2]*normal[2]);
+        if (dist > maxround || dist < -maxround) {
+          *nearzero= True;
+          break;
+        }
+      }
+    }
+  }else if (dim == 4) {
+    normal[0]= - det3_(dY(2,0), dZ(2,0), dW(2,0),
+                        dY(1,0), dZ(1,0), dW(1,0),
+                        dY(3,0), dZ(3,0), dW(3,0));
+    normal[1]=   det3_(dX(2,0), dZ(2,0), dW(2,0),
+                        dX(1,0), dZ(1,0), dW(1,0),
+                        dX(3,0), dZ(3,0), dW(3,0));
+    normal[2]= - det3_(dX(2,0), dY(2,0), dW(2,0),
+                        dX(1,0), dY(1,0), dW(1,0),
+                        dX(3,0), dY(3,0), dW(3,0));
+    normal[3]=   det3_(dX(2,0), dY(2,0), dZ(2,0),
+                        dX(1,0), dY(1,0), dZ(1,0),
+                        dX(3,0), dY(3,0), dZ(3,0));
+    qh_normalize2(qh, normal, dim, toporient, NULL, NULL);
+    *offset= -(point0[0]*normal[0] + point0[1]*normal[1]
+               + point0[2]*normal[2] + point0[3]*normal[3]);
+    maxround= qh->DISTround;
+    for (i=dim; i--; ) {
+      point= rows[i];
+      if (point != point0) {
+        dist= *offset + (point[0]*normal[0] + point[1]*normal[1]
+               + point[2]*normal[2] + point[3]*normal[3]);
+        if (dist > maxround || dist < -maxround) {
+          *nearzero= True;
+          break;
+        }
+      }
+    }
+  }
+  if (*nearzero) {
+    zzinc_(Zminnorm);
+    trace0((qh, qh->ferr, 3, "qh_sethyperplane_det: degenerate norm during p%d.\n", qh->furthest_id));
+    zzinc_(Znearlysingular);
+  }
+} /* sethyperplane_det */
+
+
+/*-<a                             href="qh-geom_r.htm#TOC"
+  >-------------------------------</a><a name="sethyperplane_gauss">-</a>
+
+  qh_sethyperplane_gauss(qh, dim, rows, point0, toporient, normal, offset, nearzero )
+    given(dim-1) X dim array of rows[i]= V_{i+1} - V_0 (point0)
+    set normalized hyperplane equation from oriented simplex
+
+  returns:
+    normal (normalized)
+    offset (places point0 on the hyperplane)
+
+  notes:
+    if nearzero
+      orientation may be incorrect because of incorrect sign flips in gausselim
+    solves [V_n-V_0,...,V_1-V_0, 0 .. 0 1] * N == [0 .. 0 1]
+        or [V_n-V_0,...,V_1-V_0, 0 .. 0 1] * N == [0]
+    i.e., N is normal to the hyperplane, and the unnormalized
+        distance to [0 .. 1] is either 1 or   0
+
+  design:
+    perform gaussian elimination
+    flip sign for negative values
+    perform back substitution
+    normalize result
+    compute offset
+*/
+void qh_sethyperplane_gauss(qhT *qh, int dim, coordT **rows, pointT *point0,
+                boolT toporient, coordT *normal, coordT *offset, boolT *nearzero) {
+  coordT *pointcoord, *normalcoef;
+  int k;
+  boolT sign= toporient, nearzero2= False;
+
+  qh_gausselim(qh, rows, dim-1, dim, &sign, nearzero);
+  for (k=dim-1; k--; ) {
+    if ((rows[k])[k] < 0)
+      sign ^= 1;
+  }
+  if (*nearzero) {
+    zzinc_(Znearlysingular);
+    trace0((qh, qh->ferr, 4, "qh_sethyperplane_gauss: nearly singular or axis parallel hyperplane during p%d.\n", qh->furthest_id));
+    qh_backnormal(qh, rows, dim-1, dim, sign, normal, &nearzero2);
+  }else {
+    qh_backnormal(qh, rows, dim-1, dim, sign, normal, &nearzero2);
+    if (nearzero2) {
+      zzinc_(Znearlysingular);
+      trace0((qh, qh->ferr, 5, "qh_sethyperplane_gauss: singular or axis parallel hyperplane at normalization during p%d.\n", qh->furthest_id));
+    }
+  }
+  if (nearzero2)
+    *nearzero= True;
+  qh_normalize2(qh, normal, dim, True, NULL, NULL);
+  pointcoord= point0;
+  normalcoef= normal;
+  *offset= -(*pointcoord++ * *normalcoef++);
+  for (k=dim-1; k--; )
+    *offset -= *pointcoord++ * *normalcoef++;
+} /* sethyperplane_gauss */
+
+
+
diff --git a/C/global_r.c b/C/global_r.c
new file mode 100644
--- /dev/null
+++ b/C/global_r.c
@@ -0,0 +1,2100 @@
+
+/*<html><pre>  -<a                             href="qh-globa_r.htm"
+  >-------------------------------</a><a name="TOP">-</a>
+
+   global_r.c
+   initializes all the globals of the qhull application
+
+   see README
+
+   see libqhull_r.h for qh.globals and function prototypes
+
+   see qhull_ra.h for internal functions
+
+   Copyright (c) 1993-2015 The Geometry Center.
+   $Id: //main/2015/qhull/src/libqhull_r/global_r.c#16 $$Change: 2066 $
+   $DateTime: 2016/01/18 19:29:17 $$Author: bbarber $
+ */
+
+#include "qhull_ra.h"
+
+/*========= qh->definition -- globals defined in libqhull_r.h =======================*/
+
+/*-<a                             href  ="qh-globa_r.htm#TOC"
+  >--------------------------------</a><a name="qh_version">-</a>
+
+  qh_version
+    version string by year and date
+    qh_version2 for Unix users and -V
+
+    the revision increases on code changes only
+
+  notes:
+    change date:    Changes.txt, Announce.txt, index.htm, README.txt,
+                    qhull-news.html, Eudora signatures, CMakeLists.txt
+    change version: README.txt, qh-get.htm, File_id.diz, Makefile.txt, CMakeLists.txt
+    check that CmakeLists @version is the same as qh_version2
+    change year:    Copying.txt
+    check download size
+    recompile user_eg_r.c, rbox_r.c, libqhull_r.c, qconvex_r.c, qdelaun_r.c qvoronoi_r.c, qhalf_r.c, testqset_r.c
+*/
+
+const char qh_version[]= "2015.2.r 2016/01/18";
+const char qh_version2[]= "qhull_r 7.2.0 (2015.2.r 2016/01/18)";
+
+/*-<a                             href="qh-globa_r.htm#TOC"
+  >-------------------------------</a><a name="appendprint">-</a>
+
+  qh_appendprint(qh, printFormat )
+    append printFormat to qh.PRINTout unless already defined
+*/
+void qh_appendprint(qhT *qh, qh_PRINT format) {
+  int i;
+
+  for (i=0; i < qh_PRINTEND; i++) {
+    if (qh->PRINTout[i] == format && format != qh_PRINTqhull)
+      break;
+    if (!qh->PRINTout[i]) {
+      qh->PRINTout[i]= format;
+      break;
+    }
+  }
+} /* appendprint */
+
+/*-<a                             href="qh-globa_r.htm#TOC"
+  >-------------------------------</a><a name="checkflags">-</a>
+
+  qh_checkflags(qh, commandStr, hiddenFlags )
+    errors if commandStr contains hiddenFlags
+    hiddenFlags starts and ends with a space and is space delimited (checked)
+
+  notes:
+    ignores first word (e.g., "qconvex i")
+    use qh_strtol/strtod since strtol/strtod may or may not skip trailing spaces
+
+  see:
+    qh_initflags() initializes Qhull according to commandStr
+*/
+void qh_checkflags(qhT *qh, char *command, char *hiddenflags) {
+  char *s= command, *t, *chkerr; /* qh_skipfilename is non-const */
+  char key, opt, prevopt;
+  char chkkey[]= "   ";
+  char chkopt[]=  "    ";
+  char chkopt2[]= "     ";
+  boolT waserr= False;
+
+  if (*hiddenflags != ' ' || hiddenflags[strlen(hiddenflags)-1] != ' ') {
+    qh_fprintf(qh, qh->ferr, 6026, "qhull error (qh_checkflags): hiddenflags must start and end with a space: \"%s\"", hiddenflags);
+    qh_errexit(qh, qh_ERRinput, NULL, NULL);
+  }
+  if (strpbrk(hiddenflags, ",\n\r\t")) {
+    qh_fprintf(qh, qh->ferr, 6027, "qhull error (qh_checkflags): hiddenflags contains commas, newlines, or tabs: \"%s\"", hiddenflags);
+    qh_errexit(qh, qh_ERRinput, NULL, NULL);
+  }
+  while (*s && !isspace(*s))  /* skip program name */
+    s++;
+  while (*s) {
+    while (*s && isspace(*s))
+      s++;
+    if (*s == '-')
+      s++;
+    if (!*s)
+      break;
+    key = *s++;
+    chkerr = NULL;
+    if (key == 'T' && (*s == 'I' || *s == 'O')) {  /* TI or TO 'file name' */
+      s= qh_skipfilename(qh, ++s);
+      continue;
+    }
+    chkkey[1]= key;
+    if (strstr(hiddenflags, chkkey)) {
+      chkerr= chkkey;
+    }else if (isupper(key)) {
+      opt= ' ';
+      prevopt= ' ';
+      chkopt[1]= key;
+      chkopt2[1]= key;
+      while (!chkerr && *s && !isspace(*s)) {
+        opt= *s++;
+        if (isalpha(opt)) {
+          chkopt[2]= opt;
+          if (strstr(hiddenflags, chkopt))
+            chkerr= chkopt;
+          if (prevopt != ' ') {
+            chkopt2[2]= prevopt;
+            chkopt2[3]= opt;
+            if (strstr(hiddenflags, chkopt2))
+              chkerr= chkopt2;
+          }
+        }else if (key == 'Q' && isdigit(opt) && prevopt != 'b'
+              && (prevopt == ' ' || islower(prevopt))) {
+            chkopt[2]= opt;
+            if (strstr(hiddenflags, chkopt))
+              chkerr= chkopt;
+        }else {
+          qh_strtod(s-1, &t);
+          if (s < t)
+            s= t;
+        }
+        prevopt= opt;
+      }
+    }
+    if (chkerr) {
+      *chkerr= '\'';
+      chkerr[strlen(chkerr)-1]=  '\'';
+      qh_fprintf(qh, qh->ferr, 6029, "qhull error: option %s is not used with this program.\n             It may be used with qhull.\n", chkerr);
+      waserr= True;
+    }
+  }
+  if (waserr)
+    qh_errexit(qh, qh_ERRinput, NULL, NULL);
+} /* checkflags */
+
+/*-<a                             href="qh-globa_r.htm#TOC"
+  >-------------------------------</a><a name="qh_clear_outputflags">-</a>
+
+  qh_clear_outputflags(qh)
+    Clear output flags for QhullPoints
+*/
+void qh_clear_outputflags(qhT *qh) {
+  int i,k;
+
+  qh->ANNOTATEoutput= False;
+  qh->DOintersections= False;
+  qh->DROPdim= -1;
+  qh->FORCEoutput= False;
+  qh->GETarea= False;
+  qh->GOODpoint= 0;
+  qh->GOODpointp= NULL;
+  qh->GOODthreshold= False;
+  qh->GOODvertex= 0;
+  qh->GOODvertexp= NULL;
+  qh->IStracing= 0;
+  qh->KEEParea= False;
+  qh->KEEPmerge= False;
+  qh->KEEPminArea= REALmax;
+  qh->PRINTcentrums= False;
+  qh->PRINTcoplanar= False;
+  qh->PRINTdots= False;
+  qh->PRINTgood= False;
+  qh->PRINTinner= False;
+  qh->PRINTneighbors= False;
+  qh->PRINTnoplanes= False;
+  qh->PRINToptions1st= False;
+  qh->PRINTouter= False;
+  qh->PRINTprecision= True;
+  qh->PRINTridges= False;
+  qh->PRINTspheres= False;
+  qh->PRINTstatistics= False;
+  qh->PRINTsummary= False;
+  qh->PRINTtransparent= False;
+  qh->SPLITthresholds= False;
+  qh->TRACElevel= 0;
+  qh->TRInormals= False;
+  qh->USEstdout= False;
+  qh->VERIFYoutput= False;
+  for (k=qh->input_dim+1; k--; ) {  /* duplicated in qh_initqhull_buffers and qh_clear_outputflags */
+    qh->lower_threshold[k]= -REALmax;
+    qh->upper_threshold[k]= REALmax;
+    qh->lower_bound[k]= -REALmax;
+    qh->upper_bound[k]= REALmax;
+  }
+
+  for (i=0; i < qh_PRINTEND; i++) {
+    qh->PRINTout[i]= qh_PRINTnone;
+  }
+
+  if (!qh->qhull_commandsiz2)
+      qh->qhull_commandsiz2= (int)strlen(qh->qhull_command); /* WARN64 */
+  else {
+      qh->qhull_command[qh->qhull_commandsiz2]= '\0';
+  }
+  if (!qh->qhull_optionsiz2)
+    qh->qhull_optionsiz2= (int)strlen(qh->qhull_options);  /* WARN64 */
+  else {
+    qh->qhull_options[qh->qhull_optionsiz2]= '\0';
+    qh->qhull_optionlen= qh_OPTIONline;  /* start a new line */
+  }
+} /* clear_outputflags */
+
+/*-<a                             href="qh-globa_r.htm#TOC"
+  >-------------------------------</a><a name="clock">-</a>
+
+  qh_clock()
+    return user CPU time in 100ths (qh_SECtick)
+    only defined for qh_CLOCKtype == 2
+
+  notes:
+    use first value to determine time 0
+    from Stevens '92 8.15
+*/
+unsigned long qh_clock(qhT *qh) {
+
+#if (qh_CLOCKtype == 2)
+  struct tms time;
+  static long clktck;  /* initialized first call and never updated */
+  double ratio, cpu;
+  unsigned long ticks;
+
+  if (!clktck) {
+    if ((clktck= sysconf(_SC_CLK_TCK)) < 0) {
+      qh_fprintf(qh, qh->ferr, 6030, "qhull internal error (qh_clock): sysconf() failed.  Use qh_CLOCKtype 1 in user.h\n");
+      qh_errexit(qh, qh_ERRqhull, NULL, NULL);
+    }
+  }
+  if (times(&time) == -1) {
+    qh_fprintf(qh, qh->ferr, 6031, "qhull internal error (qh_clock): times() failed.  Use qh_CLOCKtype 1 in user.h\n");
+    qh_errexit(qh, qh_ERRqhull, NULL, NULL);
+  }
+  ratio= qh_SECticks / (double)clktck;
+  ticks= time.tms_utime * ratio;
+  return ticks;
+#else
+  qh_fprintf(qh, qh->ferr, 6032, "qhull internal error (qh_clock): use qh_CLOCKtype 2 in user.h\n");
+  qh_errexit(qh, qh_ERRqhull, NULL, NULL); /* never returns */
+  return 0;
+#endif
+} /* clock */
+
+/*-<a                             href="qh-globa_r.htm#TOC"
+  >-------------------------------</a><a name="freebuffers">-</a>
+
+  qh_freebuffers()
+    free up global memory buffers
+
+  notes:
+    must match qh_initbuffers()
+*/
+void qh_freebuffers(qhT *qh) {
+
+  trace5((qh, qh->ferr, 5001, "qh_freebuffers: freeing up global memory buffers\n"));
+  /* allocated by qh_initqhull_buffers */
+  qh_memfree(qh, qh->NEARzero, qh->hull_dim * sizeof(realT));
+  qh_memfree(qh, qh->lower_threshold, (qh->input_dim+1) * sizeof(realT));
+  qh_memfree(qh, qh->upper_threshold, (qh->input_dim+1) * sizeof(realT));
+  qh_memfree(qh, qh->lower_bound, (qh->input_dim+1) * sizeof(realT));
+  qh_memfree(qh, qh->upper_bound, (qh->input_dim+1) * sizeof(realT));
+  qh_memfree(qh, qh->gm_matrix, (qh->hull_dim+1) * qh->hull_dim * sizeof(coordT));
+  qh_memfree(qh, qh->gm_row, (qh->hull_dim+1) * sizeof(coordT *));
+  qh->NEARzero= qh->lower_threshold= qh->upper_threshold= NULL;
+  qh->lower_bound= qh->upper_bound= NULL;
+  qh->gm_matrix= NULL;
+  qh->gm_row= NULL;
+  qh_setfree(qh, &qh->other_points);
+  qh_setfree(qh, &qh->del_vertices);
+  qh_setfree(qh, &qh->coplanarfacetset);
+  if (qh->line)                /* allocated by qh_readinput, freed if no error */
+    qh_free(qh->line);
+  if (qh->half_space)
+    qh_free(qh->half_space);
+  if (qh->temp_malloc)
+    qh_free(qh->temp_malloc);
+  if (qh->feasible_point)      /* allocated by qh_readfeasible */
+    qh_free(qh->feasible_point);
+  if (qh->feasible_string)     /* allocated by qh_initflags */
+    qh_free(qh->feasible_string);
+  qh->line= qh->feasible_string= NULL;
+  qh->half_space= qh->feasible_point= qh->temp_malloc= NULL;
+  /* usually allocated by qh_readinput */
+  if (qh->first_point && qh->POINTSmalloc) {
+    qh_free(qh->first_point);
+    qh->first_point= NULL;
+  }
+  if (qh->input_points && qh->input_malloc) { /* set by qh_joggleinput */
+    qh_free(qh->input_points);
+    qh->input_points= NULL;
+  }
+  trace5((qh, qh->ferr, 5002, "qh_freebuffers: finished\n"));
+} /* freebuffers */
+
+
+/*-<a                             href="qh-globa_r.htm#TOC"
+  >-------------------------------</a><a name="freebuild">-</a>
+
+  qh_freebuild(qh, allmem )
+    free global memory used by qh_initbuild and qh_buildhull
+    if !allmem,
+      does not free short memory (e.g., facetT, freed by qh_memfreeshort)
+
+  design:
+    free centrums
+    free each vertex
+    mark unattached ridges
+    for each facet
+      free ridges
+      free outside set, coplanar set, neighbor set, ridge set, vertex set
+      free facet
+    free hash table
+    free interior point
+    free merge set
+    free temporary sets
+*/
+void qh_freebuild(qhT *qh, boolT allmem) {
+  facetT *facet;
+  vertexT *vertex;
+  ridgeT *ridge, **ridgep;
+  mergeT *merge, **mergep;
+
+  trace1((qh, qh->ferr, 1005, "qh_freebuild: free memory from qh_inithull and qh_buildhull\n"));
+  if (qh->del_vertices)
+    qh_settruncate(qh, qh->del_vertices, 0);
+  if (allmem) {
+    while ((vertex= qh->vertex_list)) {
+      if (vertex->next)
+        qh_delvertex(qh, vertex);
+      else {
+        qh_memfree(qh, vertex, (int)sizeof(vertexT));
+        qh->newvertex_list= qh->vertex_list= NULL;
+      }
+    }
+  }else if (qh->VERTEXneighbors) {
+    FORALLvertices
+      qh_setfreelong(qh, &(vertex->neighbors));
+  }
+  qh->VERTEXneighbors= False;
+  qh->GOODclosest= NULL;
+  if (allmem) {
+    FORALLfacets {
+      FOREACHridge_(facet->ridges)
+        ridge->seen= False;
+    }
+    FORALLfacets {
+      if (facet->visible) {
+        FOREACHridge_(facet->ridges) {
+          if (!otherfacet_(ridge, facet)->visible)
+            ridge->seen= True;  /* an unattached ridge */
+        }
+      }
+    }
+    while ((facet= qh->facet_list)) {
+      FOREACHridge_(facet->ridges) {
+        if (ridge->seen) {
+          qh_setfree(qh, &(ridge->vertices));
+          qh_memfree(qh, ridge, (int)sizeof(ridgeT));
+        }else
+          ridge->seen= True;
+      }
+      qh_setfree(qh, &(facet->outsideset));
+      qh_setfree(qh, &(facet->coplanarset));
+      qh_setfree(qh, &(facet->neighbors));
+      qh_setfree(qh, &(facet->ridges));
+      qh_setfree(qh, &(facet->vertices));
+      if (facet->next)
+        qh_delfacet(qh, facet);
+      else {
+        qh_memfree(qh, facet, (int)sizeof(facetT));
+        qh->visible_list= qh->newfacet_list= qh->facet_list= NULL;
+      }
+    }
+  }else {
+    FORALLfacets {
+      qh_setfreelong(qh, &(facet->outsideset));
+      qh_setfreelong(qh, &(facet->coplanarset));
+      if (!facet->simplicial) {
+        qh_setfreelong(qh, &(facet->neighbors));
+        qh_setfreelong(qh, &(facet->ridges));
+        qh_setfreelong(qh, &(facet->vertices));
+      }
+    }
+  }
+  qh_setfree(qh, &(qh->hash_table));
+  qh_memfree(qh, qh->interior_point, qh->normal_size);
+  qh->interior_point= NULL;
+  FOREACHmerge_(qh->facet_mergeset)  /* usually empty */
+    qh_memfree(qh, merge, (int)sizeof(mergeT));
+  qh->facet_mergeset= NULL;  /* temp set */
+  qh->degen_mergeset= NULL;  /* temp set */
+  qh_settempfree_all(qh);
+} /* freebuild */
+
+/*-<a                             href="qh-globa_r.htm#TOC"
+  >-------------------------------</a><a name="freeqhull">-</a>
+
+  qh_freeqhull(qh, allmem )
+
+  free global memory and set qhT to 0
+  if !allmem,
+    does not free short memory (freed by qh_memfreeshort unless qh_NOmem)
+
+notes:
+  sets qh.NOerrexit in case caller forgets to
+  Does not throw errors
+
+see:
+  see qh_initqhull_start2()
+  For libqhull_r, qhstatT is part of qhT
+
+design:
+  free global and temporary memory from qh_initbuild and qh_buildhull
+  free buffers
+*/
+void qh_freeqhull(qhT *qh, boolT allmem) {
+
+  qh->NOerrexit= True;  /* no more setjmp since called at exit and ~QhullQh */
+  trace1((qh, qh->ferr, 1006, "qh_freeqhull: free global memory\n"));
+  qh_freebuild(qh, allmem);
+  qh_freebuffers(qh);
+  /* memset is the same in qh_freeqhull() and qh_initqhull_start2() */
+  memset((char *)qh, 0, sizeof(qhT)-sizeof(qhmemT)-sizeof(qhstatT));
+  qh->NOerrexit= True;
+} /* freeqhull2 */
+
+/*-<a                             href="qh-globa_r.htm#TOC"
+  >-------------------------------</a><a name="init_A">-</a>
+
+  qh_init_A(qh, infile, outfile, errfile, argc, argv )
+    initialize memory and stdio files
+    convert input options to option string (qh.qhull_command)
+
+  notes:
+    infile may be NULL if qh_readpoints() is not called
+
+    errfile should always be defined.  It is used for reporting
+    errors.  outfile is used for output and format options.
+
+    argc/argv may be 0/NULL
+
+    called before error handling initialized
+    qh_errexit() may not be used
+*/
+void qh_init_A(qhT *qh, FILE *infile, FILE *outfile, FILE *errfile, int argc, char *argv[]) {
+  qh_meminit(qh, errfile);
+  qh_initqhull_start(qh, infile, outfile, errfile);
+  qh_init_qhull_command(qh, argc, argv);
+} /* init_A */
+
+/*-<a                             href="qh-globa_r.htm#TOC"
+  >-------------------------------</a><a name="init_B">-</a>
+
+  qh_init_B(qh, points, numpoints, dim, ismalloc )
+    initialize globals for points array
+
+    points has numpoints dim-dimensional points
+      points[0] is the first coordinate of the first point
+      points[1] is the second coordinate of the first point
+      points[dim] is the first coordinate of the second point
+
+    ismalloc=True
+      Qhull will call qh_free(points) on exit or input transformation
+    ismalloc=False
+      Qhull will allocate a new point array if needed for input transformation
+
+    qh.qhull_command
+      is the option string.
+      It is defined by qh_init_B(), qh_qhull_command(), or qh_initflags
+
+  returns:
+    if qh.PROJECTinput or (qh.DELAUNAY and qh.PROJECTdelaunay)
+      projects the input to a new point array
+
+        if qh.DELAUNAY,
+          qh.hull_dim is increased by one
+        if qh.ATinfinity,
+          qh_projectinput adds point-at-infinity for Delaunay tri.
+
+    if qh.SCALEinput
+      changes the upper and lower bounds of the input, see qh_scaleinput(qh)
+
+    if qh.ROTATEinput
+      rotates the input by a random rotation, see qh_rotateinput()
+      if qh.DELAUNAY
+        rotates about the last coordinate
+
+  notes:
+    called after points are defined
+    qh_errexit() may be used
+*/
+void qh_init_B(qhT *qh, coordT *points, int numpoints, int dim, boolT ismalloc) {
+  qh_initqhull_globals(qh, points, numpoints, dim, ismalloc);
+  if (qh->qhmem.LASTsize == 0)
+    qh_initqhull_mem(qh);
+  /* mem_r.c and qset_r.c are initialized */
+  qh_initqhull_buffers(qh);
+  qh_initthresholds(qh, qh->qhull_command);
+  if (qh->PROJECTinput || (qh->DELAUNAY && qh->PROJECTdelaunay))
+    qh_projectinput(qh);
+  if (qh->SCALEinput)
+    qh_scaleinput(qh);
+  if (qh->ROTATErandom >= 0) {
+    qh_randommatrix(qh, qh->gm_matrix, qh->hull_dim, qh->gm_row);
+    if (qh->DELAUNAY) {
+      int k, lastk= qh->hull_dim-1;
+      for (k=0; k < lastk; k++) {
+        qh->gm_row[k][lastk]= 0.0;
+        qh->gm_row[lastk][k]= 0.0;
+      }
+      qh->gm_row[lastk][lastk]= 1.0;
+    }
+    qh_gram_schmidt(qh, qh->hull_dim, qh->gm_row);
+    qh_rotateinput(qh, qh->gm_row);
+  }
+} /* init_B */
+
+/*-<a                             href="qh-globa_r.htm#TOC"
+  >-------------------------------</a><a name="init_qhull_command">-</a>
+
+  qh_init_qhull_command(qh, argc, argv )
+    build qh.qhull_command from argc/argv
+    Calls qh_exit if qhull_command is too short
+
+  returns:
+    a space-delimited string of options (just as typed)
+
+  notes:
+    makes option string easy to input and output
+
+    argc/argv may be 0/NULL
+*/
+void qh_init_qhull_command(qhT *qh, int argc, char *argv[]) {
+
+  if (!qh_argv_to_command(argc, argv, qh->qhull_command, (int)sizeof(qh->qhull_command))){
+    /* Assumes qh.ferr is defined. */
+    qh_fprintf(qh, qh->ferr, 6033, "qhull input error: more than %d characters in command line.\n",
+          (int)sizeof(qh->qhull_command));
+    qh_exit(qh_ERRinput);  /* error reported, can not use qh_errexit */
+  }
+} /* init_qhull_command */
+
+/*-<a                             href="qh-globa_r.htm#TOC"
+  >-------------------------------</a><a name="initflags">-</a>
+
+  qh_initflags(qh, commandStr )
+    set flags and initialized constants from commandStr
+    calls qh_exit() if qh->NOerrexit
+
+  returns:
+    sets qh.qhull_command to command if needed
+
+  notes:
+    ignores first word (e.g., "qhull d")
+    use qh_strtol/strtod since strtol/strtod may or may not skip trailing spaces
+
+  see:
+    qh_initthresholds() continues processing of 'Pdn' and 'PDn'
+    'prompt' in unix_r.c for documentation
+
+  design:
+    for each space-delimited option group
+      if top-level option
+        check syntax
+        append appropriate option to option string
+        set appropriate global variable or append printFormat to print options
+      else
+        for each sub-option
+          check syntax
+          append appropriate option to option string
+          set appropriate global variable or append printFormat to print options
+*/
+void qh_initflags(qhT *qh, char *command) {
+  int k, i, lastproject;
+  char *s= command, *t, *prev_s, *start, key;
+  boolT isgeom= False, wasproject;
+  realT r;
+
+  if(qh->NOerrexit){
+    qh_fprintf(qh, qh->ferr, 6245, "qhull initflags error: qh.NOerrexit was not cleared before calling qh_initflags().  It should be cleared after setjmp().  Exit qhull.");
+    qh_exit(6245);
+  }
+  if (command <= &qh->qhull_command[0] || command > &qh->qhull_command[0] + sizeof(qh->qhull_command)) {
+    if (command != &qh->qhull_command[0]) {
+      *qh->qhull_command= '\0';
+      strncat(qh->qhull_command, command, sizeof(qh->qhull_command)-strlen(qh->qhull_command)-1);
+    }
+    while (*s && !isspace(*s))  /* skip program name */
+      s++;
+  }
+  while (*s) {
+    while (*s && isspace(*s))
+      s++;
+    if (*s == '-')
+      s++;
+    if (!*s)
+      break;
+    prev_s= s;
+    switch (*s++) {
+    case 'd':
+      qh_option(qh, "delaunay", NULL, NULL);
+      qh->DELAUNAY= True;
+      break;
+    case 'f':
+      qh_option(qh, "facets", NULL, NULL);
+      qh_appendprint(qh, qh_PRINTfacets);
+      break;
+    case 'i':
+      qh_option(qh, "incidence", NULL, NULL);
+      qh_appendprint(qh, qh_PRINTincidences);
+      break;
+    case 'm':
+      qh_option(qh, "mathematica", NULL, NULL);
+      qh_appendprint(qh, qh_PRINTmathematica);
+      break;
+    case 'n':
+      qh_option(qh, "normals", NULL, NULL);
+      qh_appendprint(qh, qh_PRINTnormals);
+      break;
+    case 'o':
+      qh_option(qh, "offFile", NULL, NULL);
+      qh_appendprint(qh, qh_PRINToff);
+      break;
+    case 'p':
+      qh_option(qh, "points", NULL, NULL);
+      qh_appendprint(qh, qh_PRINTpoints);
+      break;
+    case 's':
+      qh_option(qh, "summary", NULL, NULL);
+      qh->PRINTsummary= True;
+      break;
+    case 'v':
+      qh_option(qh, "voronoi", NULL, NULL);
+      qh->VORONOI= True;
+      qh->DELAUNAY= True;
+      break;
+    case 'A':
+      if (!isdigit(*s) && *s != '.' && *s != '-')
+        qh_fprintf(qh, qh->ferr, 7002, "qhull warning: no maximum cosine angle given for option 'An'.  Ignored.\n");
+      else {
+        if (*s == '-') {
+          qh->premerge_cos= -qh_strtod(s, &s);
+          qh_option(qh, "Angle-premerge-", NULL, &qh->premerge_cos);
+          qh->PREmerge= True;
+        }else {
+          qh->postmerge_cos= qh_strtod(s, &s);
+          qh_option(qh, "Angle-postmerge", NULL, &qh->postmerge_cos);
+          qh->POSTmerge= True;
+        }
+        qh->MERGING= True;
+      }
+      break;
+    case 'C':
+      if (!isdigit(*s) && *s != '.' && *s != '-')
+        qh_fprintf(qh, qh->ferr, 7003, "qhull warning: no centrum radius given for option 'Cn'.  Ignored.\n");
+      else {
+        if (*s == '-') {
+          qh->premerge_centrum= -qh_strtod(s, &s);
+          qh_option(qh, "Centrum-premerge-", NULL, &qh->premerge_centrum);
+          qh->PREmerge= True;
+        }else {
+          qh->postmerge_centrum= qh_strtod(s, &s);
+          qh_option(qh, "Centrum-postmerge", NULL, &qh->postmerge_centrum);
+          qh->POSTmerge= True;
+        }
+        qh->MERGING= True;
+      }
+      break;
+    case 'E':
+      if (*s == '-')
+        qh_fprintf(qh, qh->ferr, 7004, "qhull warning: negative maximum roundoff given for option 'An'.  Ignored.\n");
+      else if (!isdigit(*s))
+        qh_fprintf(qh, qh->ferr, 7005, "qhull warning: no maximum roundoff given for option 'En'.  Ignored.\n");
+      else {
+        qh->DISTround= qh_strtod(s, &s);
+        qh_option(qh, "Distance-roundoff", NULL, &qh->DISTround);
+        qh->SETroundoff= True;
+      }
+      break;
+    case 'H':
+      start= s;
+      qh->HALFspace= True;
+      qh_strtod(s, &t);
+      while (t > s)  {
+        if (*t && !isspace(*t)) {
+          if (*t == ',')
+            t++;
+          else
+            qh_fprintf(qh, qh->ferr, 7006, "qhull warning: origin for Halfspace intersection should be 'Hn,n,n,...'\n");
+        }
+        s= t;
+        qh_strtod(s, &t);
+      }
+      if (start < t) {
+        if (!(qh->feasible_string= (char*)calloc((size_t)(t-start+1), (size_t)1))) {
+          qh_fprintf(qh, qh->ferr, 6034, "qhull error: insufficient memory for 'Hn,n,n'\n");
+          qh_errexit(qh, qh_ERRmem, NULL, NULL);
+        }
+        strncpy(qh->feasible_string, start, (size_t)(t-start));
+        qh_option(qh, "Halfspace-about", NULL, NULL);
+        qh_option(qh, qh->feasible_string, NULL, NULL);
+      }else
+        qh_option(qh, "Halfspace", NULL, NULL);
+      break;
+    case 'R':
+      if (!isdigit(*s))
+        qh_fprintf(qh, qh->ferr, 7007, "qhull warning: missing random perturbation for option 'Rn'.  Ignored\n");
+      else {
+        qh->RANDOMfactor= qh_strtod(s, &s);
+        qh_option(qh, "Random_perturb", NULL, &qh->RANDOMfactor);
+        qh->RANDOMdist= True;
+      }
+      break;
+    case 'V':
+      if (!isdigit(*s) && *s != '-')
+        qh_fprintf(qh, qh->ferr, 7008, "qhull warning: missing visible distance for option 'Vn'.  Ignored\n");
+      else {
+        qh->MINvisible= qh_strtod(s, &s);
+        qh_option(qh, "Visible", NULL, &qh->MINvisible);
+      }
+      break;
+    case 'U':
+      if (!isdigit(*s) && *s != '-')
+        qh_fprintf(qh, qh->ferr, 7009, "qhull warning: missing coplanar distance for option 'Un'.  Ignored\n");
+      else {
+        qh->MAXcoplanar= qh_strtod(s, &s);
+        qh_option(qh, "U-coplanar", NULL, &qh->MAXcoplanar);
+      }
+      break;
+    case 'W':
+      if (*s == '-')
+        qh_fprintf(qh, qh->ferr, 7010, "qhull warning: negative outside width for option 'Wn'.  Ignored.\n");
+      else if (!isdigit(*s))
+        qh_fprintf(qh, qh->ferr, 7011, "qhull warning: missing outside width for option 'Wn'.  Ignored\n");
+      else {
+        qh->MINoutside= qh_strtod(s, &s);
+        qh_option(qh, "W-outside", NULL, &qh->MINoutside);
+        qh->APPROXhull= True;
+      }
+      break;
+    /************  sub menus ***************/
+    case 'F':
+      while (*s && !isspace(*s)) {
+        switch (*s++) {
+        case 'a':
+          qh_option(qh, "Farea", NULL, NULL);
+          qh_appendprint(qh, qh_PRINTarea);
+          qh->GETarea= True;
+          break;
+        case 'A':
+          qh_option(qh, "FArea-total", NULL, NULL);
+          qh->GETarea= True;
+          break;
+        case 'c':
+          qh_option(qh, "Fcoplanars", NULL, NULL);
+          qh_appendprint(qh, qh_PRINTcoplanars);
+          break;
+        case 'C':
+          qh_option(qh, "FCentrums", NULL, NULL);
+          qh_appendprint(qh, qh_PRINTcentrums);
+          break;
+        case 'd':
+          qh_option(qh, "Fd-cdd-in", NULL, NULL);
+          qh->CDDinput= True;
+          break;
+        case 'D':
+          qh_option(qh, "FD-cdd-out", NULL, NULL);
+          qh->CDDoutput= True;
+          break;
+        case 'F':
+          qh_option(qh, "FFacets-xridge", NULL, NULL);
+          qh_appendprint(qh, qh_PRINTfacets_xridge);
+          break;
+        case 'i':
+          qh_option(qh, "Finner", NULL, NULL);
+          qh_appendprint(qh, qh_PRINTinner);
+          break;
+        case 'I':
+          qh_option(qh, "FIDs", NULL, NULL);
+          qh_appendprint(qh, qh_PRINTids);
+          break;
+        case 'm':
+          qh_option(qh, "Fmerges", NULL, NULL);
+          qh_appendprint(qh, qh_PRINTmerges);
+          break;
+        case 'M':
+          qh_option(qh, "FMaple", NULL, NULL);
+          qh_appendprint(qh, qh_PRINTmaple);
+          break;
+        case 'n':
+          qh_option(qh, "Fneighbors", NULL, NULL);
+          qh_appendprint(qh, qh_PRINTneighbors);
+          break;
+        case 'N':
+          qh_option(qh, "FNeighbors-vertex", NULL, NULL);
+          qh_appendprint(qh, qh_PRINTvneighbors);
+          break;
+        case 'o':
+          qh_option(qh, "Fouter", NULL, NULL);
+          qh_appendprint(qh, qh_PRINTouter);
+          break;
+        case 'O':
+          if (qh->PRINToptions1st) {
+            qh_option(qh, "FOptions", NULL, NULL);
+            qh_appendprint(qh, qh_PRINToptions);
+          }else
+            qh->PRINToptions1st= True;
+          break;
+        case 'p':
+          qh_option(qh, "Fpoint-intersect", NULL, NULL);
+          qh_appendprint(qh, qh_PRINTpointintersect);
+          break;
+        case 'P':
+          qh_option(qh, "FPoint-nearest", NULL, NULL);
+          qh_appendprint(qh, qh_PRINTpointnearest);
+          break;
+        case 'Q':
+          qh_option(qh, "FQhull", NULL, NULL);
+          qh_appendprint(qh, qh_PRINTqhull);
+          break;
+        case 's':
+          qh_option(qh, "Fsummary", NULL, NULL);
+          qh_appendprint(qh, qh_PRINTsummary);
+          break;
+        case 'S':
+          qh_option(qh, "FSize", NULL, NULL);
+          qh_appendprint(qh, qh_PRINTsize);
+          qh->GETarea= True;
+          break;
+        case 't':
+          qh_option(qh, "Ftriangles", NULL, NULL);
+          qh_appendprint(qh, qh_PRINTtriangles);
+          break;
+        case 'v':
+          /* option set in qh_initqhull_globals */
+          qh_appendprint(qh, qh_PRINTvertices);
+          break;
+        case 'V':
+          qh_option(qh, "FVertex-average", NULL, NULL);
+          qh_appendprint(qh, qh_PRINTaverage);
+          break;
+        case 'x':
+          qh_option(qh, "Fxtremes", NULL, NULL);
+          qh_appendprint(qh, qh_PRINTextremes);
+          break;
+        default:
+          s--;
+          qh_fprintf(qh, qh->ferr, 7012, "qhull warning: unknown 'F' output option %c, rest ignored\n", (int)s[0]);
+          while (*++s && !isspace(*s));
+          break;
+        }
+      }
+      break;
+    case 'G':
+      isgeom= True;
+      qh_appendprint(qh, qh_PRINTgeom);
+      while (*s && !isspace(*s)) {
+        switch (*s++) {
+        case 'a':
+          qh_option(qh, "Gall-points", NULL, NULL);
+          qh->PRINTdots= True;
+          break;
+        case 'c':
+          qh_option(qh, "Gcentrums", NULL, NULL);
+          qh->PRINTcentrums= True;
+          break;
+        case 'h':
+          qh_option(qh, "Gintersections", NULL, NULL);
+          qh->DOintersections= True;
+          break;
+        case 'i':
+          qh_option(qh, "Ginner", NULL, NULL);
+          qh->PRINTinner= True;
+          break;
+        case 'n':
+          qh_option(qh, "Gno-planes", NULL, NULL);
+          qh->PRINTnoplanes= True;
+          break;
+        case 'o':
+          qh_option(qh, "Gouter", NULL, NULL);
+          qh->PRINTouter= True;
+          break;
+        case 'p':
+          qh_option(qh, "Gpoints", NULL, NULL);
+          qh->PRINTcoplanar= True;
+          break;
+        case 'r':
+          qh_option(qh, "Gridges", NULL, NULL);
+          qh->PRINTridges= True;
+          break;
+        case 't':
+          qh_option(qh, "Gtransparent", NULL, NULL);
+          qh->PRINTtransparent= True;
+          break;
+        case 'v':
+          qh_option(qh, "Gvertices", NULL, NULL);
+          qh->PRINTspheres= True;
+          break;
+        case 'D':
+          if (!isdigit(*s))
+            qh_fprintf(qh, qh->ferr, 6035, "qhull input error: missing dimension for option 'GDn'\n");
+          else {
+            if (qh->DROPdim >= 0)
+              qh_fprintf(qh, qh->ferr, 7013, "qhull warning: can only drop one dimension.  Previous 'GD%d' ignored\n",
+                   qh->DROPdim);
+            qh->DROPdim= qh_strtol(s, &s);
+            qh_option(qh, "GDrop-dim", &qh->DROPdim, NULL);
+          }
+          break;
+        default:
+          s--;
+          qh_fprintf(qh, qh->ferr, 7014, "qhull warning: unknown 'G' print option %c, rest ignored\n", (int)s[0]);
+          while (*++s && !isspace(*s));
+          break;
+        }
+      }
+      break;
+    case 'P':
+      while (*s && !isspace(*s)) {
+        switch (*s++) {
+        case 'd': case 'D':  /* see qh_initthresholds() */
+          key= s[-1];
+          i= qh_strtol(s, &s);
+          r= 0;
+          if (*s == ':') {
+            s++;
+            r= qh_strtod(s, &s);
+          }
+          if (key == 'd')
+            qh_option(qh, "Pdrop-facets-dim-less", &i, &r);
+          else
+            qh_option(qh, "PDrop-facets-dim-more", &i, &r);
+          break;
+        case 'g':
+          qh_option(qh, "Pgood-facets", NULL, NULL);
+          qh->PRINTgood= True;
+          break;
+        case 'G':
+          qh_option(qh, "PGood-facet-neighbors", NULL, NULL);
+          qh->PRINTneighbors= True;
+          break;
+        case 'o':
+          qh_option(qh, "Poutput-forced", NULL, NULL);
+          qh->FORCEoutput= True;
+          break;
+        case 'p':
+          qh_option(qh, "Pprecision-ignore", NULL, NULL);
+          qh->PRINTprecision= False;
+          break;
+        case 'A':
+          if (!isdigit(*s))
+            qh_fprintf(qh, qh->ferr, 6036, "qhull input error: missing facet count for keep area option 'PAn'\n");
+          else {
+            qh->KEEParea= qh_strtol(s, &s);
+            qh_option(qh, "PArea-keep", &qh->KEEParea, NULL);
+            qh->GETarea= True;
+          }
+          break;
+        case 'F':
+          if (!isdigit(*s))
+            qh_fprintf(qh, qh->ferr, 6037, "qhull input error: missing facet area for option 'PFn'\n");
+          else {
+            qh->KEEPminArea= qh_strtod(s, &s);
+            qh_option(qh, "PFacet-area-keep", NULL, &qh->KEEPminArea);
+            qh->GETarea= True;
+          }
+          break;
+        case 'M':
+          if (!isdigit(*s))
+            qh_fprintf(qh, qh->ferr, 6038, "qhull input error: missing merge count for option 'PMn'\n");
+          else {
+            qh->KEEPmerge= qh_strtol(s, &s);
+            qh_option(qh, "PMerge-keep", &qh->KEEPmerge, NULL);
+          }
+          break;
+        default:
+          s--;
+          qh_fprintf(qh, qh->ferr, 7015, "qhull warning: unknown 'P' print option %c, rest ignored\n", (int)s[0]);
+          while (*++s && !isspace(*s));
+          break;
+        }
+      }
+      break;
+    case 'Q':
+      lastproject= -1;
+      while (*s && !isspace(*s)) {
+        switch (*s++) {
+        case 'b': case 'B':  /* handled by qh_initthresholds */
+          key= s[-1];
+          if (key == 'b' && *s == 'B') {
+            s++;
+            r= qh_DEFAULTbox;
+            qh->SCALEinput= True;
+            qh_option(qh, "QbBound-unit-box", NULL, &r);
+            break;
+          }
+          if (key == 'b' && *s == 'b') {
+            s++;
+            qh->SCALElast= True;
+            qh_option(qh, "Qbbound-last", NULL, NULL);
+            break;
+          }
+          k= qh_strtol(s, &s);
+          r= 0.0;
+          wasproject= False;
+          if (*s == ':') {
+            s++;
+            if ((r= qh_strtod(s, &s)) == 0.0) {
+              t= s;            /* need true dimension for memory allocation */
+              while (*t && !isspace(*t)) {
+                if (toupper(*t++) == 'B'
+                 && k == qh_strtol(t, &t)
+                 && *t++ == ':'
+                 && qh_strtod(t, &t) == 0.0) {
+                  qh->PROJECTinput++;
+                  trace2((qh, qh->ferr, 2004, "qh_initflags: project dimension %d\n", k));
+                  qh_option(qh, "Qb-project-dim", &k, NULL);
+                  wasproject= True;
+                  lastproject= k;
+                  break;
+                }
+              }
+            }
+          }
+          if (!wasproject) {
+            if (lastproject == k && r == 0.0)
+              lastproject= -1;  /* doesn't catch all possible sequences */
+            else if (key == 'b') {
+              qh->SCALEinput= True;
+              if (r == 0.0)
+                r= -qh_DEFAULTbox;
+              qh_option(qh, "Qbound-dim-low", &k, &r);
+            }else {
+              qh->SCALEinput= True;
+              if (r == 0.0)
+                r= qh_DEFAULTbox;
+              qh_option(qh, "QBound-dim-high", &k, &r);
+            }
+          }
+          break;
+        case 'c':
+          qh_option(qh, "Qcoplanar-keep", NULL, NULL);
+          qh->KEEPcoplanar= True;
+          break;
+        case 'f':
+          qh_option(qh, "Qfurthest-outside", NULL, NULL);
+          qh->BESToutside= True;
+          break;
+        case 'g':
+          qh_option(qh, "Qgood-facets-only", NULL, NULL);
+          qh->ONLYgood= True;
+          break;
+        case 'i':
+          qh_option(qh, "Qinterior-keep", NULL, NULL);
+          qh->KEEPinside= True;
+          break;
+        case 'm':
+          qh_option(qh, "Qmax-outside-only", NULL, NULL);
+          qh->ONLYmax= True;
+          break;
+        case 'r':
+          qh_option(qh, "Qrandom-outside", NULL, NULL);
+          qh->RANDOMoutside= True;
+          break;
+        case 's':
+          qh_option(qh, "Qsearch-initial-simplex", NULL, NULL);
+          qh->ALLpoints= True;
+          break;
+        case 't':
+          qh_option(qh, "Qtriangulate", NULL, NULL);
+          qh->TRIangulate= True;
+          break;
+        case 'T':
+          qh_option(qh, "QTestPoints", NULL, NULL);
+          if (!isdigit(*s))
+            qh_fprintf(qh, qh->ferr, 6039, "qhull input error: missing number of test points for option 'QTn'\n");
+          else {
+            qh->TESTpoints= qh_strtol(s, &s);
+            qh_option(qh, "QTestPoints", &qh->TESTpoints, NULL);
+          }
+          break;
+        case 'u':
+          qh_option(qh, "QupperDelaunay", NULL, NULL);
+          qh->UPPERdelaunay= True;
+          break;
+        case 'v':
+          qh_option(qh, "Qvertex-neighbors-convex", NULL, NULL);
+          qh->TESTvneighbors= True;
+          break;
+        case 'x':
+          qh_option(qh, "Qxact-merge", NULL, NULL);
+          qh->MERGEexact= True;
+          break;
+        case 'z':
+          qh_option(qh, "Qz-infinity-point", NULL, NULL);
+          qh->ATinfinity= True;
+          break;
+        case '0':
+          qh_option(qh, "Q0-no-premerge", NULL, NULL);
+          qh->NOpremerge= True;
+          break;
+        case '1':
+          if (!isdigit(*s)) {
+            qh_option(qh, "Q1-no-angle-sort", NULL, NULL);
+            qh->ANGLEmerge= False;
+            break;
+          }
+          switch (*s++) {
+          case '0':
+            qh_option(qh, "Q10-no-narrow", NULL, NULL);
+            qh->NOnarrow= True;
+            break;
+          case '1':
+            qh_option(qh, "Q11-trinormals Qtriangulate", NULL, NULL);
+            qh->TRInormals= True;
+            qh->TRIangulate= True;
+            break;
+          case '2':
+              qh_option(qh, "Q12-no-wide-dup", NULL, NULL);
+              qh->NOwide= True;
+            break;
+          default:
+            s--;
+            qh_fprintf(qh, qh->ferr, 7016, "qhull warning: unknown 'Q' qhull option 1%c, rest ignored\n", (int)s[0]);
+            while (*++s && !isspace(*s));
+            break;
+          }
+          break;
+        case '2':
+          qh_option(qh, "Q2-no-merge-independent", NULL, NULL);
+          qh->MERGEindependent= False;
+          goto LABELcheckdigit;
+          break; /* no warnings */
+        case '3':
+          qh_option(qh, "Q3-no-merge-vertices", NULL, NULL);
+          qh->MERGEvertices= False;
+        LABELcheckdigit:
+          if (isdigit(*s))
+            qh_fprintf(qh, qh->ferr, 7017, "qhull warning: can not follow '1', '2', or '3' with a digit.  '%c' skipped.\n",
+                     *s++);
+          break;
+        case '4':
+          qh_option(qh, "Q4-avoid-old-into-new", NULL, NULL);
+          qh->AVOIDold= True;
+          break;
+        case '5':
+          qh_option(qh, "Q5-no-check-outer", NULL, NULL);
+          qh->SKIPcheckmax= True;
+          break;
+        case '6':
+          qh_option(qh, "Q6-no-concave-merge", NULL, NULL);
+          qh->SKIPconvex= True;
+          break;
+        case '7':
+          qh_option(qh, "Q7-no-breadth-first", NULL, NULL);
+          qh->VIRTUALmemory= True;
+          break;
+        case '8':
+          qh_option(qh, "Q8-no-near-inside", NULL, NULL);
+          qh->NOnearinside= True;
+          break;
+        case '9':
+          qh_option(qh, "Q9-pick-furthest", NULL, NULL);
+          qh->PICKfurthest= True;
+          break;
+        case 'G':
+          i= qh_strtol(s, &t);
+          if (qh->GOODpoint)
+            qh_fprintf(qh, qh->ferr, 7018, "qhull warning: good point already defined for option 'QGn'.  Ignored\n");
+          else if (s == t)
+            qh_fprintf(qh, qh->ferr, 7019, "qhull warning: missing good point id for option 'QGn'.  Ignored\n");
+          else if (i < 0 || *s == '-') {
+            qh->GOODpoint= i-1;
+            qh_option(qh, "QGood-if-dont-see-point", &i, NULL);
+          }else {
+            qh->GOODpoint= i+1;
+            qh_option(qh, "QGood-if-see-point", &i, NULL);
+          }
+          s= t;
+          break;
+        case 'J':
+          if (!isdigit(*s) && *s != '-')
+            qh->JOGGLEmax= 0.0;
+          else {
+            qh->JOGGLEmax= (realT) qh_strtod(s, &s);
+            qh_option(qh, "QJoggle", NULL, &qh->JOGGLEmax);
+          }
+          break;
+        case 'R':
+          if (!isdigit(*s) && *s != '-')
+            qh_fprintf(qh, qh->ferr, 7020, "qhull warning: missing random seed for option 'QRn'.  Ignored\n");
+          else {
+            qh->ROTATErandom= i= qh_strtol(s, &s);
+            if (i > 0)
+              qh_option(qh, "QRotate-id", &i, NULL );
+            else if (i < -1)
+              qh_option(qh, "QRandom-seed", &i, NULL );
+          }
+          break;
+        case 'V':
+          i= qh_strtol(s, &t);
+          if (qh->GOODvertex)
+            qh_fprintf(qh, qh->ferr, 7021, "qhull warning: good vertex already defined for option 'QVn'.  Ignored\n");
+          else if (s == t)
+            qh_fprintf(qh, qh->ferr, 7022, "qhull warning: no good point id given for option 'QVn'.  Ignored\n");
+          else if (i < 0) {
+            qh->GOODvertex= i - 1;
+            qh_option(qh, "QV-good-facets-not-point", &i, NULL);
+          }else {
+            qh_option(qh, "QV-good-facets-point", &i, NULL);
+            qh->GOODvertex= i + 1;
+          }
+          s= t;
+          break;
+        default:
+          s--;
+          qh_fprintf(qh, qh->ferr, 7023, "qhull warning: unknown 'Q' qhull option %c, rest ignored\n", (int)s[0]);
+          while (*++s && !isspace(*s));
+          break;
+        }
+      }
+      break;
+    case 'T':
+      while (*s && !isspace(*s)) {
+        if (isdigit(*s) || *s == '-')
+          qh->IStracing= qh_strtol(s, &s);
+        else switch (*s++) {
+        case 'a':
+          qh_option(qh, "Tannotate-output", NULL, NULL);
+          qh->ANNOTATEoutput= True;
+          break;
+        case 'c':
+          qh_option(qh, "Tcheck-frequently", NULL, NULL);
+          qh->CHECKfrequently= True;
+          break;
+        case 's':
+          qh_option(qh, "Tstatistics", NULL, NULL);
+          qh->PRINTstatistics= True;
+          break;
+        case 'v':
+          qh_option(qh, "Tverify", NULL, NULL);
+          qh->VERIFYoutput= True;
+          break;
+        case 'z':
+          if (qh->ferr == qh_FILEstderr) {
+            /* The C++ interface captures the output in qh_fprint_qhull() */
+            qh_option(qh, "Tz-stdout", NULL, NULL);
+            qh->USEstdout= True;
+          }else if (!qh->fout)
+            qh_fprintf(qh, qh->ferr, 7024, "qhull warning: output file undefined(stdout).  Option 'Tz' ignored.\n");
+          else {
+            qh_option(qh, "Tz-stdout", NULL, NULL);
+            qh->USEstdout= True;
+            qh->ferr= qh->fout;
+            qh->qhmem.ferr= qh->fout;
+          }
+          break;
+        case 'C':
+          if (!isdigit(*s))
+            qh_fprintf(qh, qh->ferr, 7025, "qhull warning: missing point id for cone for trace option 'TCn'.  Ignored\n");
+          else {
+            i= qh_strtol(s, &s);
+            qh_option(qh, "TCone-stop", &i, NULL);
+            qh->STOPcone= i + 1;
+          }
+          break;
+        case 'F':
+          if (!isdigit(*s))
+            qh_fprintf(qh, qh->ferr, 7026, "qhull warning: missing frequency count for trace option 'TFn'.  Ignored\n");
+          else {
+            qh->REPORTfreq= qh_strtol(s, &s);
+            qh_option(qh, "TFacet-log", &qh->REPORTfreq, NULL);
+            qh->REPORTfreq2= qh->REPORTfreq/2;  /* for tracemerging() */
+          }
+          break;
+        case 'I':
+          if (!isspace(*s))
+            qh_fprintf(qh, qh->ferr, 7027, "qhull warning: missing space between 'TI' and filename, %s\n", s);
+          while (isspace(*s))
+            s++;
+          t= qh_skipfilename(qh, s);
+          {
+            char filename[qh_FILENAMElen];
+
+            qh_copyfilename(qh, filename, (int)sizeof(filename), s, (int)(t-s));   /* WARN64 */
+            s= t;
+            if (!freopen(filename, "r", stdin)) {
+              qh_fprintf(qh, qh->ferr, 6041, "qhull error: could not open file \"%s\".", filename);
+              qh_errexit(qh, qh_ERRinput, NULL, NULL);
+            }else {
+              qh_option(qh, "TInput-file", NULL, NULL);
+              qh_option(qh, filename, NULL, NULL);
+            }
+          }
+          break;
+        case 'O':
+            if (!isspace(*s))
+                qh_fprintf(qh, qh->ferr, 7028, "qhull warning: missing space between 'TO' and filename, %s\n", s);
+            while (isspace(*s))
+                s++;
+            t= qh_skipfilename(qh, s);
+            {
+              char filename[qh_FILENAMElen];
+
+              qh_copyfilename(qh, filename, (int)sizeof(filename), s, (int)(t-s));  /* WARN64 */
+              s= t;
+              if (!qh->fout) {
+                qh_fprintf(qh, qh->ferr, 6266, "qhull input warning: qh.fout was not set by caller.  Cannot use option 'TO' to redirect output.  Ignoring option 'TO'\n");
+              }else if (!freopen(filename, "w", qh->fout)) {
+                qh_fprintf(qh, qh->ferr, 6044, "qhull error: could not open file \"%s\".", filename);
+                qh_errexit(qh, qh_ERRinput, NULL, NULL);
+              }else {
+                qh_option(qh, "TOutput-file", NULL, NULL);
+              qh_option(qh, filename, NULL, NULL);
+            }
+          }
+          break;
+        case 'P':
+          if (!isdigit(*s))
+            qh_fprintf(qh, qh->ferr, 7029, "qhull warning: missing point id for trace option 'TPn'.  Ignored\n");
+          else {
+            qh->TRACEpoint= qh_strtol(s, &s);
+            qh_option(qh, "Trace-point", &qh->TRACEpoint, NULL);
+          }
+          break;
+        case 'M':
+          if (!isdigit(*s))
+            qh_fprintf(qh, qh->ferr, 7030, "qhull warning: missing merge id for trace option 'TMn'.  Ignored\n");
+          else {
+            qh->TRACEmerge= qh_strtol(s, &s);
+            qh_option(qh, "Trace-merge", &qh->TRACEmerge, NULL);
+          }
+          break;
+        case 'R':
+          if (!isdigit(*s))
+            qh_fprintf(qh, qh->ferr, 7031, "qhull warning: missing rerun count for trace option 'TRn'.  Ignored\n");
+          else {
+            qh->RERUN= qh_strtol(s, &s);
+            qh_option(qh, "TRerun", &qh->RERUN, NULL);
+          }
+          break;
+        case 'V':
+          i= qh_strtol(s, &t);
+          if (s == t)
+            qh_fprintf(qh, qh->ferr, 7032, "qhull warning: missing furthest point id for trace option 'TVn'.  Ignored\n");
+          else if (i < 0) {
+            qh->STOPpoint= i - 1;
+            qh_option(qh, "TV-stop-before-point", &i, NULL);
+          }else {
+            qh->STOPpoint= i + 1;
+            qh_option(qh, "TV-stop-after-point", &i, NULL);
+          }
+          s= t;
+          break;
+        case 'W':
+          if (!isdigit(*s))
+            qh_fprintf(qh, qh->ferr, 7033, "qhull warning: missing max width for trace option 'TWn'.  Ignored\n");
+          else {
+            qh->TRACEdist= (realT) qh_strtod(s, &s);
+            qh_option(qh, "TWide-trace", NULL, &qh->TRACEdist);
+          }
+          break;
+        default:
+          s--;
+          qh_fprintf(qh, qh->ferr, 7034, "qhull warning: unknown 'T' trace option %c, rest ignored\n", (int)s[0]);
+          while (*++s && !isspace(*s));
+          break;
+        }
+      }
+      break;
+    default:
+      qh_fprintf(qh, qh->ferr, 7035, "qhull warning: unknown flag %c(%x)\n", (int)s[-1],
+               (int)s[-1]);
+      break;
+    }
+    if (s-1 == prev_s && *s && !isspace(*s)) {
+      qh_fprintf(qh, qh->ferr, 7036, "qhull warning: missing space after flag %c(%x); reserved for menu. Skipped.\n",
+               (int)*prev_s, (int)*prev_s);
+      while (*s && !isspace(*s))
+        s++;
+    }
+  }
+  if (qh->STOPcone && qh->JOGGLEmax < REALmax/2)
+    qh_fprintf(qh, qh->ferr, 7078, "qhull warning: 'TCn' (stopCone) ignored when used with 'QJn' (joggle)\n");
+  if (isgeom && !qh->FORCEoutput && qh->PRINTout[1])
+    qh_fprintf(qh, qh->ferr, 7037, "qhull warning: additional output formats are not compatible with Geomview\n");
+  /* set derived values in qh_initqhull_globals */
+} /* initflags */
+
+
+/*-<a                             href="qh-globa_r.htm#TOC"
+  >-------------------------------</a><a name="initqhull_buffers">-</a>
+
+  qh_initqhull_buffers(qh)
+    initialize global memory buffers
+
+  notes:
+    must match qh_freebuffers()
+*/
+void qh_initqhull_buffers(qhT *qh) {
+  int k;
+
+  qh->TEMPsize= (qh->qhmem.LASTsize - sizeof(setT))/SETelemsize;
+  if (qh->TEMPsize <= 0 || qh->TEMPsize > qh->qhmem.LASTsize)
+    qh->TEMPsize= 8;  /* e.g., if qh_NOmem */
+  qh->other_points= qh_setnew(qh, qh->TEMPsize);
+  qh->del_vertices= qh_setnew(qh, qh->TEMPsize);
+  qh->coplanarfacetset= qh_setnew(qh, qh->TEMPsize);
+  qh->NEARzero= (realT *)qh_memalloc(qh, qh->hull_dim * sizeof(realT));
+  qh->lower_threshold= (realT *)qh_memalloc(qh, (qh->input_dim+1) * sizeof(realT));
+  qh->upper_threshold= (realT *)qh_memalloc(qh, (qh->input_dim+1) * sizeof(realT));
+  qh->lower_bound= (realT *)qh_memalloc(qh, (qh->input_dim+1) * sizeof(realT));
+  qh->upper_bound= (realT *)qh_memalloc(qh, (qh->input_dim+1) * sizeof(realT));
+  for (k=qh->input_dim+1; k--; ) {  /* duplicated in qh_initqhull_buffers and qh_clear_outputflags */
+    qh->lower_threshold[k]= -REALmax;
+    qh->upper_threshold[k]= REALmax;
+    qh->lower_bound[k]= -REALmax;
+    qh->upper_bound[k]= REALmax;
+  }
+  qh->gm_matrix= (coordT *)qh_memalloc(qh, (qh->hull_dim+1) * qh->hull_dim * sizeof(coordT));
+  qh->gm_row= (coordT **)qh_memalloc(qh, (qh->hull_dim+1) * sizeof(coordT *));
+} /* initqhull_buffers */
+
+/*-<a                             href="qh-globa_r.htm#TOC"
+  >-------------------------------</a><a name="initqhull_globals">-</a>
+
+  qh_initqhull_globals(qh, points, numpoints, dim, ismalloc )
+    initialize globals
+    if ismalloc
+      points were malloc'd and qhull should free at end
+
+  returns:
+    sets qh.first_point, num_points, input_dim, hull_dim and others
+    seeds random number generator (seed=1 if tracing)
+    modifies qh.hull_dim if ((qh.DELAUNAY and qh.PROJECTdelaunay) or qh.PROJECTinput)
+    adjust user flags as needed
+    also checks DIM3 dependencies and constants
+
+  notes:
+    do not use qh_point() since an input transformation may move them elsewhere
+
+  see:
+    qh_initqhull_start() sets default values for non-zero globals
+
+  design:
+    initialize points array from input arguments
+    test for qh.ZEROcentrum
+      (i.e., use opposite vertex instead of cetrum for convexity testing)
+    initialize qh.CENTERtype, qh.normal_size,
+      qh.center_size, qh.TRACEpoint/level,
+    initialize and test random numbers
+    qh_initqhull_outputflags() -- adjust and test output flags
+*/
+void qh_initqhull_globals(qhT *qh, coordT *points, int numpoints, int dim, boolT ismalloc) {
+  int seed, pointsneeded, extra= 0, i, randi, k;
+  realT randr;
+  realT factorial;
+
+  time_t timedata;
+
+  trace0((qh, qh->ferr, 13, "qh_initqhull_globals: for %s | %s\n", qh->rbox_command,
+      qh->qhull_command));
+  qh->POINTSmalloc= ismalloc;
+  qh->first_point= points;
+  qh->num_points= numpoints;
+  qh->hull_dim= qh->input_dim= dim;
+  if (!qh->NOpremerge && !qh->MERGEexact && !qh->PREmerge && qh->JOGGLEmax > REALmax/2) {
+    qh->MERGING= True;
+    if (qh->hull_dim <= 4) {
+      qh->PREmerge= True;
+      qh_option(qh, "_pre-merge", NULL, NULL);
+    }else {
+      qh->MERGEexact= True;
+      qh_option(qh, "Qxact_merge", NULL, NULL);
+    }
+  }else if (qh->MERGEexact)
+    qh->MERGING= True;
+  if (!qh->NOpremerge && qh->JOGGLEmax > REALmax/2) {
+#ifdef qh_NOmerge
+    qh->JOGGLEmax= 0.0;
+#endif
+  }
+  if (qh->TRIangulate && qh->JOGGLEmax < REALmax/2 && qh->PRINTprecision)
+    qh_fprintf(qh, qh->ferr, 7038, "qhull warning: joggle('QJ') always produces simplicial output.  Triangulated output('Qt') does nothing.\n");
+  if (qh->JOGGLEmax < REALmax/2 && qh->DELAUNAY && !qh->SCALEinput && !qh->SCALElast) {
+    qh->SCALElast= True;
+    qh_option(qh, "Qbbound-last-qj", NULL, NULL);
+  }
+  if (qh->MERGING && !qh->POSTmerge && qh->premerge_cos > REALmax/2
+  && qh->premerge_centrum == 0) {
+    qh->ZEROcentrum= True;
+    qh->ZEROall_ok= True;
+    qh_option(qh, "_zero-centrum", NULL, NULL);
+  }
+  if (qh->JOGGLEmax < REALmax/2 && REALepsilon > 2e-8 && qh->PRINTprecision)
+    qh_fprintf(qh, qh->ferr, 7039, "qhull warning: real epsilon, %2.2g, is probably too large for joggle('QJn')\nRecompile with double precision reals(see user.h).\n",
+          REALepsilon);
+#ifdef qh_NOmerge
+  if (qh->MERGING) {
+    qh_fprintf(qh, qh->ferr, 6045, "qhull input error: merging not installed(qh_NOmerge + 'Qx', 'Cn' or 'An')\n");
+    qh_errexit(qh, qh_ERRinput, NULL, NULL);
+  }
+#endif
+  if (qh->DELAUNAY && qh->KEEPcoplanar && !qh->KEEPinside) {
+    qh->KEEPinside= True;
+    qh_option(qh, "Qinterior-keep", NULL, NULL);
+  }
+  if (qh->DELAUNAY && qh->HALFspace) {
+    qh_fprintf(qh, qh->ferr, 6046, "qhull input error: can not use Delaunay('d') or Voronoi('v') with halfspace intersection('H')\n");
+    qh_errexit(qh, qh_ERRinput, NULL, NULL);
+  }
+  if (!qh->DELAUNAY && (qh->UPPERdelaunay || qh->ATinfinity)) {
+    qh_fprintf(qh, qh->ferr, 6047, "qhull input error: use upper-Delaunay('Qu') or infinity-point('Qz') with Delaunay('d') or Voronoi('v')\n");
+    qh_errexit(qh, qh_ERRinput, NULL, NULL);
+  }
+  if (qh->UPPERdelaunay && qh->ATinfinity) {
+    qh_fprintf(qh, qh->ferr, 6048, "qhull input error: can not use infinity-point('Qz') with upper-Delaunay('Qu')\n");
+    qh_errexit(qh, qh_ERRinput, NULL, NULL);
+  }
+  if (qh->SCALElast && !qh->DELAUNAY && qh->PRINTprecision)
+    qh_fprintf(qh, qh->ferr, 7040, "qhull input warning: option 'Qbb' (scale-last-coordinate) is normally used with 'd' or 'v'\n");
+  qh->DOcheckmax= (!qh->SKIPcheckmax && qh->MERGING );
+  qh->KEEPnearinside= (qh->DOcheckmax && !(qh->KEEPinside && qh->KEEPcoplanar)
+                          && !qh->NOnearinside);
+  if (qh->MERGING)
+    qh->CENTERtype= qh_AScentrum;
+  else if (qh->VORONOI)
+    qh->CENTERtype= qh_ASvoronoi;
+  if (qh->TESTvneighbors && !qh->MERGING) {
+    qh_fprintf(qh, qh->ferr, 6049, "qhull input error: test vertex neighbors('Qv') needs a merge option\n");
+    qh_errexit(qh, qh_ERRinput, NULL ,NULL);
+  }
+  if (qh->PROJECTinput || (qh->DELAUNAY && qh->PROJECTdelaunay)) {
+    qh->hull_dim -= qh->PROJECTinput;
+    if (qh->DELAUNAY) {
+      qh->hull_dim++;
+      if (qh->ATinfinity)
+        extra= 1;
+    }
+  }
+  if (qh->hull_dim <= 1) {
+    qh_fprintf(qh, qh->ferr, 6050, "qhull error: dimension %d must be > 1\n", qh->hull_dim);
+    qh_errexit(qh, qh_ERRinput, NULL, NULL);
+  }
+  for (k=2, factorial=1.0; k < qh->hull_dim; k++)
+    factorial *= k;
+  qh->AREAfactor= 1.0 / factorial;
+  trace2((qh, qh->ferr, 2005, "qh_initqhull_globals: initialize globals.  dim %d numpoints %d malloc? %d projected %d to hull_dim %d\n",
+        dim, numpoints, ismalloc, qh->PROJECTinput, qh->hull_dim));
+  qh->normal_size= qh->hull_dim * sizeof(coordT);
+  qh->center_size= qh->normal_size - sizeof(coordT);
+  pointsneeded= qh->hull_dim+1;
+  if (qh->hull_dim > qh_DIMmergeVertex) {
+    qh->MERGEvertices= False;
+    qh_option(qh, "Q3-no-merge-vertices-dim-high", NULL, NULL);
+  }
+  if (qh->GOODpoint)
+    pointsneeded++;
+#ifdef qh_NOtrace
+  if (qh->IStracing) {
+    qh_fprintf(qh, qh->ferr, 6051, "qhull input error: tracing is not installed(qh_NOtrace in user.h)");
+    qh_errexit(qh, qh_ERRqhull, NULL, NULL);
+  }
+#endif
+  if (qh->RERUN > 1) {
+    qh->TRACElastrun= qh->IStracing; /* qh_build_withrestart duplicates next conditional */
+    if (qh->IStracing != -1)
+      qh->IStracing= 0;
+  }else if (qh->TRACEpoint != qh_IDunknown || qh->TRACEdist < REALmax/2 || qh->TRACEmerge) {
+    qh->TRACElevel= (qh->IStracing? qh->IStracing : 3);
+    qh->IStracing= 0;
+  }
+  if (qh->ROTATErandom == 0 || qh->ROTATErandom == -1) {
+    seed= (int)time(&timedata);
+    if (qh->ROTATErandom  == -1) {
+      seed= -seed;
+      qh_option(qh, "QRandom-seed", &seed, NULL );
+    }else
+      qh_option(qh, "QRotate-random", &seed, NULL);
+    qh->ROTATErandom= seed;
+  }
+  seed= qh->ROTATErandom;
+  if (seed == INT_MIN)    /* default value */
+    seed= 1;
+  else if (seed < 0)
+    seed= -seed;
+  qh_RANDOMseed_(qh, seed);
+  randr= 0.0;
+  for (i=1000; i--; ) {
+    randi= qh_RANDOMint;
+    randr += randi;
+    if (randi > qh_RANDOMmax) {
+      qh_fprintf(qh, qh->ferr, 8036, "\
+qhull configuration error (qh_RANDOMmax in user.h):\n\
+   random integer %d > qh_RANDOMmax(qh, %.8g)\n",
+               randi, qh_RANDOMmax);
+      qh_errexit(qh, qh_ERRinput, NULL, NULL);
+    }
+  }
+  qh_RANDOMseed_(qh, seed);
+  randr = randr/1000;
+  if (randr < qh_RANDOMmax * 0.1
+  || randr > qh_RANDOMmax * 0.9)
+    qh_fprintf(qh, qh->ferr, 8037, "\
+qhull configuration warning (qh_RANDOMmax in user.h):\n\
+   average of 1000 random integers (%.2g) is much different than expected (%.2g).\n\
+   Is qh_RANDOMmax (%.2g) wrong?\n",
+             randr, qh_RANDOMmax * 0.5, qh_RANDOMmax);
+  qh->RANDOMa= 2.0 * qh->RANDOMfactor/qh_RANDOMmax;
+  qh->RANDOMb= 1.0 - qh->RANDOMfactor;
+  if (qh_HASHfactor < 1.1) {
+    qh_fprintf(qh, qh->ferr, 6052, "qhull internal error (qh_initqhull_globals): qh_HASHfactor %d must be at least 1.1.  Qhull uses linear hash probing\n",
+      qh_HASHfactor);
+    qh_errexit(qh, qh_ERRqhull, NULL, NULL);
+  }
+  if (numpoints+extra < pointsneeded) {
+    qh_fprintf(qh, qh->ferr, 6214, "qhull input error: not enough points(%d) to construct initial simplex (need %d)\n",
+            numpoints, pointsneeded);
+    qh_errexit(qh, qh_ERRinput, NULL, NULL);
+  }
+  qh_initqhull_outputflags(qh);
+} /* initqhull_globals */
+
+/*-<a                             href="qh-globa_r.htm#TOC"
+  >-------------------------------</a><a name="initqhull_mem">-</a>
+
+  qh_initqhull_mem(qh, )
+    initialize mem_r.c for qhull
+    qh.hull_dim and qh.normal_size determine some of the allocation sizes
+    if qh.MERGING,
+      includes ridgeT
+    calls qh_user_memsizes(qh) to add up to 10 additional sizes for quick allocation
+      (see numsizes below)
+
+  returns:
+    mem_r.c already for qh_memalloc/qh_memfree (errors if called beforehand)
+
+  notes:
+    qh_produceoutput() prints memsizes
+
+*/
+void qh_initqhull_mem(qhT *qh) {
+  int numsizes;
+  int i;
+
+  numsizes= 8+10;
+  qh_meminitbuffers(qh, qh->IStracing, qh_MEMalign, numsizes,
+                     qh_MEMbufsize, qh_MEMinitbuf);
+  qh_memsize(qh, (int)sizeof(vertexT));
+  if (qh->MERGING) {
+    qh_memsize(qh, (int)sizeof(ridgeT));
+    qh_memsize(qh, (int)sizeof(mergeT));
+  }
+  qh_memsize(qh, (int)sizeof(facetT));
+  i= sizeof(setT) + (qh->hull_dim - 1) * SETelemsize;  /* ridge.vertices */
+  qh_memsize(qh, i);
+  qh_memsize(qh, qh->normal_size);        /* normal */
+  i += SETelemsize;                 /* facet.vertices, .ridges, .neighbors */
+  qh_memsize(qh, i);
+  qh_user_memsizes(qh);
+  qh_memsetup(qh);
+} /* initqhull_mem */
+
+/*-<a                             href="qh-globa_r.htm#TOC"
+  >-------------------------------</a><a name="initqhull_outputflags">-</a>
+
+  qh_initqhull_outputflags
+    initialize flags concerned with output
+
+  returns:
+    adjust user flags as needed
+
+  see:
+    qh_clear_outputflags() resets the flags
+
+  design:
+    test for qh.PRINTgood (i.e., only print 'good' facets)
+    check for conflicting print output options
+*/
+void qh_initqhull_outputflags(qhT *qh) {
+  boolT printgeom= False, printmath= False, printcoplanar= False;
+  int i;
+
+  trace3((qh, qh->ferr, 3024, "qh_initqhull_outputflags: %s\n", qh->qhull_command));
+  if (!(qh->PRINTgood || qh->PRINTneighbors)) {
+    if (qh->KEEParea || qh->KEEPminArea < REALmax/2 || qh->KEEPmerge || qh->DELAUNAY
+        || (!qh->ONLYgood && (qh->GOODvertex || qh->GOODpoint))) {
+      qh->PRINTgood= True;
+      qh_option(qh, "Pgood", NULL, NULL);
+    }
+  }
+  if (qh->PRINTtransparent) {
+    if (qh->hull_dim != 4 || !qh->DELAUNAY || qh->VORONOI || qh->DROPdim >= 0) {
+      qh_fprintf(qh, qh->ferr, 6215, "qhull input error: transparent Delaunay('Gt') needs 3-d Delaunay('d') w/o 'GDn'\n");
+      qh_errexit(qh, qh_ERRinput, NULL, NULL);
+    }
+    qh->DROPdim = 3;
+    qh->PRINTridges = True;
+  }
+  for (i=qh_PRINTEND; i--; ) {
+    if (qh->PRINTout[i] == qh_PRINTgeom)
+      printgeom= True;
+    else if (qh->PRINTout[i] == qh_PRINTmathematica || qh->PRINTout[i] == qh_PRINTmaple)
+      printmath= True;
+    else if (qh->PRINTout[i] == qh_PRINTcoplanars)
+      printcoplanar= True;
+    else if (qh->PRINTout[i] == qh_PRINTpointnearest)
+      printcoplanar= True;
+    else if (qh->PRINTout[i] == qh_PRINTpointintersect && !qh->HALFspace) {
+      qh_fprintf(qh, qh->ferr, 6053, "qhull input error: option 'Fp' is only used for \nhalfspace intersection('Hn,n,n').\n");
+      qh_errexit(qh, qh_ERRinput, NULL, NULL);
+    }else if (qh->PRINTout[i] == qh_PRINTtriangles && (qh->HALFspace || qh->VORONOI)) {
+      qh_fprintf(qh, qh->ferr, 6054, "qhull input error: option 'Ft' is not available for Voronoi vertices or halfspace intersection\n");
+      qh_errexit(qh, qh_ERRinput, NULL, NULL);
+    }else if (qh->PRINTout[i] == qh_PRINTcentrums && qh->VORONOI) {
+      qh_fprintf(qh, qh->ferr, 6055, "qhull input error: option 'FC' is not available for Voronoi vertices('v')\n");
+      qh_errexit(qh, qh_ERRinput, NULL, NULL);
+    }else if (qh->PRINTout[i] == qh_PRINTvertices) {
+      if (qh->VORONOI)
+        qh_option(qh, "Fvoronoi", NULL, NULL);
+      else
+        qh_option(qh, "Fvertices", NULL, NULL);
+    }
+  }
+  if (printcoplanar && qh->DELAUNAY && qh->JOGGLEmax < REALmax/2) {
+    if (qh->PRINTprecision)
+      qh_fprintf(qh, qh->ferr, 7041, "qhull input warning: 'QJ' (joggle) will usually prevent coincident input sites for options 'Fc' and 'FP'\n");
+  }
+  if (printmath && (qh->hull_dim > 3 || qh->VORONOI)) {
+    qh_fprintf(qh, qh->ferr, 6056, "qhull input error: Mathematica and Maple output is only available for 2-d and 3-d convex hulls and 2-d Delaunay triangulations\n");
+    qh_errexit(qh, qh_ERRinput, NULL, NULL);
+  }
+  if (printgeom) {
+    if (qh->hull_dim > 4) {
+      qh_fprintf(qh, qh->ferr, 6057, "qhull input error: Geomview output is only available for 2-d, 3-d and 4-d\n");
+      qh_errexit(qh, qh_ERRinput, NULL, NULL);
+    }
+    if (qh->PRINTnoplanes && !(qh->PRINTcoplanar + qh->PRINTcentrums
+     + qh->PRINTdots + qh->PRINTspheres + qh->DOintersections + qh->PRINTridges)) {
+      qh_fprintf(qh, qh->ferr, 6058, "qhull input error: no output specified for Geomview\n");
+      qh_errexit(qh, qh_ERRinput, NULL, NULL);
+    }
+    if (qh->VORONOI && (qh->hull_dim > 3 || qh->DROPdim >= 0)) {
+      qh_fprintf(qh, qh->ferr, 6059, "qhull input error: Geomview output for Voronoi diagrams only for 2-d\n");
+      qh_errexit(qh, qh_ERRinput, NULL, NULL);
+    }
+    /* can not warn about furthest-site Geomview output: no lower_threshold */
+    if (qh->hull_dim == 4 && qh->DROPdim == -1 &&
+        (qh->PRINTcoplanar || qh->PRINTspheres || qh->PRINTcentrums)) {
+      qh_fprintf(qh, qh->ferr, 7042, "qhull input warning: coplanars, vertices, and centrums output not\n\
+available for 4-d output(ignored).  Could use 'GDn' instead.\n");
+      qh->PRINTcoplanar= qh->PRINTspheres= qh->PRINTcentrums= False;
+    }
+  }
+  if (!qh->KEEPcoplanar && !qh->KEEPinside && !qh->ONLYgood) {
+    if ((qh->PRINTcoplanar && qh->PRINTspheres) || printcoplanar) {
+      if (qh->QHULLfinished) {
+        qh_fprintf(qh, qh->ferr, 7072, "qhull output warning: ignoring coplanar points, option 'Qc' was not set for the first run of qhull.\n");
+      }else {
+        qh->KEEPcoplanar = True;
+        qh_option(qh, "Qcoplanar", NULL, NULL);
+      }
+    }
+  }
+  qh->PRINTdim= qh->hull_dim;
+  if (qh->DROPdim >=0) {    /* after Geomview checks */
+    if (qh->DROPdim < qh->hull_dim) {
+      qh->PRINTdim--;
+      if (!printgeom || qh->hull_dim < 3)
+        qh_fprintf(qh, qh->ferr, 7043, "qhull input warning: drop dimension 'GD%d' is only available for 3-d/4-d Geomview\n", qh->DROPdim);
+    }else
+      qh->DROPdim= -1;
+  }else if (qh->VORONOI) {
+    qh->DROPdim= qh->hull_dim-1;
+    qh->PRINTdim= qh->hull_dim-1;
+  }
+} /* qh_initqhull_outputflags */
+
+/*-<a                             href="qh-globa_r.htm#TOC"
+  >-------------------------------</a><a name="initqhull_start">-</a>
+
+  qh_initqhull_start(qh, infile, outfile, errfile )
+    allocate memory if needed and call qh_initqhull_start2()
+*/
+void qh_initqhull_start(qhT *qh, FILE *infile, FILE *outfile, FILE *errfile) {
+
+  qh_initstatistics(qh);
+  qh_initqhull_start2(qh, infile, outfile, errfile);
+} /* initqhull_start */
+
+/*-<a                             href="qh-globa_r.htm#TOC"
+  >-------------------------------</a><a name="initqhull_start2">-</a>
+
+  qh_initqhull_start2(qh, infile, outfile, errfile )
+    start initialization of qhull
+    initialize statistics, stdio, default values for global variables
+    assumes qh is allocated
+  notes:
+    report errors elsewhere, error handling and g_qhull_output [Qhull.cpp, QhullQh()] not in initialized
+  see:
+    qh_maxmin() determines the precision constants
+    qh_freeqhull()
+*/
+void qh_initqhull_start2(qhT *qh, FILE *infile, FILE *outfile, FILE *errfile) {
+  time_t timedata;
+  int seed;
+
+  qh_CPUclock; /* start the clock(for qh_clock).  One-shot. */
+  /* memset is the same in qh_freeqhull() and qh_initqhull_start2() */
+  memset((char *)qh, 0, sizeof(qhT)-sizeof(qhmemT)-sizeof(qhstatT));   /* every field is 0, FALSE, NULL */
+  qh->NOerrexit= True;
+  qh->ANGLEmerge= True;
+  qh->DROPdim= -1;
+  qh->ferr= errfile;
+  qh->fin= infile;
+  qh->fout= outfile;
+  qh->furthest_id= qh_IDunknown;
+  qh->JOGGLEmax= REALmax;
+  qh->KEEPminArea = REALmax;
+  qh->last_low= REALmax;
+  qh->last_high= REALmax;
+  qh->last_newhigh= REALmax;
+  qh->last_random= 1;
+  qh->max_outside= 0.0;
+  qh->max_vertex= 0.0;
+  qh->MAXabs_coord= 0.0;
+  qh->MAXsumcoord= 0.0;
+  qh->MAXwidth= -REALmax;
+  qh->MERGEindependent= True;
+  qh->MINdenom_1= fmax_(1.0/REALmax, REALmin); /* used by qh_scalepoints */
+  qh->MINoutside= 0.0;
+  qh->MINvisible= REALmax;
+  qh->MAXcoplanar= REALmax;
+  qh->outside_err= REALmax;
+  qh->premerge_centrum= 0.0;
+  qh->premerge_cos= REALmax;
+  qh->PRINTprecision= True;
+  qh->PRINTradius= 0.0;
+  qh->postmerge_cos= REALmax;
+  qh->postmerge_centrum= 0.0;
+  qh->ROTATErandom= INT_MIN;
+  qh->MERGEvertices= True;
+  qh->totarea= 0.0;
+  qh->totvol= 0.0;
+  qh->TRACEdist= REALmax;
+  qh->TRACEpoint= qh_IDunknown; /* recompile or use 'TPn' */
+  qh->tracefacet_id= UINT_MAX;  /* recompile to trace a facet */
+  qh->tracevertex_id= UINT_MAX; /* recompile to trace a vertex */
+  seed= (int)time(&timedata);
+  qh_RANDOMseed_(qh, seed);
+  qh->run_id= qh_RANDOMint;
+  if(!qh->run_id)
+      qh->run_id++;  /* guarantee non-zero */
+  qh_option(qh, "run-id", &qh->run_id, NULL);
+  strcat(qh->qhull, "qhull");
+} /* initqhull_start2 */
+
+/*-<a                             href="qh-globa_r.htm#TOC"
+  >-------------------------------</a><a name="initthresholds">-</a>
+
+  qh_initthresholds(qh, commandString )
+    set thresholds for printing and scaling from commandString
+
+  returns:
+    sets qh.GOODthreshold or qh.SPLITthreshold if 'Pd0D1' used
+
+  see:
+    qh_initflags(), 'Qbk' 'QBk' 'Pdk' and 'PDk'
+    qh_inthresholds()
+
+  design:
+    for each 'Pdn' or 'PDn' option
+      check syntax
+      set qh.lower_threshold or qh.upper_threshold
+    set qh.GOODthreshold if an unbounded threshold is used
+    set qh.SPLITthreshold if a bounded threshold is used
+*/
+void qh_initthresholds(qhT *qh, char *command) {
+  realT value;
+  int idx, maxdim, k;
+  char *s= command; /* non-const due to strtol */
+  char key;
+
+  maxdim= qh->input_dim;
+  if (qh->DELAUNAY && (qh->PROJECTdelaunay || qh->PROJECTinput))
+    maxdim++;
+  while (*s) {
+    if (*s == '-')
+      s++;
+    if (*s == 'P') {
+      s++;
+      while (*s && !isspace(key= *s++)) {
+        if (key == 'd' || key == 'D') {
+          if (!isdigit(*s)) {
+            qh_fprintf(qh, qh->ferr, 7044, "qhull warning: no dimension given for Print option '%c' at: %s.  Ignored\n",
+                    key, s-1);
+            continue;
+          }
+          idx= qh_strtol(s, &s);
+          if (idx >= qh->hull_dim) {
+            qh_fprintf(qh, qh->ferr, 7045, "qhull warning: dimension %d for Print option '%c' is >= %d.  Ignored\n",
+                idx, key, qh->hull_dim);
+            continue;
+          }
+          if (*s == ':') {
+            s++;
+            value= qh_strtod(s, &s);
+            if (fabs((double)value) > 1.0) {
+              qh_fprintf(qh, qh->ferr, 7046, "qhull warning: value %2.4g for Print option %c is > +1 or < -1.  Ignored\n",
+                      value, key);
+              continue;
+            }
+          }else
+            value= 0.0;
+          if (key == 'd')
+            qh->lower_threshold[idx]= value;
+          else
+            qh->upper_threshold[idx]= value;
+        }
+      }
+    }else if (*s == 'Q') {
+      s++;
+      while (*s && !isspace(key= *s++)) {
+        if (key == 'b' && *s == 'B') {
+          s++;
+          for (k=maxdim; k--; ) {
+            qh->lower_bound[k]= -qh_DEFAULTbox;
+            qh->upper_bound[k]= qh_DEFAULTbox;
+          }
+        }else if (key == 'b' && *s == 'b')
+          s++;
+        else if (key == 'b' || key == 'B') {
+          if (!isdigit(*s)) {
+            qh_fprintf(qh, qh->ferr, 7047, "qhull warning: no dimension given for Qhull option %c.  Ignored\n",
+                    key);
+            continue;
+          }
+          idx= qh_strtol(s, &s);
+          if (idx >= maxdim) {
+            qh_fprintf(qh, qh->ferr, 7048, "qhull warning: dimension %d for Qhull option %c is >= %d.  Ignored\n",
+                idx, key, maxdim);
+            continue;
+          }
+          if (*s == ':') {
+            s++;
+            value= qh_strtod(s, &s);
+          }else if (key == 'b')
+            value= -qh_DEFAULTbox;
+          else
+            value= qh_DEFAULTbox;
+          if (key == 'b')
+            qh->lower_bound[idx]= value;
+          else
+            qh->upper_bound[idx]= value;
+        }
+      }
+    }else {
+      while (*s && !isspace(*s))
+        s++;
+    }
+    while (isspace(*s))
+      s++;
+  }
+  for (k=qh->hull_dim; k--; ) {
+    if (qh->lower_threshold[k] > -REALmax/2) {
+      qh->GOODthreshold= True;
+      if (qh->upper_threshold[k] < REALmax/2) {
+        qh->SPLITthresholds= True;
+        qh->GOODthreshold= False;
+        break;
+      }
+    }else if (qh->upper_threshold[k] < REALmax/2)
+      qh->GOODthreshold= True;
+  }
+} /* initthresholds */
+
+/*-<a                             href="qh-globa_r.htm#TOC"
+  >-------------------------------</a><a name="lib_check">-</a>
+
+  qh_lib_check( qhullLibraryType, qhTsize, vertexTsize, ridgeTsize, facetTsize, setTsize, qhmemTsize )
+    Report error if library does not agree with caller
+
+  notes:
+    NOerrors -- qh_lib_check can not call qh_errexit()
+*/
+void qh_lib_check(int qhullLibraryType, int qhTsize, int vertexTsize, int ridgeTsize, int facetTsize, int setTsize, int qhmemTsize) {
+    boolT iserror= False;
+
+#if defined(_MSC_VER) && defined(_DEBUG) && defined(QHULL_CRTDBG)  /* user_r.h */
+    // _CrtSetBreakAlloc(744);  /* Break at memalloc {744}, or 'watch' _crtBreakAlloc */
+    _CrtSetDbgFlag( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_DELAY_FREE_MEM_DF | _CRTDBG_LEAK_CHECK_DF | _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG) );
+    _CrtSetReportMode( _CRT_ERROR, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG );
+    _CrtSetReportFile( _CRT_ERROR, _CRTDBG_FILE_STDERR );
+    _CrtSetReportMode( _CRT_WARN, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG );
+    _CrtSetReportFile( _CRT_WARN, _CRTDBG_FILE_STDERR );
+    _CrtSetReportMode( _CRT_ASSERT, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG );
+    _CrtSetReportFile( _CRT_ASSERT, _CRTDBG_FILE_STDERR );
+#endif
+
+    if (qhullLibraryType==QHULL_NON_REENTRANT) { /* 0 */
+        qh_fprintf_stderr(6257, "qh_lib_check: Incorrect qhull library called.  Caller uses non-reentrant Qhull with a static qhT.  Library is reentrant.\n");
+        iserror= True;
+    }else if (qhullLibraryType==QHULL_QH_POINTER) { /* 1 */
+        qh_fprintf_stderr(6258, "qh_lib_check: Incorrect qhull library called.  Caller uses non-reentrant Qhull with a dynamic qhT via qh_QHpointer.  Library is reentrant.\n");
+        iserror= True;
+    }else if (qhullLibraryType!=QHULL_REENTRANT) { /* 2 */
+        qh_fprintf_stderr(6262, "qh_lib_check: Expecting qhullLibraryType QHULL_NON_REENTRANT(0), QHULL_QH_POINTER(1), or QHULL_REENTRANT(2).  Got %d\n", qhullLibraryType);
+        iserror= True;
+    }
+    if (qhTsize != sizeof(qhT)) {
+        qh_fprintf_stderr(6249, "qh_lib_check: Incorrect qhull library called.  Size of qhT for caller is %d, but for library is %d.\n", qhTsize, sizeof(qhT));
+        iserror= True;
+    }
+    if (vertexTsize != sizeof(vertexT)) {
+        qh_fprintf_stderr(6250, "qh_lib_check: Incorrect qhull library called.  Size of vertexT for caller is %d, but for library is %d.\n", vertexTsize, sizeof(vertexT));
+        iserror= True;
+    }
+    if (ridgeTsize != sizeof(ridgeT)) {
+        qh_fprintf_stderr(6251, "qh_lib_check: Incorrect qhull library called.  Size of ridgeT for caller is %d, but for library is %d.\n", ridgeTsize, sizeof(ridgeT));
+        iserror= True;
+    }
+    if (facetTsize != sizeof(facetT)) {
+        qh_fprintf_stderr(6252, "qh_lib_check: Incorrect qhull library called.  Size of facetT for caller is %d, but for library is %d.\n", facetTsize, sizeof(facetT));
+        iserror= True;
+    }
+    if (setTsize && setTsize != sizeof(setT)) {
+        qh_fprintf_stderr(6253, "qh_lib_check: Incorrect qhull library called.  Size of setT for caller is %d, but for library is %d.\n", setTsize, sizeof(setT));
+        iserror= True;
+    }
+    if (qhmemTsize && qhmemTsize != sizeof(qhmemT)) {
+        qh_fprintf_stderr(6254, "qh_lib_check: Incorrect qhull library called.  Size of qhmemT for caller is %d, but for library is %d.\n", qhmemTsize, sizeof(qhmemT));
+        iserror= True;
+    }
+    if (iserror) {
+        qh_fprintf_stderr(6259, "qh_lib_check: Cannot continue.  Library '%s' is reentrant (e.g., qhull_r.so)\n", qh_version2);
+        qh_exit(qh_ERRqhull);  /* can not use qh_errexit() */
+    }
+} /* lib_check */
+
+/*-<a                             href="qh-globa_r.htm#TOC"
+  >-------------------------------</a><a name="option">-</a>
+
+  qh_option(qh, option, intVal, realVal )
+    add an option description to qh.qhull_options
+
+  notes:
+    NOerrors -- qh_option can not call qh_errexit() [qh_initqhull_start2]
+    will be printed with statistics ('Ts') and errors
+    strlen(option) < 40
+*/
+void qh_option(qhT *qh, const char *option, int *i, realT *r) {
+  char buf[200];
+  int len, maxlen;
+
+  sprintf(buf, "  %s", option);
+  if (i)
+    sprintf(buf+strlen(buf), " %d", *i);
+  if (r)
+    sprintf(buf+strlen(buf), " %2.2g", *r);
+  len= (int)strlen(buf);  /* WARN64 */
+  qh->qhull_optionlen += len;
+  maxlen= sizeof(qh->qhull_options) - len -1;
+  maximize_(maxlen, 0);
+  if (qh->qhull_optionlen >= qh_OPTIONline && maxlen > 0) {
+    qh->qhull_optionlen= len;
+    strncat(qh->qhull_options, "\n", (size_t)(maxlen--));
+  }
+  strncat(qh->qhull_options, buf, (size_t)maxlen);
+} /* option */
+
+/*-<a                             href="qh-globa_r.htm#TOC"
+  >-------------------------------</a><a name="zero">-</a>
+
+  qh_zero( qh, errfile )
+    Initialize and zero Qhull's memory for qh_new_qhull()
+
+  notes:
+    Not needed in global.c because static variables are initialized to zero
+*/
+void qh_zero(qhT *qh, FILE *errfile) {
+    memset((char *)qh, 0, sizeof(qhT));   /* every field is 0, FALSE, NULL */
+    qh->NOerrexit= True;
+    qh_meminit(qh, errfile);
+} /* zero */
+
diff --git a/C/halfspaces.c b/C/halfspaces.c
new file mode 100644
--- /dev/null
+++ b/C/halfspaces.c
@@ -0,0 +1,68 @@
+#define qh_QHimport
+#include "qhull_ra.h"
+
+double** intersections(
+  double*   halfspaces,
+  double*   interiorpoint,
+  unsigned  dim,
+  unsigned  n,
+  unsigned* nintersections,
+  unsigned* exitcode,
+  unsigned  print
+)
+{
+  char opts[250];
+  sprintf(opts, "qhull s Fp FF H H%f", interiorpoint[0]); //, interiorpoint[1] , interiorpoint[2]);
+  for(unsigned i=1; i < dim; i++){
+    char x[20];
+    sprintf(x, ",%f", interiorpoint[i]);
+    strcat(opts, x);
+  }
+  printf(opts); printf("\n");
+
+  qhT qh_qh; /* Qhull's data structure */
+  qhT* qh= &qh_qh;
+  QHULL_LIB_CHECK
+  qh_meminit(qh, stderr);
+  boolT ismalloc  = False; /* True if qhull should free points in qh_freeqhull() or reallocation */
+  FILE *errfile   = NULL;
+  FILE* outfile = print ? stdout : NULL;
+  qh_zero(qh, errfile);
+  *exitcode = qh_new_qhull(qh, dim+1, n, halfspaces, ismalloc, opts, outfile,
+                           errfile);
+  printf("exitcode: %u\n", *exitcode);
+
+  double** out;
+  if(!(*exitcode)){
+    *nintersections = qh->num_facets;
+    out = malloc(*nintersections * sizeof(double*));
+    facetT *facet;
+    unsigned i_facet = 0;
+    FORALLfacets{
+      if(facet->offset != 0){
+        out[i_facet] = malloc(dim * sizeof(double));
+        for(unsigned i=0; i < dim; i++){
+          out[i_facet][i] = - facet->normal[i] / facet->offset +
+                            qh->feasible_point[i]; // = interiorpoint ? yes
+        }
+        i_facet++;
+      }else{
+        (*nintersections)--;
+      }
+    }
+
+  }
+
+  /* Do cleanup regardless of whether there is an error */
+  int curlong, totlong;
+  qh_freeqhull(qh, !qh_ALL);                /* free long memory */
+  qh_memfreeshort(qh, &curlong, &totlong);  /* free short memory and memory allocator */
+
+  if(*exitcode){
+    free(out);
+    return 0;
+  }else{
+    return out;
+  }
+
+}
diff --git a/C/io_r.c b/C/io_r.c
new file mode 100644
--- /dev/null
+++ b/C/io_r.c
@@ -0,0 +1,4062 @@
+/*<html><pre>  -<a                             href="qh-io_r.htm"
+  >-------------------------------</a><a name="TOP">-</a>
+
+   io_r.c
+   Input/Output routines of qhull application
+
+   see qh-io_r.htm and io_r.h
+
+   see user_r.c for qh_errprint and qh_printfacetlist
+
+   unix_r.c calls qh_readpoints and qh_produce_output
+
+   unix_r.c and user_r.c are the only callers of io_r.c functions
+   This allows the user to avoid loading io_r.o from qhull.a
+
+   Copyright (c) 1993-2015 The Geometry Center.
+   $Id: //main/2015/qhull/src/libqhull_r/io_r.c#4 $$Change: 2064 $
+   $DateTime: 2016/01/18 12:36:08 $$Author: bbarber $
+*/
+
+#include "qhull_ra.h"
+
+/*========= -functions in alphabetical order after qh_produce_output(qh)  =====*/
+
+/*-<a                             href="qh-io_r.htm#TOC"
+  >-------------------------------</a><a name="produce_output">-</a>
+
+  qh_produce_output(qh)
+  qh_produce_output2(qh)
+    prints out the result of qhull in desired format
+    qh_produce_output2(qh) does not call qh_prepare_output(qh)
+    if qh.GETarea
+      computes and prints area and volume
+    qh.PRINTout[] is an array of output formats
+
+  notes:
+    prints output in qh.PRINTout order
+*/
+void qh_produce_output(qhT *qh) {
+    int tempsize= qh_setsize(qh, qh->qhmem.tempstack);
+
+    qh_prepare_output(qh);
+    qh_produce_output2(qh);
+    if (qh_setsize(qh, qh->qhmem.tempstack) != tempsize) {
+        qh_fprintf(qh, qh->ferr, 6206, "qhull internal error (qh_produce_output): temporary sets not empty(%d)\n",
+            qh_setsize(qh, qh->qhmem.tempstack));
+        qh_errexit(qh, qh_ERRqhull, NULL, NULL);
+    }
+} /* produce_output */
+
+
+void qh_produce_output2(qhT *qh) {
+  int i, tempsize= qh_setsize(qh, qh->qhmem.tempstack), d_1;
+
+  if (qh->PRINTsummary)
+    qh_printsummary(qh, qh->ferr);
+  else if (qh->PRINTout[0] == qh_PRINTnone)
+    qh_printsummary(qh, qh->fout);
+  for (i=0; i < qh_PRINTEND; i++)
+    qh_printfacets(qh, qh->fout, qh->PRINTout[i], qh->facet_list, NULL, !qh_ALL);
+  qh_allstatistics(qh);
+  if (qh->PRINTprecision && !qh->MERGING && (qh->JOGGLEmax > REALmax/2 || qh->RERUN))
+    qh_printstats(qh, qh->ferr, qh->qhstat.precision, NULL);
+  if (qh->VERIFYoutput && (zzval_(Zridge) > 0 || zzval_(Zridgemid) > 0))
+    qh_printstats(qh, qh->ferr, qh->qhstat.vridges, NULL);
+  if (qh->PRINTstatistics) {
+    qh_printstatistics(qh, qh->ferr, "");
+    qh_memstatistics(qh, qh->ferr);
+    d_1= sizeof(setT) + (qh->hull_dim - 1) * SETelemsize;
+    qh_fprintf(qh, qh->ferr, 8040, "\
+    size in bytes: merge %d ridge %d vertex %d facet %d\n\
+         normal %d ridge vertices %d facet vertices or neighbors %d\n",
+            (int)sizeof(mergeT), (int)sizeof(ridgeT),
+            (int)sizeof(vertexT), (int)sizeof(facetT),
+            qh->normal_size, d_1, d_1 + SETelemsize);
+  }
+  if (qh_setsize(qh, qh->qhmem.tempstack) != tempsize) {
+    qh_fprintf(qh, qh->ferr, 6065, "qhull internal error (qh_produce_output2): temporary sets not empty(%d)\n",
+             qh_setsize(qh, qh->qhmem.tempstack));
+    qh_errexit(qh, qh_ERRqhull, NULL, NULL);
+  }
+} /* produce_output2 */
+
+/*-<a                             href="qh-io_r.htm#TOC"
+  >-------------------------------</a><a name="dfacet">-</a>
+
+  qh_dfacet(qh, id )
+    print facet by id, for debugging
+
+*/
+void qh_dfacet(qhT *qh, unsigned id) {
+  facetT *facet;
+
+  FORALLfacets {
+    if (facet->id == id) {
+      qh_printfacet(qh, qh->fout, facet);
+      break;
+    }
+  }
+} /* dfacet */
+
+
+/*-<a                             href="qh-io_r.htm#TOC"
+  >-------------------------------</a><a name="dvertex">-</a>
+
+  qh_dvertex(qh, id )
+    print vertex by id, for debugging
+*/
+void qh_dvertex(qhT *qh, unsigned id) {
+  vertexT *vertex;
+
+  FORALLvertices {
+    if (vertex->id == id) {
+      qh_printvertex(qh, qh->fout, vertex);
+      break;
+    }
+  }
+} /* dvertex */
+
+
+/*-<a                             href="qh-io_r.htm#TOC"
+  >-------------------------------</a><a name="compare_facetarea">-</a>
+
+  qh_compare_facetarea(p1, p2 )
+    used by qsort() to order facets by area
+*/
+int qh_compare_facetarea(const void *p1, const void *p2) {
+  const facetT *a= *((facetT *const*)p1), *b= *((facetT *const*)p2);
+
+  if (!a->isarea)
+    return -1;
+  if (!b->isarea)
+    return 1;
+  if (a->f.area > b->f.area)
+    return 1;
+  else if (a->f.area == b->f.area)
+    return 0;
+  return -1;
+} /* compare_facetarea */
+
+/*-<a                             href="qh-io_r.htm#TOC"
+  >-------------------------------</a><a name="compare_facetmerge">-</a>
+
+  qh_compare_facetmerge(p1, p2 )
+    used by qsort() to order facets by number of merges
+*/
+int qh_compare_facetmerge(const void *p1, const void *p2) {
+  const facetT *a= *((facetT *const*)p1), *b= *((facetT *const*)p2);
+
+  return(a->nummerge - b->nummerge);
+} /* compare_facetvisit */
+
+/*-<a                             href="qh-io_r.htm#TOC"
+  >-------------------------------</a><a name="compare_facetvisit">-</a>
+
+  qh_compare_facetvisit(p1, p2 )
+    used by qsort() to order facets by visit id or id
+*/
+int qh_compare_facetvisit(const void *p1, const void *p2) {
+  const facetT *a= *((facetT *const*)p1), *b= *((facetT *const*)p2);
+  int i,j;
+
+  if (!(i= a->visitid))
+    i= 0 - a->id; /* do not convert to int, sign distinguishes id from visitid */
+  if (!(j= b->visitid))
+    j= 0 - b->id;
+  return(i - j);
+} /* compare_facetvisit */
+
+/*-<a                             href="qh-io_r.htm#TOC"
+  >-------------------------------</a><a name="compare_vertexpoint">-</a>
+
+  qh_compare_vertexpoint( p1, p2 )
+    used by qsort() to order vertices by point id
+
+  Not usable in qhulllib_r since qh_pointid depends on qh
+
+  int qh_compare_vertexpoint(const void *p1, const void *p2) {
+  const vertexT *a= *((vertexT *const*)p1), *b= *((vertexT *const*)p2);
+
+  return((qh_pointid(qh, a->point) > qh_pointid(qh, b->point)?1:-1));
+}*/
+
+/*-<a                             href="qh-io_r.htm#TOC"
+  >-------------------------------</a><a name="copyfilename">-</a>
+
+  qh_copyfilename(qh, dest, size, source, length )
+    copy filename identified by qh_skipfilename()
+
+  notes:
+    see qh_skipfilename() for syntax
+*/
+void qh_copyfilename(qhT *qh, char *filename, int size, const char* source, int length) {
+  char c= *source;
+
+  if (length > size + 1) {
+      qh_fprintf(qh, qh->ferr, 6040, "qhull error: filename is more than %d characters, %s\n",  size-1, source);
+      qh_errexit(qh, qh_ERRinput, NULL, NULL);
+  }
+  strncpy(filename, source, length);
+  filename[length]= '\0';
+  if (c == '\'' || c == '"') {
+    char *s= filename + 1;
+    char *t= filename;
+    while (*s) {
+      if (*s == c) {
+          if (s[-1] == '\\')
+              t[-1]= c;
+      }else
+          *t++= *s;
+      s++;
+    }
+    *t= '\0';
+  }
+} /* copyfilename */
+
+/*-<a                             href="qh-io_r.htm#TOC"
+  >-------------------------------</a><a name="countfacets">-</a>
+
+  qh_countfacets(qh, facetlist, facets, printall,
+          numfacets, numsimplicial, totneighbors, numridges, numcoplanar, numtricoplanars  )
+    count good facets for printing and set visitid
+    if allfacets, ignores qh_skipfacet()
+
+  notes:
+    qh_printsummary and qh_countfacets must match counts
+
+  returns:
+    numfacets, numsimplicial, total neighbors, numridges, coplanars
+    each facet with ->visitid indicating 1-relative position
+      ->visitid==0 indicates not good
+
+  notes
+    numfacets >= numsimplicial
+    if qh.NEWfacets,
+      does not count visible facets (matches qh_printafacet)
+
+  design:
+    for all facets on facetlist and in facets set
+      unless facet is skipped or visible (i.e., will be deleted)
+        mark facet->visitid
+        update counts
+*/
+void qh_countfacets(qhT *qh, facetT *facetlist, setT *facets, boolT printall,
+    int *numfacetsp, int *numsimplicialp, int *totneighborsp, int *numridgesp, int *numcoplanarsp, int *numtricoplanarsp) {
+  facetT *facet, **facetp;
+  int numfacets= 0, numsimplicial= 0, numridges= 0, totneighbors= 0, numcoplanars= 0, numtricoplanars= 0;
+
+  FORALLfacet_(facetlist) {
+    if ((facet->visible && qh->NEWfacets)
+    || (!printall && qh_skipfacet(qh, facet)))
+      facet->visitid= 0;
+    else {
+      facet->visitid= ++numfacets;
+      totneighbors += qh_setsize(qh, facet->neighbors);
+      if (facet->simplicial) {
+        numsimplicial++;
+        if (facet->keepcentrum && facet->tricoplanar)
+          numtricoplanars++;
+      }else
+        numridges += qh_setsize(qh, facet->ridges);
+      if (facet->coplanarset)
+        numcoplanars += qh_setsize(qh, facet->coplanarset);
+    }
+  }
+
+  FOREACHfacet_(facets) {
+    if ((facet->visible && qh->NEWfacets)
+    || (!printall && qh_skipfacet(qh, facet)))
+      facet->visitid= 0;
+    else {
+      facet->visitid= ++numfacets;
+      totneighbors += qh_setsize(qh, facet->neighbors);
+      if (facet->simplicial){
+        numsimplicial++;
+        if (facet->keepcentrum && facet->tricoplanar)
+          numtricoplanars++;
+      }else
+        numridges += qh_setsize(qh, facet->ridges);
+      if (facet->coplanarset)
+        numcoplanars += qh_setsize(qh, facet->coplanarset);
+    }
+  }
+  qh->visit_id += numfacets+1;
+  *numfacetsp= numfacets;
+  *numsimplicialp= numsimplicial;
+  *totneighborsp= totneighbors;
+  *numridgesp= numridges;
+  *numcoplanarsp= numcoplanars;
+  *numtricoplanarsp= numtricoplanars;
+} /* countfacets */
+
+/*-<a                             href="qh-io_r.htm#TOC"
+  >-------------------------------</a><a name="detvnorm">-</a>
+
+  qh_detvnorm(qh, vertex, vertexA, centers, offset )
+    compute separating plane of the Voronoi diagram for a pair of input sites
+    centers= set of facets (i.e., Voronoi vertices)
+      facet->visitid= 0 iff vertex-at-infinity (i.e., unbounded)
+
+  assumes:
+    qh_ASvoronoi and qh_vertexneighbors() already set
+
+  returns:
+    norm
+      a pointer into qh.gm_matrix to qh.hull_dim-1 reals
+      copy the data before reusing qh.gm_matrix
+    offset
+      if 'QVn'
+        sign adjusted so that qh.GOODvertexp is inside
+      else
+        sign adjusted so that vertex is inside
+
+    qh.gm_matrix= simplex of points from centers relative to first center
+
+  notes:
+    in io_r.c so that code for 'v Tv' can be removed by removing io_r.c
+    returns pointer into qh.gm_matrix to avoid tracking of temporary memory
+
+  design:
+    determine midpoint of input sites
+    build points as the set of Voronoi vertices
+    select a simplex from points (if necessary)
+      include midpoint if the Voronoi region is unbounded
+    relocate the first vertex of the simplex to the origin
+    compute the normalized hyperplane through the simplex
+    orient the hyperplane toward 'QVn' or 'vertex'
+    if 'Tv' or 'Ts'
+      if bounded
+        test that hyperplane is the perpendicular bisector of the input sites
+      test that Voronoi vertices not in the simplex are still on the hyperplane
+    free up temporary memory
+*/
+pointT *qh_detvnorm(qhT *qh, vertexT *vertex, vertexT *vertexA, setT *centers, realT *offsetp) {
+  facetT *facet, **facetp;
+  int  i, k, pointid, pointidA, point_i, point_n;
+  setT *simplex= NULL;
+  pointT *point, **pointp, *point0, *midpoint, *normal, *inpoint;
+  coordT *coord, *gmcoord, *normalp;
+  setT *points= qh_settemp(qh, qh->TEMPsize);
+  boolT nearzero= False;
+  boolT unbounded= False;
+  int numcenters= 0;
+  int dim= qh->hull_dim - 1;
+  realT dist, offset, angle, zero= 0.0;
+
+  midpoint= qh->gm_matrix + qh->hull_dim * qh->hull_dim;  /* last row */
+  for (k=0; k < dim; k++)
+    midpoint[k]= (vertex->point[k] + vertexA->point[k])/2;
+  FOREACHfacet_(centers) {
+    numcenters++;
+    if (!facet->visitid)
+      unbounded= True;
+    else {
+      if (!facet->center)
+        facet->center= qh_facetcenter(qh, facet->vertices);
+      qh_setappend(qh, &points, facet->center);
+    }
+  }
+  if (numcenters > dim) {
+    simplex= qh_settemp(qh, qh->TEMPsize);
+    qh_setappend(qh, &simplex, vertex->point);
+    if (unbounded)
+      qh_setappend(qh, &simplex, midpoint);
+    qh_maxsimplex(qh, dim, points, NULL, 0, &simplex);
+    qh_setdelnth(qh, simplex, 0);
+  }else if (numcenters == dim) {
+    if (unbounded)
+      qh_setappend(qh, &points, midpoint);
+    simplex= points;
+  }else {
+    qh_fprintf(qh, qh->ferr, 6216, "qhull internal error (qh_detvnorm): too few points(%d) to compute separating plane\n", numcenters);
+    qh_errexit(qh, qh_ERRqhull, NULL, NULL);
+  }
+  i= 0;
+  gmcoord= qh->gm_matrix;
+  point0= SETfirstt_(simplex, pointT);
+  FOREACHpoint_(simplex) {
+    if (qh->IStracing >= 4)
+      qh_printmatrix(qh, qh->ferr, "qh_detvnorm: Voronoi vertex or midpoint",
+                              &point, 1, dim);
+    if (point != point0) {
+      qh->gm_row[i++]= gmcoord;
+      coord= point0;
+      for (k=dim; k--; )
+        *(gmcoord++)= *point++ - *coord++;
+    }
+  }
+  qh->gm_row[i]= gmcoord;  /* does not overlap midpoint, may be used later for qh_areasimplex */
+  normal= gmcoord;
+  qh_sethyperplane_gauss(qh, dim, qh->gm_row, point0, True,
+                normal, &offset, &nearzero);
+  if (qh->GOODvertexp == vertexA->point)
+    inpoint= vertexA->point;
+  else
+    inpoint= vertex->point;
+  zinc_(Zdistio);
+  dist= qh_distnorm(dim, inpoint, normal, &offset);
+  if (dist > 0) {
+    offset= -offset;
+    normalp= normal;
+    for (k=dim; k--; ) {
+      *normalp= -(*normalp);
+      normalp++;
+    }
+  }
+  if (qh->VERIFYoutput || qh->PRINTstatistics) {
+    pointid= qh_pointid(qh, vertex->point);
+    pointidA= qh_pointid(qh, vertexA->point);
+    if (!unbounded) {
+      zinc_(Zdiststat);
+      dist= qh_distnorm(dim, midpoint, normal, &offset);
+      if (dist < 0)
+        dist= -dist;
+      zzinc_(Zridgemid);
+      wwmax_(Wridgemidmax, dist);
+      wwadd_(Wridgemid, dist);
+      trace4((qh, qh->ferr, 4014, "qh_detvnorm: points %d %d midpoint dist %2.2g\n",
+                 pointid, pointidA, dist));
+      for (k=0; k < dim; k++)
+        midpoint[k]= vertexA->point[k] - vertex->point[k];  /* overwrites midpoint! */
+      qh_normalize(qh, midpoint, dim, False);
+      angle= qh_distnorm(dim, midpoint, normal, &zero); /* qh_detangle uses dim+1 */
+      if (angle < 0.0)
+        angle= angle + 1.0;
+      else
+        angle= angle - 1.0;
+      if (angle < 0.0)
+        angle -= angle;
+      trace4((qh, qh->ferr, 4015, "qh_detvnorm: points %d %d angle %2.2g nearzero %d\n",
+                 pointid, pointidA, angle, nearzero));
+      if (nearzero) {
+        zzinc_(Zridge0);
+        wwmax_(Wridge0max, angle);
+        wwadd_(Wridge0, angle);
+      }else {
+        zzinc_(Zridgeok)
+        wwmax_(Wridgeokmax, angle);
+        wwadd_(Wridgeok, angle);
+      }
+    }
+    if (simplex != points) {
+      FOREACHpoint_i_(qh, points) {
+        if (!qh_setin(simplex, point)) {
+          facet= SETelemt_(centers, point_i, facetT);
+          zinc_(Zdiststat);
+          dist= qh_distnorm(dim, point, normal, &offset);
+          if (dist < 0)
+            dist= -dist;
+          zzinc_(Zridge);
+          wwmax_(Wridgemax, dist);
+          wwadd_(Wridge, dist);
+          trace4((qh, qh->ferr, 4016, "qh_detvnorm: points %d %d Voronoi vertex %d dist %2.2g\n",
+                             pointid, pointidA, facet->visitid, dist));
+        }
+      }
+    }
+  }
+  *offsetp= offset;
+  if (simplex != points)
+    qh_settempfree(qh, &simplex);
+  qh_settempfree(qh, &points);
+  return normal;
+} /* detvnorm */
+
+/*-<a                             href="qh-io_r.htm#TOC"
+  >-------------------------------</a><a name="detvridge">-</a>
+
+  qh_detvridge(qh, vertexA )
+    determine Voronoi ridge from 'seen' neighbors of vertexA
+    include one vertex-at-infinite if an !neighbor->visitid
+
+  returns:
+    temporary set of centers (facets, i.e., Voronoi vertices)
+    sorted by center id
+*/
+setT *qh_detvridge(qhT *qh, vertexT *vertex) {
+  setT *centers= qh_settemp(qh, qh->TEMPsize);
+  setT *tricenters= qh_settemp(qh, qh->TEMPsize);
+  facetT *neighbor, **neighborp;
+  boolT firstinf= True;
+
+  FOREACHneighbor_(vertex) {
+    if (neighbor->seen) {
+      if (neighbor->visitid) {
+        if (!neighbor->tricoplanar || qh_setunique(qh, &tricenters, neighbor->center))
+          qh_setappend(qh, &centers, neighbor);
+      }else if (firstinf) {
+        firstinf= False;
+        qh_setappend(qh, &centers, neighbor);
+      }
+    }
+  }
+  qsort(SETaddr_(centers, facetT), (size_t)qh_setsize(qh, centers),
+             sizeof(facetT *), qh_compare_facetvisit);
+  qh_settempfree(qh, &tricenters);
+  return centers;
+} /* detvridge */
+
+/*-<a                             href="qh-io_r.htm#TOC"
+  >-------------------------------</a><a name="detvridge3">-</a>
+
+  qh_detvridge3(qh, atvertex, vertex )
+    determine 3-d Voronoi ridge from 'seen' neighbors of atvertex and vertex
+    include one vertex-at-infinite for !neighbor->visitid
+    assumes all facet->seen2= True
+
+  returns:
+    temporary set of centers (facets, i.e., Voronoi vertices)
+    listed in adjacency order (!oriented)
+    all facet->seen2= True
+
+  design:
+    mark all neighbors of atvertex
+    for each adjacent neighbor of both atvertex and vertex
+      if neighbor selected
+        add neighbor to set of Voronoi vertices
+*/
+setT *qh_detvridge3(qhT *qh, vertexT *atvertex, vertexT *vertex) {
+  setT *centers= qh_settemp(qh, qh->TEMPsize);
+  setT *tricenters= qh_settemp(qh, qh->TEMPsize);
+  facetT *neighbor, **neighborp, *facet= NULL;
+  boolT firstinf= True;
+
+  FOREACHneighbor_(atvertex)
+    neighbor->seen2= False;
+  FOREACHneighbor_(vertex) {
+    if (!neighbor->seen2) {
+      facet= neighbor;
+      break;
+    }
+  }
+  while (facet) {
+    facet->seen2= True;
+    if (neighbor->seen) {
+      if (facet->visitid) {
+        if (!facet->tricoplanar || qh_setunique(qh, &tricenters, facet->center))
+          qh_setappend(qh, &centers, facet);
+      }else if (firstinf) {
+        firstinf= False;
+        qh_setappend(qh, &centers, facet);
+      }
+    }
+    FOREACHneighbor_(facet) {
+      if (!neighbor->seen2) {
+        if (qh_setin(vertex->neighbors, neighbor))
+          break;
+        else
+          neighbor->seen2= True;
+      }
+    }
+    facet= neighbor;
+  }
+  if (qh->CHECKfrequently) {
+    FOREACHneighbor_(vertex) {
+      if (!neighbor->seen2) {
+          qh_fprintf(qh, qh->ferr, 6217, "qhull internal error (qh_detvridge3): neighbors of vertex p%d are not connected at facet %d\n",
+                 qh_pointid(qh, vertex->point), neighbor->id);
+        qh_errexit(qh, qh_ERRqhull, neighbor, NULL);
+      }
+    }
+  }
+  FOREACHneighbor_(atvertex)
+    neighbor->seen2= True;
+  qh_settempfree(qh, &tricenters);
+  return centers;
+} /* detvridge3 */
+
+/*-<a                             href="qh-io_r.htm#TOC"
+  >-------------------------------</a><a name="eachvoronoi">-</a>
+
+  qh_eachvoronoi(qh, fp, printvridge, vertex, visitall, innerouter, inorder )
+    if visitall,
+      visit all Voronoi ridges for vertex (i.e., an input site)
+    else
+      visit all unvisited Voronoi ridges for vertex
+      all vertex->seen= False if unvisited
+    assumes
+      all facet->seen= False
+      all facet->seen2= True (for qh_detvridge3)
+      all facet->visitid == 0 if vertex_at_infinity
+                         == index of Voronoi vertex
+                         >= qh.num_facets if ignored
+    innerouter:
+      qh_RIDGEall--  both inner (bounded) and outer(unbounded) ridges
+      qh_RIDGEinner- only inner
+      qh_RIDGEouter- only outer
+
+    if inorder
+      orders vertices for 3-d Voronoi diagrams
+
+  returns:
+    number of visited ridges (does not include previously visited ridges)
+
+    if printvridge,
+      calls printvridge( fp, vertex, vertexA, centers)
+        fp== any pointer (assumes FILE*)
+        vertex,vertexA= pair of input sites that define a Voronoi ridge
+        centers= set of facets (i.e., Voronoi vertices)
+                 ->visitid == index or 0 if vertex_at_infinity
+                 ordered for 3-d Voronoi diagram
+  notes:
+    uses qh.vertex_visit
+
+  see:
+    qh_eachvoronoi_all()
+
+  design:
+    mark selected neighbors of atvertex
+    for each selected neighbor (either Voronoi vertex or vertex-at-infinity)
+      for each unvisited vertex
+        if atvertex and vertex share more than d-1 neighbors
+          bump totalcount
+          if printvridge defined
+            build the set of shared neighbors (i.e., Voronoi vertices)
+            call printvridge
+*/
+int qh_eachvoronoi(qhT *qh, FILE *fp, printvridgeT printvridge, vertexT *atvertex, boolT visitall, qh_RIDGE innerouter, boolT inorder) {
+  boolT unbounded;
+  int count;
+  facetT *neighbor, **neighborp, *neighborA, **neighborAp;
+  setT *centers;
+  setT *tricenters= qh_settemp(qh, qh->TEMPsize);
+
+  vertexT *vertex, **vertexp;
+  boolT firstinf;
+  unsigned int numfacets= (unsigned int)qh->num_facets;
+  int totridges= 0;
+
+  qh->vertex_visit++;
+  atvertex->seen= True;
+  if (visitall) {
+    FORALLvertices
+      vertex->seen= False;
+  }
+  FOREACHneighbor_(atvertex) {
+    if (neighbor->visitid < numfacets)
+      neighbor->seen= True;
+  }
+  FOREACHneighbor_(atvertex) {
+    if (neighbor->seen) {
+      FOREACHvertex_(neighbor->vertices) {
+        if (vertex->visitid != qh->vertex_visit && !vertex->seen) {
+          vertex->visitid= qh->vertex_visit;
+          count= 0;
+          firstinf= True;
+          qh_settruncate(qh, tricenters, 0);
+          FOREACHneighborA_(vertex) {
+            if (neighborA->seen) {
+              if (neighborA->visitid) {
+                if (!neighborA->tricoplanar || qh_setunique(qh, &tricenters, neighborA->center))
+                  count++;
+              }else if (firstinf) {
+                count++;
+                firstinf= False;
+              }
+            }
+          }
+          if (count >= qh->hull_dim - 1) {  /* e.g., 3 for 3-d Voronoi */
+            if (firstinf) {
+              if (innerouter == qh_RIDGEouter)
+                continue;
+              unbounded= False;
+            }else {
+              if (innerouter == qh_RIDGEinner)
+                continue;
+              unbounded= True;
+            }
+            totridges++;
+            trace4((qh, qh->ferr, 4017, "qh_eachvoronoi: Voronoi ridge of %d vertices between sites %d and %d\n",
+                  count, qh_pointid(qh, atvertex->point), qh_pointid(qh, vertex->point)));
+            if (printvridge && fp) {
+              if (inorder && qh->hull_dim == 3+1) /* 3-d Voronoi diagram */
+                centers= qh_detvridge3(qh, atvertex, vertex);
+              else
+                centers= qh_detvridge(qh, vertex);
+              (*printvridge)(qh, fp, atvertex, vertex, centers, unbounded);
+              qh_settempfree(qh, &centers);
+            }
+          }
+        }
+      }
+    }
+  }
+  FOREACHneighbor_(atvertex)
+    neighbor->seen= False;
+  qh_settempfree(qh, &tricenters);
+  return totridges;
+} /* eachvoronoi */
+
+
+/*-<a                             href="qh-poly_r.htm#TOC"
+  >-------------------------------</a><a name="eachvoronoi_all">-</a>
+
+  qh_eachvoronoi_all(qh, fp, printvridge, isUpper, innerouter, inorder )
+    visit all Voronoi ridges
+
+    innerouter:
+      see qh_eachvoronoi()
+
+    if inorder
+      orders vertices for 3-d Voronoi diagrams
+
+  returns
+    total number of ridges
+
+    if isUpper == facet->upperdelaunay  (i.e., a Vornoi vertex)
+      facet->visitid= Voronoi vertex index(same as 'o' format)
+    else
+      facet->visitid= 0
+
+    if printvridge,
+      calls printvridge( fp, vertex, vertexA, centers)
+      [see qh_eachvoronoi]
+
+  notes:
+    Not used for qhull.exe
+    same effect as qh_printvdiagram but ridges not sorted by point id
+*/
+int qh_eachvoronoi_all(qhT *qh, FILE *fp, printvridgeT printvridge, boolT isUpper, qh_RIDGE innerouter, boolT inorder) {
+  facetT *facet;
+  vertexT *vertex;
+  int numcenters= 1;  /* vertex 0 is vertex-at-infinity */
+  int totridges= 0;
+
+  qh_clearcenters(qh, qh_ASvoronoi);
+  qh_vertexneighbors(qh);
+  maximize_(qh->visit_id, (unsigned) qh->num_facets);
+  FORALLfacets {
+    facet->visitid= 0;
+    facet->seen= False;
+    facet->seen2= True;
+  }
+  FORALLfacets {
+    if (facet->upperdelaunay == isUpper)
+      facet->visitid= numcenters++;
+  }
+  FORALLvertices
+    vertex->seen= False;
+  FORALLvertices {
+    if (qh->GOODvertex > 0 && qh_pointid(qh, vertex->point)+1 != qh->GOODvertex)
+      continue;
+    totridges += qh_eachvoronoi(qh, fp, printvridge, vertex,
+                   !qh_ALL, innerouter, inorder);
+  }
+  return totridges;
+} /* eachvoronoi_all */
+
+/*-<a                             href="qh-io_r.htm#TOC"
+  >-------------------------------</a><a name="facet2point">-</a>
+
+  qh_facet2point(qh, facet, point0, point1, mindist )
+    return two projected temporary vertices for a 2-d facet
+    may be non-simplicial
+
+  returns:
+    point0 and point1 oriented and projected to the facet
+    returns mindist (maximum distance below plane)
+*/
+void qh_facet2point(qhT *qh, facetT *facet, pointT **point0, pointT **point1, realT *mindist) {
+  vertexT *vertex0, *vertex1;
+  realT dist;
+
+  if (facet->toporient ^ qh_ORIENTclock) {
+    vertex0= SETfirstt_(facet->vertices, vertexT);
+    vertex1= SETsecondt_(facet->vertices, vertexT);
+  }else {
+    vertex1= SETfirstt_(facet->vertices, vertexT);
+    vertex0= SETsecondt_(facet->vertices, vertexT);
+  }
+  zadd_(Zdistio, 2);
+  qh_distplane(qh, vertex0->point, facet, &dist);
+  *mindist= dist;
+  *point0= qh_projectpoint(qh, vertex0->point, facet, dist);
+  qh_distplane(qh, vertex1->point, facet, &dist);
+  minimize_(*mindist, dist);
+  *point1= qh_projectpoint(qh, vertex1->point, facet, dist);
+} /* facet2point */
+
+
+/*-<a                             href="qh-io_r.htm#TOC"
+  >-------------------------------</a><a name="facetvertices">-</a>
+
+  qh_facetvertices(qh, facetlist, facets, allfacets )
+    returns temporary set of vertices in a set and/or list of facets
+    if allfacets, ignores qh_skipfacet()
+
+  returns:
+    vertices with qh.vertex_visit
+
+  notes:
+    optimized for allfacets of facet_list
+
+  design:
+    if allfacets of facet_list
+      create vertex set from vertex_list
+    else
+      for each selected facet in facets or facetlist
+        append unvisited vertices to vertex set
+*/
+setT *qh_facetvertices(qhT *qh, facetT *facetlist, setT *facets, boolT allfacets) {
+  setT *vertices;
+  facetT *facet, **facetp;
+  vertexT *vertex, **vertexp;
+
+  qh->vertex_visit++;
+  if (facetlist == qh->facet_list && allfacets && !facets) {
+    vertices= qh_settemp(qh, qh->num_vertices);
+    FORALLvertices {
+      vertex->visitid= qh->vertex_visit;
+      qh_setappend(qh, &vertices, vertex);
+    }
+  }else {
+    vertices= qh_settemp(qh, qh->TEMPsize);
+    FORALLfacet_(facetlist) {
+      if (!allfacets && qh_skipfacet(qh, facet))
+        continue;
+      FOREACHvertex_(facet->vertices) {
+        if (vertex->visitid != qh->vertex_visit) {
+          vertex->visitid= qh->vertex_visit;
+          qh_setappend(qh, &vertices, vertex);
+        }
+      }
+    }
+  }
+  FOREACHfacet_(facets) {
+    if (!allfacets && qh_skipfacet(qh, facet))
+      continue;
+    FOREACHvertex_(facet->vertices) {
+      if (vertex->visitid != qh->vertex_visit) {
+        vertex->visitid= qh->vertex_visit;
+        qh_setappend(qh, &vertices, vertex);
+      }
+    }
+  }
+  return vertices;
+} /* facetvertices */
+
+/*-<a                             href="qh-geom_r.htm#TOC"
+  >-------------------------------</a><a name="geomplanes">-</a>
+
+  qh_geomplanes(qh, facet, outerplane, innerplane )
+    return outer and inner planes for Geomview
+    qh.PRINTradius is size of vertices and points (includes qh.JOGGLEmax)
+
+  notes:
+    assume precise calculations in io.c with roundoff covered by qh_GEOMepsilon
+*/
+void qh_geomplanes(qhT *qh, facetT *facet, realT *outerplane, realT *innerplane) {
+  realT radius;
+
+  if (qh->MERGING || qh->JOGGLEmax < REALmax/2) {
+    qh_outerinner(qh, facet, outerplane, innerplane);
+    radius= qh->PRINTradius;
+    if (qh->JOGGLEmax < REALmax/2)
+      radius -= qh->JOGGLEmax * sqrt((realT)qh->hull_dim);  /* already accounted for in qh_outerinner() */
+    *outerplane += radius;
+    *innerplane -= radius;
+    if (qh->PRINTcoplanar || qh->PRINTspheres) {
+      *outerplane += qh->MAXabs_coord * qh_GEOMepsilon;
+      *innerplane -= qh->MAXabs_coord * qh_GEOMepsilon;
+    }
+  }else
+    *innerplane= *outerplane= 0;
+} /* geomplanes */
+
+
+/*-<a                             href="qh-io_r.htm#TOC"
+  >-------------------------------</a><a name="markkeep">-</a>
+
+  qh_markkeep(qh, facetlist )
+    mark good facets that meet qh.KEEParea, qh.KEEPmerge, and qh.KEEPminArea
+    ignores visible facets (!part of convex hull)
+
+  returns:
+    may clear facet->good
+    recomputes qh.num_good
+
+  design:
+    get set of good facets
+    if qh.KEEParea
+      sort facets by area
+      clear facet->good for all but n largest facets
+    if qh.KEEPmerge
+      sort facets by merge count
+      clear facet->good for all but n most merged facets
+    if qh.KEEPminarea
+      clear facet->good if area too small
+    update qh.num_good
+*/
+void qh_markkeep(qhT *qh, facetT *facetlist) {
+  facetT *facet, **facetp;
+  setT *facets= qh_settemp(qh, qh->num_facets);
+  int size, count;
+
+  trace2((qh, qh->ferr, 2006, "qh_markkeep: only keep %d largest and/or %d most merged facets and/or min area %.2g\n",
+          qh->KEEParea, qh->KEEPmerge, qh->KEEPminArea));
+  FORALLfacet_(facetlist) {
+    if (!facet->visible && facet->good)
+      qh_setappend(qh, &facets, facet);
+  }
+  size= qh_setsize(qh, facets);
+  if (qh->KEEParea) {
+    qsort(SETaddr_(facets, facetT), (size_t)size,
+             sizeof(facetT *), qh_compare_facetarea);
+    if ((count= size - qh->KEEParea) > 0) {
+      FOREACHfacet_(facets) {
+        facet->good= False;
+        if (--count == 0)
+          break;
+      }
+    }
+  }
+  if (qh->KEEPmerge) {
+    qsort(SETaddr_(facets, facetT), (size_t)size,
+             sizeof(facetT *), qh_compare_facetmerge);
+    if ((count= size - qh->KEEPmerge) > 0) {
+      FOREACHfacet_(facets) {
+        facet->good= False;
+        if (--count == 0)
+          break;
+      }
+    }
+  }
+  if (qh->KEEPminArea < REALmax/2) {
+    FOREACHfacet_(facets) {
+      if (!facet->isarea || facet->f.area < qh->KEEPminArea)
+        facet->good= False;
+    }
+  }
+  qh_settempfree(qh, &facets);
+  count= 0;
+  FORALLfacet_(facetlist) {
+    if (facet->good)
+      count++;
+  }
+  qh->num_good= count;
+} /* markkeep */
+
+
+/*-<a                             href="qh-io_r.htm#TOC"
+  >-------------------------------</a><a name="markvoronoi">-</a>
+
+  qh_markvoronoi(qh, facetlist, facets, printall, isLower, numcenters )
+    mark voronoi vertices for printing by site pairs
+
+  returns:
+    temporary set of vertices indexed by pointid
+    isLower set if printing lower hull (i.e., at least one facet is lower hull)
+    numcenters= total number of Voronoi vertices
+    bumps qh.printoutnum for vertex-at-infinity
+    clears all facet->seen and sets facet->seen2
+
+    if selected
+      facet->visitid= Voronoi vertex id
+    else if upper hull (or 'Qu' and lower hull)
+      facet->visitid= 0
+    else
+      facet->visitid >= qh->num_facets
+
+  notes:
+    ignores qh.ATinfinity, if defined
+*/
+setT *qh_markvoronoi(qhT *qh, facetT *facetlist, setT *facets, boolT printall, boolT *isLowerp, int *numcentersp) {
+  int numcenters=0;
+  facetT *facet, **facetp;
+  setT *vertices;
+  boolT isLower= False;
+
+  qh->printoutnum++;
+  qh_clearcenters(qh, qh_ASvoronoi);  /* in case, qh_printvdiagram2 called by user */
+  qh_vertexneighbors(qh);
+  vertices= qh_pointvertex(qh);
+  if (qh->ATinfinity)
+    SETelem_(vertices, qh->num_points-1)= NULL;
+  qh->visit_id++;
+  maximize_(qh->visit_id, (unsigned) qh->num_facets);
+  FORALLfacet_(facetlist) {
+    if (printall || !qh_skipfacet(qh, facet)) {
+      if (!facet->upperdelaunay) {
+        isLower= True;
+        break;
+      }
+    }
+  }
+  FOREACHfacet_(facets) {
+    if (printall || !qh_skipfacet(qh, facet)) {
+      if (!facet->upperdelaunay) {
+        isLower= True;
+        break;
+      }
+    }
+  }
+  FORALLfacets {
+    if (facet->normal && (facet->upperdelaunay == isLower))
+      facet->visitid= 0;  /* facetlist or facets may overwrite */
+    else
+      facet->visitid= qh->visit_id;
+    facet->seen= False;
+    facet->seen2= True;
+  }
+  numcenters++;  /* qh_INFINITE */
+  FORALLfacet_(facetlist) {
+    if (printall || !qh_skipfacet(qh, facet))
+      facet->visitid= numcenters++;
+  }
+  FOREACHfacet_(facets) {
+    if (printall || !qh_skipfacet(qh, facet))
+      facet->visitid= numcenters++;
+  }
+  *isLowerp= isLower;
+  *numcentersp= numcenters;
+  trace2((qh, qh->ferr, 2007, "qh_markvoronoi: isLower %d numcenters %d\n", isLower, numcenters));
+  return vertices;
+} /* markvoronoi */
+
+/*-<a                             href="qh-io_r.htm#TOC"
+  >-------------------------------</a><a name="order_vertexneighbors">-</a>
+
+  qh_order_vertexneighbors(qh, vertex )
+    order facet neighbors of a 2-d or 3-d vertex by adjacency
+
+  notes:
+    does not orient the neighbors
+
+  design:
+    initialize a new neighbor set with the first facet in vertex->neighbors
+    while vertex->neighbors non-empty
+      select next neighbor in the previous facet's neighbor set
+    set vertex->neighbors to the new neighbor set
+*/
+void qh_order_vertexneighbors(qhT *qh, vertexT *vertex) {
+  setT *newset;
+  facetT *facet, *neighbor, **neighborp;
+
+  trace4((qh, qh->ferr, 4018, "qh_order_vertexneighbors: order neighbors of v%d for 3-d\n", vertex->id));
+  newset= qh_settemp(qh, qh_setsize(qh, vertex->neighbors));
+  facet= (facetT*)qh_setdellast(vertex->neighbors);
+  qh_setappend(qh, &newset, facet);
+  while (qh_setsize(qh, vertex->neighbors)) {
+    FOREACHneighbor_(vertex) {
+      if (qh_setin(facet->neighbors, neighbor)) {
+        qh_setdel(vertex->neighbors, neighbor);
+        qh_setappend(qh, &newset, neighbor);
+        facet= neighbor;
+        break;
+      }
+    }
+    if (!neighbor) {
+      qh_fprintf(qh, qh->ferr, 6066, "qhull internal error (qh_order_vertexneighbors): no neighbor of v%d for f%d\n",
+        vertex->id, facet->id);
+      qh_errexit(qh, qh_ERRqhull, facet, NULL);
+    }
+  }
+  qh_setfree(qh, &vertex->neighbors);
+  qh_settemppop(qh);
+  vertex->neighbors= newset;
+} /* order_vertexneighbors */
+
+/*-<a                             href="qh-io_r.htm#TOC"
+  >-------------------------------</a><a name="prepare_output">-</a>
+
+  qh_prepare_output(qh, )
+    prepare for qh_produce_output2(qh) according to
+      qh.KEEPminArea, KEEParea, KEEPmerge, GOODvertex, GOODthreshold, GOODpoint, ONLYgood, SPLITthresholds
+    does not reset facet->good
+
+  notes
+    except for PRINTstatistics, no-op if previously called with same options
+*/
+void qh_prepare_output(qhT *qh) {
+  if (qh->VORONOI) {
+    qh_clearcenters(qh, qh_ASvoronoi);  /* must be before qh_triangulate */
+    qh_vertexneighbors(qh);
+  }
+  if (qh->TRIangulate && !qh->hasTriangulation) {
+    qh_triangulate(qh);
+    if (qh->VERIFYoutput && !qh->CHECKfrequently)
+      qh_checkpolygon(qh, qh->facet_list);
+  }
+  qh_findgood_all(qh, qh->facet_list);
+  if (qh->GETarea)
+    qh_getarea(qh, qh->facet_list);
+  if (qh->KEEParea || qh->KEEPmerge || qh->KEEPminArea < REALmax/2)
+    qh_markkeep(qh, qh->facet_list);
+  if (qh->PRINTstatistics)
+    qh_collectstatistics(qh);
+}
+
+/*-<a                             href="qh-io_r.htm#TOC"
+  >-------------------------------</a><a name="printafacet">-</a>
+
+  qh_printafacet(qh, fp, format, facet, printall )
+    print facet to fp in given output format (see qh.PRINTout)
+
+  returns:
+    nop if !printall and qh_skipfacet()
+    nop if visible facet and NEWfacets and format != PRINTfacets
+    must match qh_countfacets
+
+  notes
+    preserves qh.visit_id
+    facet->normal may be null if PREmerge/MERGEexact and STOPcone before merge
+
+  see
+    qh_printbegin() and qh_printend()
+
+  design:
+    test for printing facet
+    call appropriate routine for format
+    or output results directly
+*/
+void qh_printafacet(qhT *qh, FILE *fp, qh_PRINT format, facetT *facet, boolT printall) {
+  realT color[4], offset, dist, outerplane, innerplane;
+  boolT zerodiv;
+  coordT *point, *normp, *coordp, **pointp, *feasiblep;
+  int k;
+  vertexT *vertex, **vertexp;
+  facetT *neighbor, **neighborp;
+
+  if (!printall && qh_skipfacet(qh, facet))
+    return;
+  if (facet->visible && qh->NEWfacets && format != qh_PRINTfacets)
+    return;
+  qh->printoutnum++;
+  switch (format) {
+  case qh_PRINTarea:
+    if (facet->isarea) {
+      qh_fprintf(qh, fp, 9009, qh_REAL_1, facet->f.area);
+      qh_fprintf(qh, fp, 9010, "\n");
+    }else
+      qh_fprintf(qh, fp, 9011, "0\n");
+    break;
+  case qh_PRINTcoplanars:
+    qh_fprintf(qh, fp, 9012, "%d", qh_setsize(qh, facet->coplanarset));
+    FOREACHpoint_(facet->coplanarset)
+      qh_fprintf(qh, fp, 9013, " %d", qh_pointid(qh, point));
+    qh_fprintf(qh, fp, 9014, "\n");
+    break;
+  case qh_PRINTcentrums:
+    qh_printcenter(qh, fp, format, NULL, facet);
+    break;
+  case qh_PRINTfacets:
+    qh_printfacet(qh, fp, facet);
+    break;
+  case qh_PRINTfacets_xridge:
+    qh_printfacetheader(qh, fp, facet);
+    break;
+  case qh_PRINTgeom:  /* either 2 , 3, or 4-d by qh_printbegin */
+    if (!facet->normal)
+      break;
+    for (k=qh->hull_dim; k--; ) {
+      color[k]= (facet->normal[k]+1.0)/2.0;
+      maximize_(color[k], -1.0);
+      minimize_(color[k], +1.0);
+    }
+    qh_projectdim3(qh, color, color);
+    if (qh->PRINTdim != qh->hull_dim)
+      qh_normalize2(qh, color, 3, True, NULL, NULL);
+    if (qh->hull_dim <= 2)
+      qh_printfacet2geom(qh, fp, facet, color);
+    else if (qh->hull_dim == 3) {
+      if (facet->simplicial)
+        qh_printfacet3geom_simplicial(qh, fp, facet, color);
+      else
+        qh_printfacet3geom_nonsimplicial(qh, fp, facet, color);
+    }else {
+      if (facet->simplicial)
+        qh_printfacet4geom_simplicial(qh, fp, facet, color);
+      else
+        qh_printfacet4geom_nonsimplicial(qh, fp, facet, color);
+    }
+    break;
+  case qh_PRINTids:
+    qh_fprintf(qh, fp, 9015, "%d\n", facet->id);
+    break;
+  case qh_PRINTincidences:
+  case qh_PRINToff:
+  case qh_PRINTtriangles:
+    if (qh->hull_dim == 3 && format != qh_PRINTtriangles)
+      qh_printfacet3vertex(qh, fp, facet, format);
+    else if (facet->simplicial || qh->hull_dim == 2 || format == qh_PRINToff)
+      qh_printfacetNvertex_simplicial(qh, fp, facet, format);
+    else
+      qh_printfacetNvertex_nonsimplicial(qh, fp, facet, qh->printoutvar++, format);
+    break;
+  case qh_PRINTinner:
+    qh_outerinner(qh, facet, NULL, &innerplane);
+    offset= facet->offset - innerplane;
+    goto LABELprintnorm;
+    break; /* prevent warning */
+  case qh_PRINTmerges:
+    qh_fprintf(qh, fp, 9016, "%d\n", facet->nummerge);
+    break;
+  case qh_PRINTnormals:
+    offset= facet->offset;
+    goto LABELprintnorm;
+    break; /* prevent warning */
+  case qh_PRINTouter:
+    qh_outerinner(qh, facet, &outerplane, NULL);
+    offset= facet->offset - outerplane;
+  LABELprintnorm:
+    if (!facet->normal) {
+      qh_fprintf(qh, fp, 9017, "no normal for facet f%d\n", facet->id);
+      break;
+    }
+    if (qh->CDDoutput) {
+      qh_fprintf(qh, fp, 9018, qh_REAL_1, -offset);
+      for (k=0; k < qh->hull_dim; k++)
+        qh_fprintf(qh, fp, 9019, qh_REAL_1, -facet->normal[k]);
+    }else {
+      for (k=0; k < qh->hull_dim; k++)
+        qh_fprintf(qh, fp, 9020, qh_REAL_1, facet->normal[k]);
+      qh_fprintf(qh, fp, 9021, qh_REAL_1, offset);
+    }
+    qh_fprintf(qh, fp, 9022, "\n");
+    break;
+  case qh_PRINTmathematica:  /* either 2 or 3-d by qh_printbegin */
+  case qh_PRINTmaple:
+    if (qh->hull_dim == 2)
+      qh_printfacet2math(qh, fp, facet, format, qh->printoutvar++);
+    else
+      qh_printfacet3math(qh, fp, facet, format, qh->printoutvar++);
+    break;
+  case qh_PRINTneighbors:
+    qh_fprintf(qh, fp, 9023, "%d", qh_setsize(qh, facet->neighbors));
+    FOREACHneighbor_(facet)
+      qh_fprintf(qh, fp, 9024, " %d",
+               neighbor->visitid ? neighbor->visitid - 1: 0 - neighbor->id);
+    qh_fprintf(qh, fp, 9025, "\n");
+    break;
+  case qh_PRINTpointintersect:
+    if (!qh->feasible_point) {
+      qh_fprintf(qh, qh->ferr, 6067, "qhull input error (qh_printafacet): option 'Fp' needs qh->feasible_point\n");
+      qh_errexit(qh, qh_ERRinput, NULL, NULL);
+    }
+    if (facet->offset > 0)
+      goto LABELprintinfinite;
+    point= coordp= (coordT*)qh_memalloc(qh, qh->normal_size);
+    normp= facet->normal;
+    feasiblep= qh->feasible_point;
+    if (facet->offset < -qh->MINdenom) {
+      for (k=qh->hull_dim; k--; )
+        *(coordp++)= (*(normp++) / - facet->offset) + *(feasiblep++);
+    }else {
+      for (k=qh->hull_dim; k--; ) {
+        *(coordp++)= qh_divzero(*(normp++), facet->offset, qh->MINdenom_1,
+                                 &zerodiv) + *(feasiblep++);
+        if (zerodiv) {
+          qh_memfree(qh, point, qh->normal_size);
+          goto LABELprintinfinite;
+        }
+      }
+    }
+    qh_printpoint(qh, fp, NULL, point);
+    qh_memfree(qh, point, qh->normal_size);
+    break;
+  LABELprintinfinite:
+    for (k=qh->hull_dim; k--; )
+      qh_fprintf(qh, fp, 9026, qh_REAL_1, qh_INFINITE);
+    qh_fprintf(qh, fp, 9027, "\n");
+    break;
+  case qh_PRINTpointnearest:
+    FOREACHpoint_(facet->coplanarset) {
+      int id, id2;
+      vertex= qh_nearvertex(qh, facet, point, &dist);
+      id= qh_pointid(qh, vertex->point);
+      id2= qh_pointid(qh, point);
+      qh_fprintf(qh, fp, 9028, "%d %d %d " qh_REAL_1 "\n", id, id2, facet->id, dist);
+    }
+    break;
+  case qh_PRINTpoints:  /* VORONOI only by qh_printbegin */
+    if (qh->CDDoutput)
+      qh_fprintf(qh, fp, 9029, "1 ");
+    qh_printcenter(qh, fp, format, NULL, facet);
+    break;
+  case qh_PRINTvertices:
+    qh_fprintf(qh, fp, 9030, "%d", qh_setsize(qh, facet->vertices));
+    FOREACHvertex_(facet->vertices)
+      qh_fprintf(qh, fp, 9031, " %d", qh_pointid(qh, vertex->point));
+    qh_fprintf(qh, fp, 9032, "\n");
+    break;
+  default:
+    break;
+  }
+} /* printafacet */
+
+/*-<a                             href="qh-io_r.htm#TOC"
+  >-------------------------------</a><a name="printbegin">-</a>
+
+  qh_printbegin(qh, )
+    prints header for all output formats
+
+  returns:
+    checks for valid format
+
+  notes:
+    uses qh.visit_id for 3/4off
+    changes qh.interior_point if printing centrums
+    qh_countfacets clears facet->visitid for non-good facets
+
+  see
+    qh_printend() and qh_printafacet()
+
+  design:
+    count facets and related statistics
+    print header for format
+*/
+void qh_printbegin(qhT *qh, FILE *fp, qh_PRINT format, facetT *facetlist, setT *facets, boolT printall) {
+  int numfacets, numsimplicial, numridges, totneighbors, numcoplanars, numtricoplanars;
+  int i, num;
+  facetT *facet, **facetp;
+  vertexT *vertex, **vertexp;
+  setT *vertices;
+  pointT *point, **pointp, *pointtemp;
+
+  qh->printoutnum= 0;
+  qh_countfacets(qh, facetlist, facets, printall, &numfacets, &numsimplicial,
+      &totneighbors, &numridges, &numcoplanars, &numtricoplanars);
+  switch (format) {
+  case qh_PRINTnone:
+    break;
+  case qh_PRINTarea:
+    qh_fprintf(qh, fp, 9033, "%d\n", numfacets);
+    break;
+  case qh_PRINTcoplanars:
+    qh_fprintf(qh, fp, 9034, "%d\n", numfacets);
+    break;
+  case qh_PRINTcentrums:
+    if (qh->CENTERtype == qh_ASnone)
+      qh_clearcenters(qh, qh_AScentrum);
+    qh_fprintf(qh, fp, 9035, "%d\n%d\n", qh->hull_dim, numfacets);
+    break;
+  case qh_PRINTfacets:
+  case qh_PRINTfacets_xridge:
+    if (facetlist)
+      qh_printvertexlist(qh, fp, "Vertices and facets:\n", facetlist, facets, printall);
+    break;
+  case qh_PRINTgeom:
+    if (qh->hull_dim > 4)  /* qh_initqhull_globals also checks */
+      goto LABELnoformat;
+    if (qh->VORONOI && qh->hull_dim > 3)  /* PRINTdim == DROPdim == hull_dim-1 */
+      goto LABELnoformat;
+    if (qh->hull_dim == 2 && (qh->PRINTridges || qh->DOintersections))
+      qh_fprintf(qh, qh->ferr, 7049, "qhull warning: output for ridges and intersections not implemented in 2-d\n");
+    if (qh->hull_dim == 4 && (qh->PRINTinner || qh->PRINTouter ||
+                             (qh->PRINTdim == 4 && qh->PRINTcentrums)))
+      qh_fprintf(qh, qh->ferr, 7050, "qhull warning: output for outer/inner planes and centrums not implemented in 4-d\n");
+    if (qh->PRINTdim == 4 && (qh->PRINTspheres))
+      qh_fprintf(qh, qh->ferr, 7051, "qhull warning: output for vertices not implemented in 4-d\n");
+    if (qh->PRINTdim == 4 && qh->DOintersections && qh->PRINTnoplanes)
+      qh_fprintf(qh, qh->ferr, 7052, "qhull warning: 'Gnh' generates no output in 4-d\n");
+    if (qh->PRINTdim == 2) {
+      qh_fprintf(qh, fp, 9036, "{appearance {linewidth 3} LIST # %s | %s\n",
+              qh->rbox_command, qh->qhull_command);
+    }else if (qh->PRINTdim == 3) {
+      qh_fprintf(qh, fp, 9037, "{appearance {+edge -evert linewidth 2} LIST # %s | %s\n",
+              qh->rbox_command, qh->qhull_command);
+    }else if (qh->PRINTdim == 4) {
+      qh->visit_id++;
+      num= 0;
+      FORALLfacet_(facetlist)    /* get number of ridges to be printed */
+        qh_printend4geom(qh, NULL, facet, &num, printall);
+      FOREACHfacet_(facets)
+        qh_printend4geom(qh, NULL, facet, &num, printall);
+      qh->ridgeoutnum= num;
+      qh->printoutvar= 0;  /* counts number of ridges in output */
+      qh_fprintf(qh, fp, 9038, "LIST # %s | %s\n", qh->rbox_command, qh->qhull_command);
+    }
+
+    if (qh->PRINTdots) {
+      qh->printoutnum++;
+      num= qh->num_points + qh_setsize(qh, qh->other_points);
+      if (qh->DELAUNAY && qh->ATinfinity)
+        num--;
+      if (qh->PRINTdim == 4)
+        qh_fprintf(qh, fp, 9039, "4VECT %d %d 1\n", num, num);
+      else
+        qh_fprintf(qh, fp, 9040, "VECT %d %d 1\n", num, num);
+
+      for (i=num; i--; ) {
+        if (i % 20 == 0)
+          qh_fprintf(qh, fp, 9041, "\n");
+        qh_fprintf(qh, fp, 9042, "1 ");
+      }
+      qh_fprintf(qh, fp, 9043, "# 1 point per line\n1 ");
+      for (i=num-1; i--; ) { /* num at least 3 for D2 */
+        if (i % 20 == 0)
+          qh_fprintf(qh, fp, 9044, "\n");
+        qh_fprintf(qh, fp, 9045, "0 ");
+      }
+      qh_fprintf(qh, fp, 9046, "# 1 color for all\n");
+      FORALLpoints {
+        if (!qh->DELAUNAY || !qh->ATinfinity || qh_pointid(qh, point) != qh->num_points-1) {
+          if (qh->PRINTdim == 4)
+            qh_printpoint(qh, fp, NULL, point);
+            else
+              qh_printpoint3(qh, fp, point);
+        }
+      }
+      FOREACHpoint_(qh->other_points) {
+        if (qh->PRINTdim == 4)
+          qh_printpoint(qh, fp, NULL, point);
+        else
+          qh_printpoint3(qh, fp, point);
+      }
+      qh_fprintf(qh, fp, 9047, "0 1 1 1  # color of points\n");
+    }
+
+    if (qh->PRINTdim == 4  && !qh->PRINTnoplanes)
+      /* 4dview loads up multiple 4OFF objects slowly */
+      qh_fprintf(qh, fp, 9048, "4OFF %d %d 1\n", 3*qh->ridgeoutnum, qh->ridgeoutnum);
+    qh->PRINTcradius= 2 * qh->DISTround;  /* include test DISTround */
+    if (qh->PREmerge) {
+      maximize_(qh->PRINTcradius, qh->premerge_centrum + qh->DISTround);
+    }else if (qh->POSTmerge)
+      maximize_(qh->PRINTcradius, qh->postmerge_centrum + qh->DISTround);
+    qh->PRINTradius= qh->PRINTcradius;
+    if (qh->PRINTspheres + qh->PRINTcoplanar)
+      maximize_(qh->PRINTradius, qh->MAXabs_coord * qh_MINradius);
+    if (qh->premerge_cos < REALmax/2) {
+      maximize_(qh->PRINTradius, (1- qh->premerge_cos) * qh->MAXabs_coord);
+    }else if (!qh->PREmerge && qh->POSTmerge && qh->postmerge_cos < REALmax/2) {
+      maximize_(qh->PRINTradius, (1- qh->postmerge_cos) * qh->MAXabs_coord);
+    }
+    maximize_(qh->PRINTradius, qh->MINvisible);
+    if (qh->JOGGLEmax < REALmax/2)
+      qh->PRINTradius += qh->JOGGLEmax * sqrt((realT)qh->hull_dim);
+    if (qh->PRINTdim != 4 &&
+        (qh->PRINTcoplanar || qh->PRINTspheres || qh->PRINTcentrums)) {
+      vertices= qh_facetvertices(qh, facetlist, facets, printall);
+      if (qh->PRINTspheres && qh->PRINTdim <= 3)
+        qh_printspheres(qh, fp, vertices, qh->PRINTradius);
+      if (qh->PRINTcoplanar || qh->PRINTcentrums) {
+        qh->firstcentrum= True;
+        if (qh->PRINTcoplanar&& !qh->PRINTspheres) {
+          FOREACHvertex_(vertices)
+            qh_printpointvect2(qh, fp, vertex->point, NULL, qh->interior_point, qh->PRINTradius);
+        }
+        FORALLfacet_(facetlist) {
+          if (!printall && qh_skipfacet(qh, facet))
+            continue;
+          if (!facet->normal)
+            continue;
+          if (qh->PRINTcentrums && qh->PRINTdim <= 3)
+            qh_printcentrum(qh, fp, facet, qh->PRINTcradius);
+          if (!qh->PRINTcoplanar)
+            continue;
+          FOREACHpoint_(facet->coplanarset)
+            qh_printpointvect2(qh, fp, point, facet->normal, NULL, qh->PRINTradius);
+          FOREACHpoint_(facet->outsideset)
+            qh_printpointvect2(qh, fp, point, facet->normal, NULL, qh->PRINTradius);
+        }
+        FOREACHfacet_(facets) {
+          if (!printall && qh_skipfacet(qh, facet))
+            continue;
+          if (!facet->normal)
+            continue;
+          if (qh->PRINTcentrums && qh->PRINTdim <= 3)
+            qh_printcentrum(qh, fp, facet, qh->PRINTcradius);
+          if (!qh->PRINTcoplanar)
+            continue;
+          FOREACHpoint_(facet->coplanarset)
+            qh_printpointvect2(qh, fp, point, facet->normal, NULL, qh->PRINTradius);
+          FOREACHpoint_(facet->outsideset)
+            qh_printpointvect2(qh, fp, point, facet->normal, NULL, qh->PRINTradius);
+        }
+      }
+      qh_settempfree(qh, &vertices);
+    }
+    qh->visit_id++; /* for printing hyperplane intersections */
+    break;
+  case qh_PRINTids:
+    qh_fprintf(qh, fp, 9049, "%d\n", numfacets);
+    break;
+  case qh_PRINTincidences:
+    if (qh->VORONOI && qh->PRINTprecision)
+      qh_fprintf(qh, qh->ferr, 7053, "qhull warning: writing Delaunay.  Use 'p' or 'o' for Voronoi centers\n");
+    qh->printoutvar= qh->vertex_id;  /* centrum id for non-simplicial facets */
+    if (qh->hull_dim <= 3)
+      qh_fprintf(qh, fp, 9050, "%d\n", numfacets);
+    else
+      qh_fprintf(qh, fp, 9051, "%d\n", numsimplicial+numridges);
+    break;
+  case qh_PRINTinner:
+  case qh_PRINTnormals:
+  case qh_PRINTouter:
+    if (qh->CDDoutput)
+      qh_fprintf(qh, fp, 9052, "%s | %s\nbegin\n    %d %d real\n", qh->rbox_command,
+            qh->qhull_command, numfacets, qh->hull_dim+1);
+    else
+      qh_fprintf(qh, fp, 9053, "%d\n%d\n", qh->hull_dim+1, numfacets);
+    break;
+  case qh_PRINTmathematica:
+  case qh_PRINTmaple:
+    if (qh->hull_dim > 3)  /* qh_initbuffers also checks */
+      goto LABELnoformat;
+    if (qh->VORONOI)
+      qh_fprintf(qh, qh->ferr, 7054, "qhull warning: output is the Delaunay triangulation\n");
+    if (format == qh_PRINTmaple) {
+      if (qh->hull_dim == 2)
+        qh_fprintf(qh, fp, 9054, "PLOT(CURVES(\n");
+      else
+        qh_fprintf(qh, fp, 9055, "PLOT3D(POLYGONS(\n");
+    }else
+      qh_fprintf(qh, fp, 9056, "{\n");
+    qh->printoutvar= 0;   /* counts number of facets for notfirst */
+    break;
+  case qh_PRINTmerges:
+    qh_fprintf(qh, fp, 9057, "%d\n", numfacets);
+    break;
+  case qh_PRINTpointintersect:
+    qh_fprintf(qh, fp, 9058, "%d\n%d\n", qh->hull_dim, numfacets);
+    break;
+  case qh_PRINTneighbors:
+    qh_fprintf(qh, fp, 9059, "%d\n", numfacets);
+    break;
+  case qh_PRINToff:
+  case qh_PRINTtriangles:
+    if (qh->VORONOI)
+      goto LABELnoformat;
+    num = qh->hull_dim;
+    if (format == qh_PRINToff || qh->hull_dim == 2)
+      qh_fprintf(qh, fp, 9060, "%d\n%d %d %d\n", num,
+        qh->num_points+qh_setsize(qh, qh->other_points), numfacets, totneighbors/2);
+    else { /* qh_PRINTtriangles */
+      qh->printoutvar= qh->num_points+qh_setsize(qh, qh->other_points); /* first centrum */
+      if (qh->DELAUNAY)
+        num--;  /* drop last dimension */
+      qh_fprintf(qh, fp, 9061, "%d\n%d %d %d\n", num, qh->printoutvar
+        + numfacets - numsimplicial, numsimplicial + numridges, totneighbors/2);
+    }
+    FORALLpoints
+      qh_printpointid(qh, qh->fout, NULL, num, point, qh_IDunknown);
+    FOREACHpoint_(qh->other_points)
+      qh_printpointid(qh, qh->fout, NULL, num, point, qh_IDunknown);
+    if (format == qh_PRINTtriangles && qh->hull_dim > 2) {
+      FORALLfacets {
+        if (!facet->simplicial && facet->visitid)
+          qh_printcenter(qh, qh->fout, format, NULL, facet);
+      }
+    }
+    break;
+  case qh_PRINTpointnearest:
+    qh_fprintf(qh, fp, 9062, "%d\n", numcoplanars);
+    break;
+  case qh_PRINTpoints:
+    if (!qh->VORONOI)
+      goto LABELnoformat;
+    if (qh->CDDoutput)
+      qh_fprintf(qh, fp, 9063, "%s | %s\nbegin\n%d %d real\n", qh->rbox_command,
+           qh->qhull_command, numfacets, qh->hull_dim);
+    else
+      qh_fprintf(qh, fp, 9064, "%d\n%d\n", qh->hull_dim-1, numfacets);
+    break;
+  case qh_PRINTvertices:
+    qh_fprintf(qh, fp, 9065, "%d\n", numfacets);
+    break;
+  case qh_PRINTsummary:
+  default:
+  LABELnoformat:
+    qh_fprintf(qh, qh->ferr, 6068, "qhull internal error (qh_printbegin): can not use this format for dimension %d\n",
+         qh->hull_dim);
+    qh_errexit(qh, qh_ERRqhull, NULL, NULL);
+  }
+} /* printbegin */
+
+/*-<a                             href="qh-io_r.htm#TOC"
+  >-------------------------------</a><a name="printcenter">-</a>
+
+  qh_printcenter(qh, fp, string, facet )
+    print facet->center as centrum or Voronoi center
+    string may be NULL.  Don't include '%' codes.
+    nop if qh->CENTERtype neither CENTERvoronoi nor CENTERcentrum
+    if upper envelope of Delaunay triangulation and point at-infinity
+      prints qh_INFINITE instead;
+
+  notes:
+    defines facet->center if needed
+    if format=PRINTgeom, adds a 0 if would otherwise be 2-d
+    Same as QhullFacet::printCenter
+*/
+void qh_printcenter(qhT *qh, FILE *fp, qh_PRINT format, const char *string, facetT *facet) {
+  int k, num;
+
+  if (qh->CENTERtype != qh_ASvoronoi && qh->CENTERtype != qh_AScentrum)
+    return;
+  if (string)
+    qh_fprintf(qh, fp, 9066, string);
+  if (qh->CENTERtype == qh_ASvoronoi) {
+    num= qh->hull_dim-1;
+    if (!facet->normal || !facet->upperdelaunay || !qh->ATinfinity) {
+      if (!facet->center)
+        facet->center= qh_facetcenter(qh, facet->vertices);
+      for (k=0; k < num; k++)
+        qh_fprintf(qh, fp, 9067, qh_REAL_1, facet->center[k]);
+    }else {
+      for (k=0; k < num; k++)
+        qh_fprintf(qh, fp, 9068, qh_REAL_1, qh_INFINITE);
+    }
+  }else /* qh->CENTERtype == qh_AScentrum */ {
+    num= qh->hull_dim;
+    if (format == qh_PRINTtriangles && qh->DELAUNAY)
+      num--;
+    if (!facet->center)
+      facet->center= qh_getcentrum(qh, facet);
+    for (k=0; k < num; k++)
+      qh_fprintf(qh, fp, 9069, qh_REAL_1, facet->center[k]);
+  }
+  if (format == qh_PRINTgeom && num == 2)
+    qh_fprintf(qh, fp, 9070, " 0\n");
+  else
+    qh_fprintf(qh, fp, 9071, "\n");
+} /* printcenter */
+
+/*-<a                             href="qh-io_r.htm#TOC"
+  >-------------------------------</a><a name="printcentrum">-</a>
+
+  qh_printcentrum(qh, fp, facet, radius )
+    print centrum for a facet in OOGL format
+    radius defines size of centrum
+    2-d or 3-d only
+
+  returns:
+    defines facet->center if needed
+*/
+void qh_printcentrum(qhT *qh, FILE *fp, facetT *facet, realT radius) {
+  pointT *centrum, *projpt;
+  boolT tempcentrum= False;
+  realT xaxis[4], yaxis[4], normal[4], dist;
+  realT green[3]={0, 1, 0};
+  vertexT *apex;
+  int k;
+
+  if (qh->CENTERtype == qh_AScentrum) {
+    if (!facet->center)
+      facet->center= qh_getcentrum(qh, facet);
+    centrum= facet->center;
+  }else {
+    centrum= qh_getcentrum(qh, facet);
+    tempcentrum= True;
+  }
+  qh_fprintf(qh, fp, 9072, "{appearance {-normal -edge normscale 0} ");
+  if (qh->firstcentrum) {
+    qh->firstcentrum= False;
+    qh_fprintf(qh, fp, 9073, "{INST geom { define centrum CQUAD  # f%d\n\
+-0.3 -0.3 0.0001     0 0 1 1\n\
+ 0.3 -0.3 0.0001     0 0 1 1\n\
+ 0.3  0.3 0.0001     0 0 1 1\n\
+-0.3  0.3 0.0001     0 0 1 1 } transform { \n", facet->id);
+  }else
+    qh_fprintf(qh, fp, 9074, "{INST geom { : centrum } transform { # f%d\n", facet->id);
+  apex= SETfirstt_(facet->vertices, vertexT);
+  qh_distplane(qh, apex->point, facet, &dist);
+  projpt= qh_projectpoint(qh, apex->point, facet, dist);
+  for (k=qh->hull_dim; k--; ) {
+    xaxis[k]= projpt[k] - centrum[k];
+    normal[k]= facet->normal[k];
+  }
+  if (qh->hull_dim == 2) {
+    xaxis[2]= 0;
+    normal[2]= 0;
+  }else if (qh->hull_dim == 4) {
+    qh_projectdim3(qh, xaxis, xaxis);
+    qh_projectdim3(qh, normal, normal);
+    qh_normalize2(qh, normal, qh->PRINTdim, True, NULL, NULL);
+  }
+  qh_crossproduct(3, xaxis, normal, yaxis);
+  qh_fprintf(qh, fp, 9075, "%8.4g %8.4g %8.4g 0\n", xaxis[0], xaxis[1], xaxis[2]);
+  qh_fprintf(qh, fp, 9076, "%8.4g %8.4g %8.4g 0\n", yaxis[0], yaxis[1], yaxis[2]);
+  qh_fprintf(qh, fp, 9077, "%8.4g %8.4g %8.4g 0\n", normal[0], normal[1], normal[2]);
+  qh_printpoint3(qh, fp, centrum);
+  qh_fprintf(qh, fp, 9078, "1 }}}\n");
+  qh_memfree(qh, projpt, qh->normal_size);
+  qh_printpointvect(qh, fp, centrum, facet->normal, NULL, radius, green);
+  if (tempcentrum)
+    qh_memfree(qh, centrum, qh->normal_size);
+} /* printcentrum */
+
+/*-<a                             href="qh-io_r.htm#TOC"
+  >-------------------------------</a><a name="printend">-</a>
+
+  qh_printend(qh, fp, format )
+    prints trailer for all output formats
+
+  see:
+    qh_printbegin() and qh_printafacet()
+
+*/
+void qh_printend(qhT *qh, FILE *fp, qh_PRINT format, facetT *facetlist, setT *facets, boolT printall) {
+  int num;
+  facetT *facet, **facetp;
+
+  if (!qh->printoutnum)
+    qh_fprintf(qh, qh->ferr, 7055, "qhull warning: no facets printed\n");
+  switch (format) {
+  case qh_PRINTgeom:
+    if (qh->hull_dim == 4 && qh->DROPdim < 0  && !qh->PRINTnoplanes) {
+      qh->visit_id++;
+      num= 0;
+      FORALLfacet_(facetlist)
+        qh_printend4geom(qh, fp, facet,&num, printall);
+      FOREACHfacet_(facets)
+        qh_printend4geom(qh, fp, facet, &num, printall);
+      if (num != qh->ridgeoutnum || qh->printoutvar != qh->ridgeoutnum) {
+        qh_fprintf(qh, qh->ferr, 6069, "qhull internal error (qh_printend): number of ridges %d != number printed %d and at end %d\n", qh->ridgeoutnum, qh->printoutvar, num);
+        qh_errexit(qh, qh_ERRqhull, NULL, NULL);
+      }
+    }else
+      qh_fprintf(qh, fp, 9079, "}\n");
+    break;
+  case qh_PRINTinner:
+  case qh_PRINTnormals:
+  case qh_PRINTouter:
+    if (qh->CDDoutput)
+      qh_fprintf(qh, fp, 9080, "end\n");
+    break;
+  case qh_PRINTmaple:
+    qh_fprintf(qh, fp, 9081, "));\n");
+    break;
+  case qh_PRINTmathematica:
+    qh_fprintf(qh, fp, 9082, "}\n");
+    break;
+  case qh_PRINTpoints:
+    if (qh->CDDoutput)
+      qh_fprintf(qh, fp, 9083, "end\n");
+    break;
+  default:
+    break;
+  }
+} /* printend */
+
+/*-<a                             href="qh-io_r.htm#TOC"
+  >-------------------------------</a><a name="printend4geom">-</a>
+
+  qh_printend4geom(qh, fp, facet, numridges, printall )
+    helper function for qh_printbegin/printend
+
+  returns:
+    number of printed ridges
+
+  notes:
+    just counts printed ridges if fp=NULL
+    uses facet->visitid
+    must agree with qh_printfacet4geom...
+
+  design:
+    computes color for facet from its normal
+    prints each ridge of facet
+*/
+void qh_printend4geom(qhT *qh, FILE *fp, facetT *facet, int *nump, boolT printall) {
+  realT color[3];
+  int i, num= *nump;
+  facetT *neighbor, **neighborp;
+  ridgeT *ridge, **ridgep;
+
+  if (!printall && qh_skipfacet(qh, facet))
+    return;
+  if (qh->PRINTnoplanes || (facet->visible && qh->NEWfacets))
+    return;
+  if (!facet->normal)
+    return;
+  if (fp) {
+    for (i=0; i < 3; i++) {
+      color[i]= (facet->normal[i]+1.0)/2.0;
+      maximize_(color[i], -1.0);
+      minimize_(color[i], +1.0);
+    }
+  }
+  facet->visitid= qh->visit_id;
+  if (facet->simplicial) {
+    FOREACHneighbor_(facet) {
+      if (neighbor->visitid != qh->visit_id) {
+        if (fp)
+          qh_fprintf(qh, fp, 9084, "3 %d %d %d %8.4g %8.4g %8.4g 1 # f%d f%d\n",
+                 3*num, 3*num+1, 3*num+2, color[0], color[1], color[2],
+                 facet->id, neighbor->id);
+        num++;
+      }
+    }
+  }else {
+    FOREACHridge_(facet->ridges) {
+      neighbor= otherfacet_(ridge, facet);
+      if (neighbor->visitid != qh->visit_id) {
+        if (fp)
+          qh_fprintf(qh, fp, 9085, "3 %d %d %d %8.4g %8.4g %8.4g 1 #r%d f%d f%d\n",
+                 3*num, 3*num+1, 3*num+2, color[0], color[1], color[2],
+                 ridge->id, facet->id, neighbor->id);
+        num++;
+      }
+    }
+  }
+  *nump= num;
+} /* printend4geom */
+
+/*-<a                             href="qh-io_r.htm#TOC"
+  >-------------------------------</a><a name="printextremes">-</a>
+
+  qh_printextremes(qh, fp, facetlist, facets, printall )
+    print extreme points for convex hulls or halfspace intersections
+
+  notes:
+    #points, followed by ids, one per line
+
+    sorted by id
+    same order as qh_printpoints_out if no coplanar/interior points
+*/
+void qh_printextremes(qhT *qh, FILE *fp, facetT *facetlist, setT *facets, boolT printall) {
+  setT *vertices, *points;
+  pointT *point;
+  vertexT *vertex, **vertexp;
+  int id;
+  int numpoints=0, point_i, point_n;
+  int allpoints= qh->num_points + qh_setsize(qh, qh->other_points);
+
+  points= qh_settemp(qh, allpoints);
+  qh_setzero(qh, points, 0, allpoints);
+  vertices= qh_facetvertices(qh, facetlist, facets, printall);
+  FOREACHvertex_(vertices) {
+    id= qh_pointid(qh, vertex->point);
+    if (id >= 0) {
+      SETelem_(points, id)= vertex->point;
+      numpoints++;
+    }
+  }
+  qh_settempfree(qh, &vertices);
+  qh_fprintf(qh, fp, 9086, "%d\n", numpoints);
+  FOREACHpoint_i_(qh, points) {
+    if (point)
+      qh_fprintf(qh, fp, 9087, "%d\n", point_i);
+  }
+  qh_settempfree(qh, &points);
+} /* printextremes */
+
+/*-<a                             href="qh-io_r.htm#TOC"
+  >-------------------------------</a><a name="printextremes_2d">-</a>
+
+  qh_printextremes_2d(qh, fp, facetlist, facets, printall )
+    prints point ids for facets in qh_ORIENTclock order
+
+  notes:
+    #points, followed by ids, one per line
+    if facetlist/facets are disjoint than the output includes skips
+    errors if facets form a loop
+    does not print coplanar points
+*/
+void qh_printextremes_2d(qhT *qh, FILE *fp, facetT *facetlist, setT *facets, boolT printall) {
+  int numfacets, numridges, totneighbors, numcoplanars, numsimplicial, numtricoplanars;
+  setT *vertices;
+  facetT *facet, *startfacet, *nextfacet;
+  vertexT *vertexA, *vertexB;
+
+  qh_countfacets(qh, facetlist, facets, printall, &numfacets, &numsimplicial,
+      &totneighbors, &numridges, &numcoplanars, &numtricoplanars); /* marks qh->visit_id */
+  vertices= qh_facetvertices(qh, facetlist, facets, printall);
+  qh_fprintf(qh, fp, 9088, "%d\n", qh_setsize(qh, vertices));
+  qh_settempfree(qh, &vertices);
+  if (!numfacets)
+    return;
+  facet= startfacet= facetlist ? facetlist : SETfirstt_(facets, facetT);
+  qh->vertex_visit++;
+  qh->visit_id++;
+  do {
+    if (facet->toporient ^ qh_ORIENTclock) {
+      vertexA= SETfirstt_(facet->vertices, vertexT);
+      vertexB= SETsecondt_(facet->vertices, vertexT);
+      nextfacet= SETfirstt_(facet->neighbors, facetT);
+    }else {
+      vertexA= SETsecondt_(facet->vertices, vertexT);
+      vertexB= SETfirstt_(facet->vertices, vertexT);
+      nextfacet= SETsecondt_(facet->neighbors, facetT);
+    }
+    if (facet->visitid == qh->visit_id) {
+      qh_fprintf(qh, qh->ferr, 6218, "Qhull internal error (qh_printextremes_2d): loop in facet list.  facet %d nextfacet %d\n",
+                 facet->id, nextfacet->id);
+      qh_errexit2(qh, qh_ERRqhull, facet, nextfacet);
+    }
+    if (facet->visitid) {
+      if (vertexA->visitid != qh->vertex_visit) {
+        vertexA->visitid= qh->vertex_visit;
+        qh_fprintf(qh, fp, 9089, "%d\n", qh_pointid(qh, vertexA->point));
+      }
+      if (vertexB->visitid != qh->vertex_visit) {
+        vertexB->visitid= qh->vertex_visit;
+        qh_fprintf(qh, fp, 9090, "%d\n", qh_pointid(qh, vertexB->point));
+      }
+    }
+    facet->visitid= qh->visit_id;
+    facet= nextfacet;
+  }while (facet && facet != startfacet);
+} /* printextremes_2d */
+
+/*-<a                             href="qh-io_r.htm#TOC"
+  >-------------------------------</a><a name="printextremes_d">-</a>
+
+  qh_printextremes_d(qh, fp, facetlist, facets, printall )
+    print extreme points of input sites for Delaunay triangulations
+
+  notes:
+    #points, followed by ids, one per line
+
+    unordered
+*/
+void qh_printextremes_d(qhT *qh, FILE *fp, facetT *facetlist, setT *facets, boolT printall) {
+  setT *vertices;
+  vertexT *vertex, **vertexp;
+  boolT upperseen, lowerseen;
+  facetT *neighbor, **neighborp;
+  int numpoints=0;
+
+  vertices= qh_facetvertices(qh, facetlist, facets, printall);
+  qh_vertexneighbors(qh);
+  FOREACHvertex_(vertices) {
+    upperseen= lowerseen= False;
+    FOREACHneighbor_(vertex) {
+      if (neighbor->upperdelaunay)
+        upperseen= True;
+      else
+        lowerseen= True;
+    }
+    if (upperseen && lowerseen) {
+      vertex->seen= True;
+      numpoints++;
+    }else
+      vertex->seen= False;
+  }
+  qh_fprintf(qh, fp, 9091, "%d\n", numpoints);
+  FOREACHvertex_(vertices) {
+    if (vertex->seen)
+      qh_fprintf(qh, fp, 9092, "%d\n", qh_pointid(qh, vertex->point));
+  }
+  qh_settempfree(qh, &vertices);
+} /* printextremes_d */
+
+/*-<a                             href="qh-io_r.htm#TOC"
+  >-------------------------------</a><a name="printfacet">-</a>
+
+  qh_printfacet(qh, fp, facet )
+    prints all fields of a facet to fp
+
+  notes:
+    ridges printed in neighbor order
+*/
+void qh_printfacet(qhT *qh, FILE *fp, facetT *facet) {
+
+  qh_printfacetheader(qh, fp, facet);
+  if (facet->ridges)
+    qh_printfacetridges(qh, fp, facet);
+} /* printfacet */
+
+
+/*-<a                             href="qh-io_r.htm#TOC"
+  >-------------------------------</a><a name="printfacet2geom">-</a>
+
+  qh_printfacet2geom(qh, fp, facet, color )
+    print facet as part of a 2-d VECT for Geomview
+
+    notes:
+      assume precise calculations in io_r.c with roundoff covered by qh_GEOMepsilon
+      mindist is calculated within io_r.c.  maxoutside is calculated elsewhere
+      so a DISTround error may have occurred.
+*/
+void qh_printfacet2geom(qhT *qh, FILE *fp, facetT *facet, realT color[3]) {
+  pointT *point0, *point1;
+  realT mindist, innerplane, outerplane;
+  int k;
+
+  qh_facet2point(qh, facet, &point0, &point1, &mindist);
+  qh_geomplanes(qh, facet, &outerplane, &innerplane);
+  if (qh->PRINTouter || (!qh->PRINTnoplanes && !qh->PRINTinner))
+    qh_printfacet2geom_points(qh, fp, point0, point1, facet, outerplane, color);
+  if (qh->PRINTinner || (!qh->PRINTnoplanes && !qh->PRINTouter &&
+                outerplane - innerplane > 2 * qh->MAXabs_coord * qh_GEOMepsilon)) {
+    for (k=3; k--; )
+      color[k]= 1.0 - color[k];
+    qh_printfacet2geom_points(qh, fp, point0, point1, facet, innerplane, color);
+  }
+  qh_memfree(qh, point1, qh->normal_size);
+  qh_memfree(qh, point0, qh->normal_size);
+} /* printfacet2geom */
+
+/*-<a                             href="qh-io_r.htm#TOC"
+  >-------------------------------</a><a name="printfacet2geom_points">-</a>
+
+  qh_printfacet2geom_points(qh, fp, point1, point2, facet, offset, color )
+    prints a 2-d facet as a VECT with 2 points at some offset.
+    The points are on the facet's plane.
+*/
+void qh_printfacet2geom_points(qhT *qh, FILE *fp, pointT *point1, pointT *point2,
+                               facetT *facet, realT offset, realT color[3]) {
+  pointT *p1= point1, *p2= point2;
+
+  qh_fprintf(qh, fp, 9093, "VECT 1 2 1 2 1 # f%d\n", facet->id);
+  if (offset != 0.0) {
+    p1= qh_projectpoint(qh, p1, facet, -offset);
+    p2= qh_projectpoint(qh, p2, facet, -offset);
+  }
+  qh_fprintf(qh, fp, 9094, "%8.4g %8.4g %8.4g\n%8.4g %8.4g %8.4g\n",
+           p1[0], p1[1], 0.0, p2[0], p2[1], 0.0);
+  if (offset != 0.0) {
+    qh_memfree(qh, p1, qh->normal_size);
+    qh_memfree(qh, p2, qh->normal_size);
+  }
+  qh_fprintf(qh, fp, 9095, "%8.4g %8.4g %8.4g 1.0\n", color[0], color[1], color[2]);
+} /* printfacet2geom_points */
+
+
+/*-<a                             href="qh-io_r.htm#TOC"
+  >-------------------------------</a><a name="printfacet2math">-</a>
+
+  qh_printfacet2math(qh, fp, facet, format, notfirst )
+    print 2-d Maple or Mathematica output for a facet
+    may be non-simplicial
+
+  notes:
+    use %16.8f since Mathematica 2.2 does not handle exponential format
+    see qh_printfacet3math
+*/
+void qh_printfacet2math(qhT *qh, FILE *fp, facetT *facet, qh_PRINT format, int notfirst) {
+  pointT *point0, *point1;
+  realT mindist;
+  const char *pointfmt;
+
+  qh_facet2point(qh, facet, &point0, &point1, &mindist);
+  if (notfirst)
+    qh_fprintf(qh, fp, 9096, ",");
+  if (format == qh_PRINTmaple)
+    pointfmt= "[[%16.8f, %16.8f], [%16.8f, %16.8f]]\n";
+  else
+    pointfmt= "Line[{{%16.8f, %16.8f}, {%16.8f, %16.8f}}]\n";
+  qh_fprintf(qh, fp, 9097, pointfmt, point0[0], point0[1], point1[0], point1[1]);
+  qh_memfree(qh, point1, qh->normal_size);
+  qh_memfree(qh, point0, qh->normal_size);
+} /* printfacet2math */
+
+
+/*-<a                             href="qh-io_r.htm#TOC"
+  >-------------------------------</a><a name="printfacet3geom_nonsimplicial">-</a>
+
+  qh_printfacet3geom_nonsimplicial(qh, fp, facet, color )
+    print Geomview OFF for a 3-d nonsimplicial facet.
+    if DOintersections, prints ridges to unvisited neighbors(qh->visit_id)
+
+  notes
+    uses facet->visitid for intersections and ridges
+*/
+void qh_printfacet3geom_nonsimplicial(qhT *qh, FILE *fp, facetT *facet, realT color[3]) {
+  ridgeT *ridge, **ridgep;
+  setT *projectedpoints, *vertices;
+  vertexT *vertex, **vertexp, *vertexA, *vertexB;
+  pointT *projpt, *point, **pointp;
+  facetT *neighbor;
+  realT dist, outerplane, innerplane;
+  int cntvertices, k;
+  realT black[3]={0, 0, 0}, green[3]={0, 1, 0};
+
+  qh_geomplanes(qh, facet, &outerplane, &innerplane);
+  vertices= qh_facet3vertex(qh, facet); /* oriented */
+  cntvertices= qh_setsize(qh, vertices);
+  projectedpoints= qh_settemp(qh, cntvertices);
+  FOREACHvertex_(vertices) {
+    zinc_(Zdistio);
+    qh_distplane(qh, vertex->point, facet, &dist);
+    projpt= qh_projectpoint(qh, vertex->point, facet, dist);
+    qh_setappend(qh, &projectedpoints, projpt);
+  }
+  if (qh->PRINTouter || (!qh->PRINTnoplanes && !qh->PRINTinner))
+    qh_printfacet3geom_points(qh, fp, projectedpoints, facet, outerplane, color);
+  if (qh->PRINTinner || (!qh->PRINTnoplanes && !qh->PRINTouter &&
+                outerplane - innerplane > 2 * qh->MAXabs_coord * qh_GEOMepsilon)) {
+    for (k=3; k--; )
+      color[k]= 1.0 - color[k];
+    qh_printfacet3geom_points(qh, fp, projectedpoints, facet, innerplane, color);
+  }
+  FOREACHpoint_(projectedpoints)
+    qh_memfree(qh, point, qh->normal_size);
+  qh_settempfree(qh, &projectedpoints);
+  qh_settempfree(qh, &vertices);
+  if ((qh->DOintersections || qh->PRINTridges)
+  && (!facet->visible || !qh->NEWfacets)) {
+    facet->visitid= qh->visit_id;
+    FOREACHridge_(facet->ridges) {
+      neighbor= otherfacet_(ridge, facet);
+      if (neighbor->visitid != qh->visit_id) {
+        if (qh->DOintersections)
+          qh_printhyperplaneintersection(qh, fp, facet, neighbor, ridge->vertices, black);
+        if (qh->PRINTridges) {
+          vertexA= SETfirstt_(ridge->vertices, vertexT);
+          vertexB= SETsecondt_(ridge->vertices, vertexT);
+          qh_printline3geom(qh, fp, vertexA->point, vertexB->point, green);
+        }
+      }
+    }
+  }
+} /* printfacet3geom_nonsimplicial */
+
+/*-<a                             href="qh-io_r.htm#TOC"
+  >-------------------------------</a><a name="printfacet3geom_points">-</a>
+
+  qh_printfacet3geom_points(qh, fp, points, facet, offset )
+    prints a 3-d facet as OFF Geomview object.
+    offset is relative to the facet's hyperplane
+    Facet is determined as a list of points
+*/
+void qh_printfacet3geom_points(qhT *qh, FILE *fp, setT *points, facetT *facet, realT offset, realT color[3]) {
+  int k, n= qh_setsize(qh, points), i;
+  pointT *point, **pointp;
+  setT *printpoints;
+
+  qh_fprintf(qh, fp, 9098, "{ OFF %d 1 1 # f%d\n", n, facet->id);
+  if (offset != 0.0) {
+    printpoints= qh_settemp(qh, n);
+    FOREACHpoint_(points)
+      qh_setappend(qh, &printpoints, qh_projectpoint(qh, point, facet, -offset));
+  }else
+    printpoints= points;
+  FOREACHpoint_(printpoints) {
+    for (k=0; k < qh->hull_dim; k++) {
+      if (k == qh->DROPdim)
+        qh_fprintf(qh, fp, 9099, "0 ");
+      else
+        qh_fprintf(qh, fp, 9100, "%8.4g ", point[k]);
+    }
+    if (printpoints != points)
+      qh_memfree(qh, point, qh->normal_size);
+    qh_fprintf(qh, fp, 9101, "\n");
+  }
+  if (printpoints != points)
+    qh_settempfree(qh, &printpoints);
+  qh_fprintf(qh, fp, 9102, "%d ", n);
+  for (i=0; i < n; i++)
+    qh_fprintf(qh, fp, 9103, "%d ", i);
+  qh_fprintf(qh, fp, 9104, "%8.4g %8.4g %8.4g 1.0 }\n", color[0], color[1], color[2]);
+} /* printfacet3geom_points */
+
+
+/*-<a                             href="qh-io_r.htm#TOC"
+  >-------------------------------</a><a name="printfacet3geom_simplicial">-</a>
+
+  qh_printfacet3geom_simplicial(qh, )
+    print Geomview OFF for a 3-d simplicial facet.
+
+  notes:
+    may flip color
+    uses facet->visitid for intersections and ridges
+
+    assume precise calculations in io_r.c with roundoff covered by qh_GEOMepsilon
+    innerplane may be off by qh->DISTround.  Maxoutside is calculated elsewhere
+    so a DISTround error may have occurred.
+*/
+void qh_printfacet3geom_simplicial(qhT *qh, FILE *fp, facetT *facet, realT color[3]) {
+  setT *points, *vertices;
+  vertexT *vertex, **vertexp, *vertexA, *vertexB;
+  facetT *neighbor, **neighborp;
+  realT outerplane, innerplane;
+  realT black[3]={0, 0, 0}, green[3]={0, 1, 0};
+  int k;
+
+  qh_geomplanes(qh, facet, &outerplane, &innerplane);
+  vertices= qh_facet3vertex(qh, facet);
+  points= qh_settemp(qh, qh->TEMPsize);
+  FOREACHvertex_(vertices)
+    qh_setappend(qh, &points, vertex->point);
+  if (qh->PRINTouter || (!qh->PRINTnoplanes && !qh->PRINTinner))
+    qh_printfacet3geom_points(qh, fp, points, facet, outerplane, color);
+  if (qh->PRINTinner || (!qh->PRINTnoplanes && !qh->PRINTouter &&
+              outerplane - innerplane > 2 * qh->MAXabs_coord * qh_GEOMepsilon)) {
+    for (k=3; k--; )
+      color[k]= 1.0 - color[k];
+    qh_printfacet3geom_points(qh, fp, points, facet, innerplane, color);
+  }
+  qh_settempfree(qh, &points);
+  qh_settempfree(qh, &vertices);
+  if ((qh->DOintersections || qh->PRINTridges)
+  && (!facet->visible || !qh->NEWfacets)) {
+    facet->visitid= qh->visit_id;
+    FOREACHneighbor_(facet) {
+      if (neighbor->visitid != qh->visit_id) {
+        vertices= qh_setnew_delnthsorted(qh, facet->vertices, qh->hull_dim,
+                          SETindex_(facet->neighbors, neighbor), 0);
+        if (qh->DOintersections)
+           qh_printhyperplaneintersection(qh, fp, facet, neighbor, vertices, black);
+        if (qh->PRINTridges) {
+          vertexA= SETfirstt_(vertices, vertexT);
+          vertexB= SETsecondt_(vertices, vertexT);
+          qh_printline3geom(qh, fp, vertexA->point, vertexB->point, green);
+        }
+        qh_setfree(qh, &vertices);
+      }
+    }
+  }
+} /* printfacet3geom_simplicial */
+
+/*-<a                             href="qh-io_r.htm#TOC"
+  >-------------------------------</a><a name="printfacet3math">-</a>
+
+  qh_printfacet3math(qh, fp, facet, notfirst )
+    print 3-d Maple or Mathematica output for a facet
+
+  notes:
+    may be non-simplicial
+    use %16.8f since Mathematica 2.2 does not handle exponential format
+    see qh_printfacet2math
+*/
+void qh_printfacet3math(qhT *qh, FILE *fp, facetT *facet, qh_PRINT format, int notfirst) {
+  vertexT *vertex, **vertexp;
+  setT *points, *vertices;
+  pointT *point, **pointp;
+  boolT firstpoint= True;
+  realT dist;
+  const char *pointfmt, *endfmt;
+
+  if (notfirst)
+    qh_fprintf(qh, fp, 9105, ",\n");
+  vertices= qh_facet3vertex(qh, facet);
+  points= qh_settemp(qh, qh_setsize(qh, vertices));
+  FOREACHvertex_(vertices) {
+    zinc_(Zdistio);
+    qh_distplane(qh, vertex->point, facet, &dist);
+    point= qh_projectpoint(qh, vertex->point, facet, dist);
+    qh_setappend(qh, &points, point);
+  }
+  if (format == qh_PRINTmaple) {
+    qh_fprintf(qh, fp, 9106, "[");
+    pointfmt= "[%16.8f, %16.8f, %16.8f]";
+    endfmt= "]";
+  }else {
+    qh_fprintf(qh, fp, 9107, "Polygon[{");
+    pointfmt= "{%16.8f, %16.8f, %16.8f}";
+    endfmt= "}]";
+  }
+  FOREACHpoint_(points) {
+    if (firstpoint)
+      firstpoint= False;
+    else
+      qh_fprintf(qh, fp, 9108, ",\n");
+    qh_fprintf(qh, fp, 9109, pointfmt, point[0], point[1], point[2]);
+  }
+  FOREACHpoint_(points)
+    qh_memfree(qh, point, qh->normal_size);
+  qh_settempfree(qh, &points);
+  qh_settempfree(qh, &vertices);
+  qh_fprintf(qh, fp, 9110, "%s", endfmt);
+} /* printfacet3math */
+
+
+/*-<a                             href="qh-io_r.htm#TOC"
+  >-------------------------------</a><a name="printfacet3vertex">-</a>
+
+  qh_printfacet3vertex(qh, fp, facet, format )
+    print vertices in a 3-d facet as point ids
+
+  notes:
+    prints number of vertices first if format == qh_PRINToff
+    the facet may be non-simplicial
+*/
+void qh_printfacet3vertex(qhT *qh, FILE *fp, facetT *facet, qh_PRINT format) {
+  vertexT *vertex, **vertexp;
+  setT *vertices;
+
+  vertices= qh_facet3vertex(qh, facet);
+  if (format == qh_PRINToff)
+    qh_fprintf(qh, fp, 9111, "%d ", qh_setsize(qh, vertices));
+  FOREACHvertex_(vertices)
+    qh_fprintf(qh, fp, 9112, "%d ", qh_pointid(qh, vertex->point));
+  qh_fprintf(qh, fp, 9113, "\n");
+  qh_settempfree(qh, &vertices);
+} /* printfacet3vertex */
+
+
+/*-<a                             href="qh-io_r.htm#TOC"
+  >-------------------------------</a><a name="printfacet4geom_nonsimplicial">-</a>
+
+  qh_printfacet4geom_nonsimplicial(qh, )
+    print Geomview 4OFF file for a 4d nonsimplicial facet
+    prints all ridges to unvisited neighbors (qh.visit_id)
+    if qh.DROPdim
+      prints in OFF format
+
+  notes:
+    must agree with printend4geom()
+*/
+void qh_printfacet4geom_nonsimplicial(qhT *qh, FILE *fp, facetT *facet, realT color[3]) {
+  facetT *neighbor;
+  ridgeT *ridge, **ridgep;
+  vertexT *vertex, **vertexp;
+  pointT *point;
+  int k;
+  realT dist;
+
+  facet->visitid= qh->visit_id;
+  if (qh->PRINTnoplanes || (facet->visible && qh->NEWfacets))
+    return;
+  FOREACHridge_(facet->ridges) {
+    neighbor= otherfacet_(ridge, facet);
+    if (neighbor->visitid == qh->visit_id)
+      continue;
+    if (qh->PRINTtransparent && !neighbor->good)
+      continue;
+    if (qh->DOintersections)
+      qh_printhyperplaneintersection(qh, fp, facet, neighbor, ridge->vertices, color);
+    else {
+      if (qh->DROPdim >= 0)
+        qh_fprintf(qh, fp, 9114, "OFF 3 1 1 # f%d\n", facet->id);
+      else {
+        qh->printoutvar++;
+        qh_fprintf(qh, fp, 9115, "# r%d between f%d f%d\n", ridge->id, facet->id, neighbor->id);
+      }
+      FOREACHvertex_(ridge->vertices) {
+        zinc_(Zdistio);
+        qh_distplane(qh, vertex->point,facet, &dist);
+        point=qh_projectpoint(qh, vertex->point,facet, dist);
+        for (k=0; k < qh->hull_dim; k++) {
+          if (k != qh->DROPdim)
+            qh_fprintf(qh, fp, 9116, "%8.4g ", point[k]);
+        }
+        qh_fprintf(qh, fp, 9117, "\n");
+        qh_memfree(qh, point, qh->normal_size);
+      }
+      if (qh->DROPdim >= 0)
+        qh_fprintf(qh, fp, 9118, "3 0 1 2 %8.4g %8.4g %8.4g\n", color[0], color[1], color[2]);
+    }
+  }
+} /* printfacet4geom_nonsimplicial */
+
+
+/*-<a                             href="qh-io_r.htm#TOC"
+  >-------------------------------</a><a name="printfacet4geom_simplicial">-</a>
+
+  qh_printfacet4geom_simplicial(qh, fp, facet, color )
+    print Geomview 4OFF file for a 4d simplicial facet
+    prints triangles for unvisited neighbors (qh.visit_id)
+
+  notes:
+    must agree with printend4geom()
+*/
+void qh_printfacet4geom_simplicial(qhT *qh, FILE *fp, facetT *facet, realT color[3]) {
+  setT *vertices;
+  facetT *neighbor, **neighborp;
+  vertexT *vertex, **vertexp;
+  int k;
+
+  facet->visitid= qh->visit_id;
+  if (qh->PRINTnoplanes || (facet->visible && qh->NEWfacets))
+    return;
+  FOREACHneighbor_(facet) {
+    if (neighbor->visitid == qh->visit_id)
+      continue;
+    if (qh->PRINTtransparent && !neighbor->good)
+      continue;
+    vertices= qh_setnew_delnthsorted(qh, facet->vertices, qh->hull_dim,
+                          SETindex_(facet->neighbors, neighbor), 0);
+    if (qh->DOintersections)
+      qh_printhyperplaneintersection(qh, fp, facet, neighbor, vertices, color);
+    else {
+      if (qh->DROPdim >= 0)
+        qh_fprintf(qh, fp, 9119, "OFF 3 1 1 # ridge between f%d f%d\n",
+                facet->id, neighbor->id);
+      else {
+        qh->printoutvar++;
+        qh_fprintf(qh, fp, 9120, "# ridge between f%d f%d\n", facet->id, neighbor->id);
+      }
+      FOREACHvertex_(vertices) {
+        for (k=0; k < qh->hull_dim; k++) {
+          if (k != qh->DROPdim)
+            qh_fprintf(qh, fp, 9121, "%8.4g ", vertex->point[k]);
+        }
+        qh_fprintf(qh, fp, 9122, "\n");
+      }
+      if (qh->DROPdim >= 0)
+        qh_fprintf(qh, fp, 9123, "3 0 1 2 %8.4g %8.4g %8.4g\n", color[0], color[1], color[2]);
+    }
+    qh_setfree(qh, &vertices);
+  }
+} /* printfacet4geom_simplicial */
+
+
+/*-<a                             href="qh-io_r.htm#TOC"
+  >-------------------------------</a><a name="printfacetNvertex_nonsimplicial">-</a>
+
+  qh_printfacetNvertex_nonsimplicial(qh, fp, facet, id, format )
+    print vertices for an N-d non-simplicial facet
+    triangulates each ridge to the id
+*/
+void qh_printfacetNvertex_nonsimplicial(qhT *qh, FILE *fp, facetT *facet, int id, qh_PRINT format) {
+  vertexT *vertex, **vertexp;
+  ridgeT *ridge, **ridgep;
+
+  if (facet->visible && qh->NEWfacets)
+    return;
+  FOREACHridge_(facet->ridges) {
+    if (format == qh_PRINTtriangles)
+      qh_fprintf(qh, fp, 9124, "%d ", qh->hull_dim);
+    qh_fprintf(qh, fp, 9125, "%d ", id);
+    if ((ridge->top == facet) ^ qh_ORIENTclock) {
+      FOREACHvertex_(ridge->vertices)
+        qh_fprintf(qh, fp, 9126, "%d ", qh_pointid(qh, vertex->point));
+    }else {
+      FOREACHvertexreverse12_(ridge->vertices)
+        qh_fprintf(qh, fp, 9127, "%d ", qh_pointid(qh, vertex->point));
+    }
+    qh_fprintf(qh, fp, 9128, "\n");
+  }
+} /* printfacetNvertex_nonsimplicial */
+
+
+/*-<a                             href="qh-io_r.htm#TOC"
+  >-------------------------------</a><a name="printfacetNvertex_simplicial">-</a>
+
+  qh_printfacetNvertex_simplicial(qh, fp, facet, format )
+    print vertices for an N-d simplicial facet
+    prints vertices for non-simplicial facets
+      2-d facets (orientation preserved by qh_mergefacet2d)
+      PRINToff ('o') for 4-d and higher
+*/
+void qh_printfacetNvertex_simplicial(qhT *qh, FILE *fp, facetT *facet, qh_PRINT format) {
+  vertexT *vertex, **vertexp;
+
+  if (format == qh_PRINToff || format == qh_PRINTtriangles)
+    qh_fprintf(qh, fp, 9129, "%d ", qh_setsize(qh, facet->vertices));
+  if ((facet->toporient ^ qh_ORIENTclock)
+  || (qh->hull_dim > 2 && !facet->simplicial)) {
+    FOREACHvertex_(facet->vertices)
+      qh_fprintf(qh, fp, 9130, "%d ", qh_pointid(qh, vertex->point));
+  }else {
+    FOREACHvertexreverse12_(facet->vertices)
+      qh_fprintf(qh, fp, 9131, "%d ", qh_pointid(qh, vertex->point));
+  }
+  qh_fprintf(qh, fp, 9132, "\n");
+} /* printfacetNvertex_simplicial */
+
+
+/*-<a                             href="qh-io_r.htm#TOC"
+  >-------------------------------</a><a name="printfacetheader">-</a>
+
+  qh_printfacetheader(qh, fp, facet )
+    prints header fields of a facet to fp
+
+  notes:
+    for 'f' output and debugging
+    Same as QhullFacet::printHeader()
+*/
+void qh_printfacetheader(qhT *qh, FILE *fp, facetT *facet) {
+  pointT *point, **pointp, *furthest;
+  facetT *neighbor, **neighborp;
+  realT dist;
+
+  if (facet == qh_MERGEridge) {
+    qh_fprintf(qh, fp, 9133, " MERGEridge\n");
+    return;
+  }else if (facet == qh_DUPLICATEridge) {
+    qh_fprintf(qh, fp, 9134, " DUPLICATEridge\n");
+    return;
+  }else if (!facet) {
+    qh_fprintf(qh, fp, 9135, " NULLfacet\n");
+    return;
+  }
+  qh->old_randomdist= qh->RANDOMdist;
+  qh->RANDOMdist= False;
+  qh_fprintf(qh, fp, 9136, "- f%d\n", facet->id);
+  qh_fprintf(qh, fp, 9137, "    - flags:");
+  if (facet->toporient)
+    qh_fprintf(qh, fp, 9138, " top");
+  else
+    qh_fprintf(qh, fp, 9139, " bottom");
+  if (facet->simplicial)
+    qh_fprintf(qh, fp, 9140, " simplicial");
+  if (facet->tricoplanar)
+    qh_fprintf(qh, fp, 9141, " tricoplanar");
+  if (facet->upperdelaunay)
+    qh_fprintf(qh, fp, 9142, " upperDelaunay");
+  if (facet->visible)
+    qh_fprintf(qh, fp, 9143, " visible");
+  if (facet->newfacet)
+    qh_fprintf(qh, fp, 9144, " new");
+  if (facet->tested)
+    qh_fprintf(qh, fp, 9145, " tested");
+  if (!facet->good)
+    qh_fprintf(qh, fp, 9146, " notG");
+  if (facet->seen)
+    qh_fprintf(qh, fp, 9147, " seen");
+  if (facet->coplanar)
+    qh_fprintf(qh, fp, 9148, " coplanar");
+  if (facet->mergehorizon)
+    qh_fprintf(qh, fp, 9149, " mergehorizon");
+  if (facet->keepcentrum)
+    qh_fprintf(qh, fp, 9150, " keepcentrum");
+  if (facet->dupridge)
+    qh_fprintf(qh, fp, 9151, " dupridge");
+  if (facet->mergeridge && !facet->mergeridge2)
+    qh_fprintf(qh, fp, 9152, " mergeridge1");
+  if (facet->mergeridge2)
+    qh_fprintf(qh, fp, 9153, " mergeridge2");
+  if (facet->newmerge)
+    qh_fprintf(qh, fp, 9154, " newmerge");
+  if (facet->flipped)
+    qh_fprintf(qh, fp, 9155, " flipped");
+  if (facet->notfurthest)
+    qh_fprintf(qh, fp, 9156, " notfurthest");
+  if (facet->degenerate)
+    qh_fprintf(qh, fp, 9157, " degenerate");
+  if (facet->redundant)
+    qh_fprintf(qh, fp, 9158, " redundant");
+  qh_fprintf(qh, fp, 9159, "\n");
+  if (facet->isarea)
+    qh_fprintf(qh, fp, 9160, "    - area: %2.2g\n", facet->f.area);
+  else if (qh->NEWfacets && facet->visible && facet->f.replace)
+    qh_fprintf(qh, fp, 9161, "    - replacement: f%d\n", facet->f.replace->id);
+  else if (facet->newfacet) {
+    if (facet->f.samecycle && facet->f.samecycle != facet)
+      qh_fprintf(qh, fp, 9162, "    - shares same visible/horizon as f%d\n", facet->f.samecycle->id);
+  }else if (facet->tricoplanar /* !isarea */) {
+    if (facet->f.triowner)
+      qh_fprintf(qh, fp, 9163, "    - owner of normal & centrum is facet f%d\n", facet->f.triowner->id);
+  }else if (facet->f.newcycle)
+    qh_fprintf(qh, fp, 9164, "    - was horizon to f%d\n", facet->f.newcycle->id);
+  if (facet->nummerge)
+    qh_fprintf(qh, fp, 9165, "    - merges: %d\n", facet->nummerge);
+  qh_printpointid(qh, fp, "    - normal: ", qh->hull_dim, facet->normal, qh_IDunknown);
+  qh_fprintf(qh, fp, 9166, "    - offset: %10.7g\n", facet->offset);
+  if (qh->CENTERtype == qh_ASvoronoi || facet->center)
+    qh_printcenter(qh, fp, qh_PRINTfacets, "    - center: ", facet);
+#if qh_MAXoutside
+  if (facet->maxoutside > qh->DISTround)
+    qh_fprintf(qh, fp, 9167, "    - maxoutside: %10.7g\n", facet->maxoutside);
+#endif
+  if (!SETempty_(facet->outsideset)) {
+    furthest= (pointT*)qh_setlast(facet->outsideset);
+    if (qh_setsize(qh, facet->outsideset) < 6) {
+      qh_fprintf(qh, fp, 9168, "    - outside set(furthest p%d):\n", qh_pointid(qh, furthest));
+      FOREACHpoint_(facet->outsideset)
+        qh_printpoint(qh, fp, "     ", point);
+    }else if (qh_setsize(qh, facet->outsideset) < 21) {
+      qh_printpoints(qh, fp, "    - outside set:", facet->outsideset);
+    }else {
+      qh_fprintf(qh, fp, 9169, "    - outside set:  %d points.", qh_setsize(qh, facet->outsideset));
+      qh_printpoint(qh, fp, "  Furthest", furthest);
+    }
+#if !qh_COMPUTEfurthest
+    qh_fprintf(qh, fp, 9170, "    - furthest distance= %2.2g\n", facet->furthestdist);
+#endif
+  }
+  if (!SETempty_(facet->coplanarset)) {
+    furthest= (pointT*)qh_setlast(facet->coplanarset);
+    if (qh_setsize(qh, facet->coplanarset) < 6) {
+      qh_fprintf(qh, fp, 9171, "    - coplanar set(furthest p%d):\n", qh_pointid(qh, furthest));
+      FOREACHpoint_(facet->coplanarset)
+        qh_printpoint(qh, fp, "     ", point);
+    }else if (qh_setsize(qh, facet->coplanarset) < 21) {
+      qh_printpoints(qh, fp, "    - coplanar set:", facet->coplanarset);
+    }else {
+      qh_fprintf(qh, fp, 9172, "    - coplanar set:  %d points.", qh_setsize(qh, facet->coplanarset));
+      qh_printpoint(qh, fp, "  Furthest", furthest);
+    }
+    zinc_(Zdistio);
+    qh_distplane(qh, furthest, facet, &dist);
+    qh_fprintf(qh, fp, 9173, "      furthest distance= %2.2g\n", dist);
+  }
+  qh_printvertices(qh, fp, "    - vertices:", facet->vertices);
+  qh_fprintf(qh, fp, 9174, "    - neighboring facets:");
+  FOREACHneighbor_(facet) {
+    if (neighbor == qh_MERGEridge)
+      qh_fprintf(qh, fp, 9175, " MERGE");
+    else if (neighbor == qh_DUPLICATEridge)
+      qh_fprintf(qh, fp, 9176, " DUP");
+    else
+      qh_fprintf(qh, fp, 9177, " f%d", neighbor->id);
+  }
+  qh_fprintf(qh, fp, 9178, "\n");
+  qh->RANDOMdist= qh->old_randomdist;
+} /* printfacetheader */
+
+
+/*-<a                             href="qh-io_r.htm#TOC"
+  >-------------------------------</a><a name="printfacetridges">-</a>
+
+  qh_printfacetridges(qh, fp, facet )
+    prints ridges of a facet to fp
+
+  notes:
+    ridges printed in neighbor order
+    assumes the ridges exist
+    for 'f' output
+    same as QhullFacet::printRidges
+*/
+void qh_printfacetridges(qhT *qh, FILE *fp, facetT *facet) {
+  facetT *neighbor, **neighborp;
+  ridgeT *ridge, **ridgep;
+  int numridges= 0;
+
+
+  if (facet->visible && qh->NEWfacets) {
+    qh_fprintf(qh, fp, 9179, "    - ridges(ids may be garbage):");
+    FOREACHridge_(facet->ridges)
+      qh_fprintf(qh, fp, 9180, " r%d", ridge->id);
+    qh_fprintf(qh, fp, 9181, "\n");
+  }else {
+    qh_fprintf(qh, fp, 9182, "    - ridges:\n");
+    FOREACHridge_(facet->ridges)
+      ridge->seen= False;
+    if (qh->hull_dim == 3) {
+      ridge= SETfirstt_(facet->ridges, ridgeT);
+      while (ridge && !ridge->seen) {
+        ridge->seen= True;
+        qh_printridge(qh, fp, ridge);
+        numridges++;
+        ridge= qh_nextridge3d(ridge, facet, NULL);
+        }
+    }else {
+      FOREACHneighbor_(facet) {
+        FOREACHridge_(facet->ridges) {
+          if (otherfacet_(ridge,facet) == neighbor) {
+            ridge->seen= True;
+            qh_printridge(qh, fp, ridge);
+            numridges++;
+          }
+        }
+      }
+    }
+    if (numridges != qh_setsize(qh, facet->ridges)) {
+      qh_fprintf(qh, fp, 9183, "     - all ridges:");
+      FOREACHridge_(facet->ridges)
+        qh_fprintf(qh, fp, 9184, " r%d", ridge->id);
+        qh_fprintf(qh, fp, 9185, "\n");
+    }
+    FOREACHridge_(facet->ridges) {
+      if (!ridge->seen)
+        qh_printridge(qh, fp, ridge);
+    }
+  }
+} /* printfacetridges */
+
+/*-<a                             href="qh-io_r.htm#TOC"
+  >-------------------------------</a><a name="printfacets">-</a>
+
+  qh_printfacets(qh, fp, format, facetlist, facets, printall )
+    prints facetlist and/or facet set in output format
+
+  notes:
+    also used for specialized formats ('FO' and summary)
+    turns off 'Rn' option since want actual numbers
+*/
+void qh_printfacets(qhT *qh, FILE *fp, qh_PRINT format, facetT *facetlist, setT *facets, boolT printall) {
+  int numfacets, numsimplicial, numridges, totneighbors, numcoplanars, numtricoplanars;
+  facetT *facet, **facetp;
+  setT *vertices;
+  coordT *center;
+  realT outerplane, innerplane;
+
+  qh->old_randomdist= qh->RANDOMdist;
+  qh->RANDOMdist= False;
+  if (qh->CDDoutput && (format == qh_PRINTcentrums || format == qh_PRINTpointintersect || format == qh_PRINToff))
+    qh_fprintf(qh, qh->ferr, 7056, "qhull warning: CDD format is not available for centrums, halfspace\nintersections, and OFF file format.\n");
+  if (format == qh_PRINTnone)
+    ; /* print nothing */
+  else if (format == qh_PRINTaverage) {
+    vertices= qh_facetvertices(qh, facetlist, facets, printall);
+    center= qh_getcenter(qh, vertices);
+    qh_fprintf(qh, fp, 9186, "%d 1\n", qh->hull_dim);
+    qh_printpointid(qh, fp, NULL, qh->hull_dim, center, qh_IDunknown);
+    qh_memfree(qh, center, qh->normal_size);
+    qh_settempfree(qh, &vertices);
+  }else if (format == qh_PRINTextremes) {
+    if (qh->DELAUNAY)
+      qh_printextremes_d(qh, fp, facetlist, facets, printall);
+    else if (qh->hull_dim == 2)
+      qh_printextremes_2d(qh, fp, facetlist, facets, printall);
+    else
+      qh_printextremes(qh, fp, facetlist, facets, printall);
+  }else if (format == qh_PRINToptions)
+    qh_fprintf(qh, fp, 9187, "Options selected for Qhull %s:\n%s\n", qh_version, qh->qhull_options);
+  else if (format == qh_PRINTpoints && !qh->VORONOI)
+    qh_printpoints_out(qh, fp, facetlist, facets, printall);
+  else if (format == qh_PRINTqhull)
+    qh_fprintf(qh, fp, 9188, "%s | %s\n", qh->rbox_command, qh->qhull_command);
+  else if (format == qh_PRINTsize) {
+    qh_fprintf(qh, fp, 9189, "0\n2 ");
+    qh_fprintf(qh, fp, 9190, qh_REAL_1, qh->totarea);
+    qh_fprintf(qh, fp, 9191, qh_REAL_1, qh->totvol);
+    qh_fprintf(qh, fp, 9192, "\n");
+  }else if (format == qh_PRINTsummary) {
+    qh_countfacets(qh, facetlist, facets, printall, &numfacets, &numsimplicial,
+      &totneighbors, &numridges, &numcoplanars, &numtricoplanars);
+    vertices= qh_facetvertices(qh, facetlist, facets, printall);
+    qh_fprintf(qh, fp, 9193, "10 %d %d %d %d %d %d %d %d %d %d\n2 ", qh->hull_dim,
+                qh->num_points + qh_setsize(qh, qh->other_points),
+                qh->num_vertices, qh->num_facets - qh->num_visible,
+                qh_setsize(qh, vertices), numfacets, numcoplanars,
+                numfacets - numsimplicial, zzval_(Zdelvertextot),
+                numtricoplanars);
+    qh_settempfree(qh, &vertices);
+    qh_outerinner(qh, NULL, &outerplane, &innerplane);
+    qh_fprintf(qh, fp, 9194, qh_REAL_2n, outerplane, innerplane);
+  }else if (format == qh_PRINTvneighbors)
+    qh_printvneighbors(qh, fp, facetlist, facets, printall);
+  else if (qh->VORONOI && format == qh_PRINToff)
+    qh_printvoronoi(qh, fp, format, facetlist, facets, printall);
+  else if (qh->VORONOI && format == qh_PRINTgeom) {
+    qh_printbegin(qh, fp, format, facetlist, facets, printall);
+    qh_printvoronoi(qh, fp, format, facetlist, facets, printall);
+    qh_printend(qh, fp, format, facetlist, facets, printall);
+  }else if (qh->VORONOI
+  && (format == qh_PRINTvertices || format == qh_PRINTinner || format == qh_PRINTouter))
+    qh_printvdiagram(qh, fp, format, facetlist, facets, printall);
+  else {
+    qh_printbegin(qh, fp, format, facetlist, facets, printall);
+    FORALLfacet_(facetlist)
+      qh_printafacet(qh, fp, format, facet, printall);
+    FOREACHfacet_(facets)
+      qh_printafacet(qh, fp, format, facet, printall);
+    qh_printend(qh, fp, format, facetlist, facets, printall);
+  }
+  qh->RANDOMdist= qh->old_randomdist;
+} /* printfacets */
+
+
+/*-<a                             href="qh-io_r.htm#TOC"
+  >-------------------------------</a><a name="printhyperplaneintersection">-</a>
+
+  qh_printhyperplaneintersection(qh, fp, facet1, facet2, vertices, color )
+    print Geomview OFF or 4OFF for the intersection of two hyperplanes in 3-d or 4-d
+*/
+void qh_printhyperplaneintersection(qhT *qh, FILE *fp, facetT *facet1, facetT *facet2,
+                   setT *vertices, realT color[3]) {
+  realT costheta, denominator, dist1, dist2, s, t, mindenom, p[4];
+  vertexT *vertex, **vertexp;
+  int i, k;
+  boolT nearzero1, nearzero2;
+
+  costheta= qh_getangle(qh, facet1->normal, facet2->normal);
+  denominator= 1 - costheta * costheta;
+  i= qh_setsize(qh, vertices);
+  if (qh->hull_dim == 3)
+    qh_fprintf(qh, fp, 9195, "VECT 1 %d 1 %d 1 ", i, i);
+  else if (qh->hull_dim == 4 && qh->DROPdim >= 0)
+    qh_fprintf(qh, fp, 9196, "OFF 3 1 1 ");
+  else
+    qh->printoutvar++;
+  qh_fprintf(qh, fp, 9197, "# intersect f%d f%d\n", facet1->id, facet2->id);
+  mindenom= 1 / (10.0 * qh->MAXabs_coord);
+  FOREACHvertex_(vertices) {
+    zadd_(Zdistio, 2);
+    qh_distplane(qh, vertex->point, facet1, &dist1);
+    qh_distplane(qh, vertex->point, facet2, &dist2);
+    s= qh_divzero(-dist1 + costheta * dist2, denominator,mindenom,&nearzero1);
+    t= qh_divzero(-dist2 + costheta * dist1, denominator,mindenom,&nearzero2);
+    if (nearzero1 || nearzero2)
+      s= t= 0.0;
+    for (k=qh->hull_dim; k--; )
+      p[k]= vertex->point[k] + facet1->normal[k] * s + facet2->normal[k] * t;
+    if (qh->PRINTdim <= 3) {
+      qh_projectdim3(qh, p, p);
+      qh_fprintf(qh, fp, 9198, "%8.4g %8.4g %8.4g # ", p[0], p[1], p[2]);
+    }else
+      qh_fprintf(qh, fp, 9199, "%8.4g %8.4g %8.4g %8.4g # ", p[0], p[1], p[2], p[3]);
+    if (nearzero1+nearzero2)
+      qh_fprintf(qh, fp, 9200, "p%d(coplanar facets)\n", qh_pointid(qh, vertex->point));
+    else
+      qh_fprintf(qh, fp, 9201, "projected p%d\n", qh_pointid(qh, vertex->point));
+  }
+  if (qh->hull_dim == 3)
+    qh_fprintf(qh, fp, 9202, "%8.4g %8.4g %8.4g 1.0\n", color[0], color[1], color[2]);
+  else if (qh->hull_dim == 4 && qh->DROPdim >= 0)
+    qh_fprintf(qh, fp, 9203, "3 0 1 2 %8.4g %8.4g %8.4g 1.0\n", color[0], color[1], color[2]);
+} /* printhyperplaneintersection */
+
+/*-<a                             href="qh-io_r.htm#TOC"
+  >-------------------------------</a><a name="printline3geom">-</a>
+
+  qh_printline3geom(qh, fp, pointA, pointB, color )
+    prints a line as a VECT
+    prints 0's for qh.DROPdim
+
+  notes:
+    if pointA == pointB,
+      it's a 1 point VECT
+*/
+void qh_printline3geom(qhT *qh, FILE *fp, pointT *pointA, pointT *pointB, realT color[3]) {
+  int k;
+  realT pA[4], pB[4];
+
+  qh_projectdim3(qh, pointA, pA);
+  qh_projectdim3(qh, pointB, pB);
+  if ((fabs(pA[0] - pB[0]) > 1e-3) ||
+      (fabs(pA[1] - pB[1]) > 1e-3) ||
+      (fabs(pA[2] - pB[2]) > 1e-3)) {
+    qh_fprintf(qh, fp, 9204, "VECT 1 2 1 2 1\n");
+    for (k=0; k < 3; k++)
+       qh_fprintf(qh, fp, 9205, "%8.4g ", pB[k]);
+    qh_fprintf(qh, fp, 9206, " # p%d\n", qh_pointid(qh, pointB));
+  }else
+    qh_fprintf(qh, fp, 9207, "VECT 1 1 1 1 1\n");
+  for (k=0; k < 3; k++)
+    qh_fprintf(qh, fp, 9208, "%8.4g ", pA[k]);
+  qh_fprintf(qh, fp, 9209, " # p%d\n", qh_pointid(qh, pointA));
+  qh_fprintf(qh, fp, 9210, "%8.4g %8.4g %8.4g 1\n", color[0], color[1], color[2]);
+}
+
+/*-<a                             href="qh-io_r.htm#TOC"
+  >-------------------------------</a><a name="printneighborhood">-</a>
+
+  qh_printneighborhood(qh, fp, format, facetA, facetB, printall )
+    print neighborhood of one or two facets
+
+  notes:
+    calls qh_findgood_all()
+    bumps qh.visit_id
+*/
+void qh_printneighborhood(qhT *qh, FILE *fp, qh_PRINT format, facetT *facetA, facetT *facetB, boolT printall) {
+  facetT *neighbor, **neighborp, *facet;
+  setT *facets;
+
+  if (format == qh_PRINTnone)
+    return;
+  qh_findgood_all(qh, qh->facet_list);
+  if (facetA == facetB)
+    facetB= NULL;
+  facets= qh_settemp(qh, 2*(qh_setsize(qh, facetA->neighbors)+1));
+  qh->visit_id++;
+  for (facet= facetA; facet; facet= ((facet == facetA) ? facetB : NULL)) {
+    if (facet->visitid != qh->visit_id) {
+      facet->visitid= qh->visit_id;
+      qh_setappend(qh, &facets, facet);
+    }
+    FOREACHneighbor_(facet) {
+      if (neighbor->visitid == qh->visit_id)
+        continue;
+      neighbor->visitid= qh->visit_id;
+      if (printall || !qh_skipfacet(qh, neighbor))
+        qh_setappend(qh, &facets, neighbor);
+    }
+  }
+  qh_printfacets(qh, fp, format, NULL, facets, printall);
+  qh_settempfree(qh, &facets);
+} /* printneighborhood */
+
+/*-<a                             href="qh-io_r.htm#TOC"
+  >-------------------------------</a><a name="printpoint">-</a>
+
+  qh_printpoint(qh, fp, string, point )
+  qh_printpointid(qh, fp, string, dim, point, id )
+    prints the coordinates of a point
+
+  returns:
+    if string is defined
+      prints 'string p%d'.  Skips p%d if id=qh_IDunknown(-1) or qh_IDnone(-3)
+
+  notes:
+    nop if point is NULL
+    Same as QhullPoint's printPoint
+*/
+void qh_printpoint(qhT *qh, FILE *fp, const char *string, pointT *point) {
+  int id= qh_pointid(qh, point);
+
+  qh_printpointid(qh, fp, string, qh->hull_dim, point, id);
+} /* printpoint */
+
+void qh_printpointid(qhT *qh, FILE *fp, const char *string, int dim, pointT *point, int id) {
+  int k;
+  realT r; /*bug fix*/
+
+  if (!point)
+    return;
+  if (string) {
+    qh_fprintf(qh, fp, 9211, "%s", string);
+    if (id != qh_IDunknown && id != qh_IDnone)
+      qh_fprintf(qh, fp, 9212, " p%d: ", id);
+  }
+  for (k=dim; k--; ) {
+    r= *point++;
+    if (string)
+      qh_fprintf(qh, fp, 9213, " %8.4g", r);
+    else
+      qh_fprintf(qh, fp, 9214, qh_REAL_1, r);
+  }
+  qh_fprintf(qh, fp, 9215, "\n");
+} /* printpointid */
+
+/*-<a                             href="qh-io_r.htm#TOC"
+  >-------------------------------</a><a name="printpoint3">-</a>
+
+  qh_printpoint3(qh, fp, point )
+    prints 2-d, 3-d, or 4-d point as Geomview 3-d coordinates
+*/
+void qh_printpoint3(qhT *qh, FILE *fp, pointT *point) {
+  int k;
+  realT p[4];
+
+  qh_projectdim3(qh, point, p);
+  for (k=0; k < 3; k++)
+    qh_fprintf(qh, fp, 9216, "%8.4g ", p[k]);
+  qh_fprintf(qh, fp, 9217, " # p%d\n", qh_pointid(qh, point));
+} /* printpoint3 */
+
+/*----------------------------------------
+-printpoints- print pointids for a set of points starting at index
+   see geom_r.c
+*/
+
+/*-<a                             href="qh-io_r.htm#TOC"
+  >-------------------------------</a><a name="printpoints_out">-</a>
+
+  qh_printpoints_out(qh, fp, facetlist, facets, printall )
+    prints vertices, coplanar/inside points, for facets by their point coordinates
+    allows qh.CDDoutput
+
+  notes:
+    same format as qhull input
+    if no coplanar/interior points,
+      same order as qh_printextremes
+*/
+void qh_printpoints_out(qhT *qh, FILE *fp, facetT *facetlist, setT *facets, boolT printall) {
+  int allpoints= qh->num_points + qh_setsize(qh, qh->other_points);
+  int numpoints=0, point_i, point_n;
+  setT *vertices, *points;
+  facetT *facet, **facetp;
+  pointT *point, **pointp;
+  vertexT *vertex, **vertexp;
+  int id;
+
+  points= qh_settemp(qh, allpoints);
+  qh_setzero(qh, points, 0, allpoints);
+  vertices= qh_facetvertices(qh, facetlist, facets, printall);
+  FOREACHvertex_(vertices) {
+    id= qh_pointid(qh, vertex->point);
+    if (id >= 0)
+      SETelem_(points, id)= vertex->point;
+  }
+  if (qh->KEEPinside || qh->KEEPcoplanar || qh->KEEPnearinside) {
+    FORALLfacet_(facetlist) {
+      if (!printall && qh_skipfacet(qh, facet))
+        continue;
+      FOREACHpoint_(facet->coplanarset) {
+        id= qh_pointid(qh, point);
+        if (id >= 0)
+          SETelem_(points, id)= point;
+      }
+    }
+    FOREACHfacet_(facets) {
+      if (!printall && qh_skipfacet(qh, facet))
+        continue;
+      FOREACHpoint_(facet->coplanarset) {
+        id= qh_pointid(qh, point);
+        if (id >= 0)
+          SETelem_(points, id)= point;
+      }
+    }
+  }
+  qh_settempfree(qh, &vertices);
+  FOREACHpoint_i_(qh, points) {
+    if (point)
+      numpoints++;
+  }
+  if (qh->CDDoutput)
+    qh_fprintf(qh, fp, 9218, "%s | %s\nbegin\n%d %d real\n", qh->rbox_command,
+             qh->qhull_command, numpoints, qh->hull_dim + 1);
+  else
+    qh_fprintf(qh, fp, 9219, "%d\n%d\n", qh->hull_dim, numpoints);
+  FOREACHpoint_i_(qh, points) {
+    if (point) {
+      if (qh->CDDoutput)
+        qh_fprintf(qh, fp, 9220, "1 ");
+      qh_printpoint(qh, fp, NULL, point);
+    }
+  }
+  if (qh->CDDoutput)
+    qh_fprintf(qh, fp, 9221, "end\n");
+  qh_settempfree(qh, &points);
+} /* printpoints_out */
+
+
+/*-<a                             href="qh-io_r.htm#TOC"
+  >-------------------------------</a><a name="printpointvect">-</a>
+
+  qh_printpointvect(qh, fp, point, normal, center, radius, color )
+    prints a 2-d, 3-d, or 4-d point as 3-d VECT's relative to normal or to center point
+*/
+void qh_printpointvect(qhT *qh, FILE *fp, pointT *point, coordT *normal, pointT *center, realT radius, realT color[3]) {
+  realT diff[4], pointA[4];
+  int k;
+
+  for (k=qh->hull_dim; k--; ) {
+    if (center)
+      diff[k]= point[k]-center[k];
+    else if (normal)
+      diff[k]= normal[k];
+    else
+      diff[k]= 0;
+  }
+  if (center)
+    qh_normalize2(qh, diff, qh->hull_dim, True, NULL, NULL);
+  for (k=qh->hull_dim; k--; )
+    pointA[k]= point[k]+diff[k] * radius;
+  qh_printline3geom(qh, fp, point, pointA, color);
+} /* printpointvect */
+
+/*-<a                             href="qh-io_r.htm#TOC"
+  >-------------------------------</a><a name="printpointvect2">-</a>
+
+  qh_printpointvect2(qh, fp, point, normal, center, radius )
+    prints a 2-d, 3-d, or 4-d point as 2 3-d VECT's for an imprecise point
+*/
+void qh_printpointvect2(qhT *qh, FILE *fp, pointT *point, coordT *normal, pointT *center, realT radius) {
+  realT red[3]={1, 0, 0}, yellow[3]={1, 1, 0};
+
+  qh_printpointvect(qh, fp, point, normal, center, radius, red);
+  qh_printpointvect(qh, fp, point, normal, center, -radius, yellow);
+} /* printpointvect2 */
+
+/*-<a                             href="qh-io_r.htm#TOC"
+  >-------------------------------</a><a name="printridge">-</a>
+
+  qh_printridge(qh, fp, ridge )
+    prints the information in a ridge
+
+  notes:
+    for qh_printfacetridges()
+    same as operator<< [QhullRidge.cpp]
+*/
+void qh_printridge(qhT *qh, FILE *fp, ridgeT *ridge) {
+
+  qh_fprintf(qh, fp, 9222, "     - r%d", ridge->id);
+  if (ridge->tested)
+    qh_fprintf(qh, fp, 9223, " tested");
+  if (ridge->nonconvex)
+    qh_fprintf(qh, fp, 9224, " nonconvex");
+  qh_fprintf(qh, fp, 9225, "\n");
+  qh_printvertices(qh, fp, "           vertices:", ridge->vertices);
+  if (ridge->top && ridge->bottom)
+    qh_fprintf(qh, fp, 9226, "           between f%d and f%d\n",
+            ridge->top->id, ridge->bottom->id);
+} /* printridge */
+
+/*-<a                             href="qh-io_r.htm#TOC"
+  >-------------------------------</a><a name="printspheres">-</a>
+
+  qh_printspheres(qh, fp, vertices, radius )
+    prints 3-d vertices as OFF spheres
+
+  notes:
+    inflated octahedron from Stuart Levy earth/mksphere2
+*/
+void qh_printspheres(qhT *qh, FILE *fp, setT *vertices, realT radius) {
+  vertexT *vertex, **vertexp;
+
+  qh->printoutnum++;
+  qh_fprintf(qh, fp, 9227, "{appearance {-edge -normal normscale 0} {\n\
+INST geom {define vsphere OFF\n\
+18 32 48\n\
+\n\
+0 0 1\n\
+1 0 0\n\
+0 1 0\n\
+-1 0 0\n\
+0 -1 0\n\
+0 0 -1\n\
+0.707107 0 0.707107\n\
+0 -0.707107 0.707107\n\
+0.707107 -0.707107 0\n\
+-0.707107 0 0.707107\n\
+-0.707107 -0.707107 0\n\
+0 0.707107 0.707107\n\
+-0.707107 0.707107 0\n\
+0.707107 0.707107 0\n\
+0.707107 0 -0.707107\n\
+0 0.707107 -0.707107\n\
+-0.707107 0 -0.707107\n\
+0 -0.707107 -0.707107\n\
+\n\
+3 0 6 11\n\
+3 0 7 6 \n\
+3 0 9 7 \n\
+3 0 11 9\n\
+3 1 6 8 \n\
+3 1 8 14\n\
+3 1 13 6\n\
+3 1 14 13\n\
+3 2 11 13\n\
+3 2 12 11\n\
+3 2 13 15\n\
+3 2 15 12\n\
+3 3 9 12\n\
+3 3 10 9\n\
+3 3 12 16\n\
+3 3 16 10\n\
+3 4 7 10\n\
+3 4 8 7\n\
+3 4 10 17\n\
+3 4 17 8\n\
+3 5 14 17\n\
+3 5 15 14\n\
+3 5 16 15\n\
+3 5 17 16\n\
+3 6 13 11\n\
+3 7 8 6\n\
+3 9 10 7\n\
+3 11 12 9\n\
+3 14 8 17\n\
+3 15 13 14\n\
+3 16 12 15\n\
+3 17 10 16\n} transforms { TLIST\n");
+  FOREACHvertex_(vertices) {
+    qh_fprintf(qh, fp, 9228, "%8.4g 0 0 0 # v%d\n 0 %8.4g 0 0\n0 0 %8.4g 0\n",
+      radius, vertex->id, radius, radius);
+    qh_printpoint3(qh, fp, vertex->point);
+    qh_fprintf(qh, fp, 9229, "1\n");
+  }
+  qh_fprintf(qh, fp, 9230, "}}}\n");
+} /* printspheres */
+
+
+/*----------------------------------------------
+-printsummary-
+                see libqhull_r.c
+*/
+
+/*-<a                             href="qh-io_r.htm#TOC"
+  >-------------------------------</a><a name="printvdiagram">-</a>
+
+  qh_printvdiagram(qh, fp, format, facetlist, facets, printall )
+    print voronoi diagram
+      # of pairs of input sites
+      #indices site1 site2 vertex1 ...
+
+    sites indexed by input point id
+      point 0 is the first input point
+    vertices indexed by 'o' and 'p' order
+      vertex 0 is the 'vertex-at-infinity'
+      vertex 1 is the first Voronoi vertex
+
+  see:
+    qh_printvoronoi()
+    qh_eachvoronoi_all()
+
+  notes:
+    if all facets are upperdelaunay,
+      prints upper hull (furthest-site Voronoi diagram)
+*/
+void qh_printvdiagram(qhT *qh, FILE *fp, qh_PRINT format, facetT *facetlist, setT *facets, boolT printall) {
+  setT *vertices;
+  int totcount, numcenters;
+  boolT isLower;
+  qh_RIDGE innerouter= qh_RIDGEall;
+  printvridgeT printvridge= NULL;
+
+  if (format == qh_PRINTvertices) {
+    innerouter= qh_RIDGEall;
+    printvridge= qh_printvridge;
+  }else if (format == qh_PRINTinner) {
+    innerouter= qh_RIDGEinner;
+    printvridge= qh_printvnorm;
+  }else if (format == qh_PRINTouter) {
+    innerouter= qh_RIDGEouter;
+    printvridge= qh_printvnorm;
+  }else {
+    qh_fprintf(qh, qh->ferr, 6219, "Qhull internal error (qh_printvdiagram): unknown print format %d.\n", format);
+    qh_errexit(qh, qh_ERRinput, NULL, NULL);
+  }
+  vertices= qh_markvoronoi(qh, facetlist, facets, printall, &isLower, &numcenters);
+  totcount= qh_printvdiagram2(qh, NULL, NULL, vertices, innerouter, False);
+  qh_fprintf(qh, fp, 9231, "%d\n", totcount);
+  totcount= qh_printvdiagram2(qh, fp, printvridge, vertices, innerouter, True /* inorder*/);
+  qh_settempfree(qh, &vertices);
+#if 0  /* for testing qh_eachvoronoi_all */
+  qh_fprintf(qh, fp, 9232, "\n");
+  totcount= qh_eachvoronoi_all(qh, fp, printvridge, qh->UPPERdelaunay, innerouter, True /* inorder*/);
+  qh_fprintf(qh, fp, 9233, "%d\n", totcount);
+#endif
+} /* printvdiagram */
+
+/*-<a                             href="qh-io_r.htm#TOC"
+  >-------------------------------</a><a name="printvdiagram2">-</a>
+
+  qh_printvdiagram2(qh, fp, printvridge, vertices, innerouter, inorder )
+    visit all pairs of input sites (vertices) for selected Voronoi vertices
+    vertices may include NULLs
+
+  innerouter:
+    qh_RIDGEall   print inner ridges(bounded) and outer ridges(unbounded)
+    qh_RIDGEinner print only inner ridges
+    qh_RIDGEouter print only outer ridges
+
+  inorder:
+    print 3-d Voronoi vertices in order
+
+  assumes:
+    qh_markvoronoi marked facet->visitid for Voronoi vertices
+    all facet->seen= False
+    all facet->seen2= True
+
+  returns:
+    total number of Voronoi ridges
+    if printvridge,
+      calls printvridge( fp, vertex, vertexA, centers) for each ridge
+      [see qh_eachvoronoi()]
+
+  see:
+    qh_eachvoronoi_all()
+*/
+int qh_printvdiagram2(qhT *qh, FILE *fp, printvridgeT printvridge, setT *vertices, qh_RIDGE innerouter, boolT inorder) {
+  int totcount= 0;
+  int vertex_i, vertex_n;
+  vertexT *vertex;
+
+  FORALLvertices
+    vertex->seen= False;
+  FOREACHvertex_i_(qh, vertices) {
+    if (vertex) {
+      if (qh->GOODvertex > 0 && qh_pointid(qh, vertex->point)+1 != qh->GOODvertex)
+        continue;
+      totcount += qh_eachvoronoi(qh, fp, printvridge, vertex, !qh_ALL, innerouter, inorder);
+    }
+  }
+  return totcount;
+} /* printvdiagram2 */
+
+/*-<a                             href="qh-io_r.htm#TOC"
+  >-------------------------------</a><a name="printvertex">-</a>
+
+  qh_printvertex(qh, fp, vertex )
+    prints the information in a vertex
+    Duplicated as operator<< [QhullVertex.cpp]
+*/
+void qh_printvertex(qhT *qh, FILE *fp, vertexT *vertex) {
+  pointT *point;
+  int k, count= 0;
+  facetT *neighbor, **neighborp;
+  realT r; /*bug fix*/
+
+  if (!vertex) {
+    qh_fprintf(qh, fp, 9234, "  NULLvertex\n");
+    return;
+  }
+  qh_fprintf(qh, fp, 9235, "- p%d(v%d):", qh_pointid(qh, vertex->point), vertex->id);
+  point= vertex->point;
+  if (point) {
+    for (k=qh->hull_dim; k--; ) {
+      r= *point++;
+      qh_fprintf(qh, fp, 9236, " %5.2g", r);
+    }
+  }
+  if (vertex->deleted)
+    qh_fprintf(qh, fp, 9237, " deleted");
+  if (vertex->delridge)
+    qh_fprintf(qh, fp, 9238, " ridgedeleted");
+  qh_fprintf(qh, fp, 9239, "\n");
+  if (vertex->neighbors) {
+    qh_fprintf(qh, fp, 9240, "  neighbors:");
+    FOREACHneighbor_(vertex) {
+      if (++count % 100 == 0)
+        qh_fprintf(qh, fp, 9241, "\n     ");
+      qh_fprintf(qh, fp, 9242, " f%d", neighbor->id);
+    }
+    qh_fprintf(qh, fp, 9243, "\n");
+  }
+} /* printvertex */
+
+
+/*-<a                             href="qh-io_r.htm#TOC"
+  >-------------------------------</a><a name="printvertexlist">-</a>
+
+  qh_printvertexlist(qh, fp, string, facetlist, facets, printall )
+    prints vertices used by a facetlist or facet set
+    tests qh_skipfacet() if !printall
+*/
+void qh_printvertexlist(qhT *qh, FILE *fp, const char* string, facetT *facetlist,
+                         setT *facets, boolT printall) {
+  vertexT *vertex, **vertexp;
+  setT *vertices;
+
+  vertices= qh_facetvertices(qh, facetlist, facets, printall);
+  qh_fprintf(qh, fp, 9244, "%s", string);
+  FOREACHvertex_(vertices)
+    qh_printvertex(qh, fp, vertex);
+  qh_settempfree(qh, &vertices);
+} /* printvertexlist */
+
+
+/*-<a                             href="qh-io_r.htm#TOC"
+  >-------------------------------</a><a name="printvertices">-</a>
+
+  qh_printvertices(qh, fp, string, vertices )
+    prints vertices in a set
+    duplicated as printVertexSet [QhullVertex.cpp]
+*/
+void qh_printvertices(qhT *qh, FILE *fp, const char* string, setT *vertices) {
+  vertexT *vertex, **vertexp;
+
+  qh_fprintf(qh, fp, 9245, "%s", string);
+  FOREACHvertex_(vertices)
+    qh_fprintf(qh, fp, 9246, " p%d(v%d)", qh_pointid(qh, vertex->point), vertex->id);
+  qh_fprintf(qh, fp, 9247, "\n");
+} /* printvertices */
+
+/*-<a                             href="qh-io_r.htm#TOC"
+  >-------------------------------</a><a name="printvneighbors">-</a>
+
+  qh_printvneighbors(qh, fp, facetlist, facets, printall )
+    print vertex neighbors of vertices in facetlist and facets ('FN')
+
+  notes:
+    qh_countfacets clears facet->visitid for non-printed facets
+
+  design:
+    collect facet count and related statistics
+    if necessary, build neighbor sets for each vertex
+    collect vertices in facetlist and facets
+    build a point array for point->vertex and point->coplanar facet
+    for each point
+      list vertex neighbors or coplanar facet
+*/
+void qh_printvneighbors(qhT *qh, FILE *fp, facetT* facetlist, setT *facets, boolT printall) {
+  int numfacets, numsimplicial, numridges, totneighbors, numneighbors, numcoplanars, numtricoplanars;
+  setT *vertices, *vertex_points, *coplanar_points;
+  int numpoints= qh->num_points + qh_setsize(qh, qh->other_points);
+  vertexT *vertex, **vertexp;
+  int vertex_i, vertex_n;
+  facetT *facet, **facetp, *neighbor, **neighborp;
+  pointT *point, **pointp;
+
+  qh_countfacets(qh, facetlist, facets, printall, &numfacets, &numsimplicial,
+      &totneighbors, &numridges, &numcoplanars, &numtricoplanars);  /* sets facet->visitid */
+  qh_fprintf(qh, fp, 9248, "%d\n", numpoints);
+  qh_vertexneighbors(qh);
+  vertices= qh_facetvertices(qh, facetlist, facets, printall);
+  vertex_points= qh_settemp(qh, numpoints);
+  coplanar_points= qh_settemp(qh, numpoints);
+  qh_setzero(qh, vertex_points, 0, numpoints);
+  qh_setzero(qh, coplanar_points, 0, numpoints);
+  FOREACHvertex_(vertices)
+    qh_point_add(qh, vertex_points, vertex->point, vertex);
+  FORALLfacet_(facetlist) {
+    FOREACHpoint_(facet->coplanarset)
+      qh_point_add(qh, coplanar_points, point, facet);
+  }
+  FOREACHfacet_(facets) {
+    FOREACHpoint_(facet->coplanarset)
+      qh_point_add(qh, coplanar_points, point, facet);
+  }
+  FOREACHvertex_i_(qh, vertex_points) {
+    if (vertex) {
+      numneighbors= qh_setsize(qh, vertex->neighbors);
+      qh_fprintf(qh, fp, 9249, "%d", numneighbors);
+      if (qh->hull_dim == 3)
+        qh_order_vertexneighbors(qh, vertex);
+      else if (qh->hull_dim >= 4)
+        qsort(SETaddr_(vertex->neighbors, facetT), (size_t)numneighbors,
+             sizeof(facetT *), qh_compare_facetvisit);
+      FOREACHneighbor_(vertex)
+        qh_fprintf(qh, fp, 9250, " %d",
+                 neighbor->visitid ? neighbor->visitid - 1 : 0 - neighbor->id);
+      qh_fprintf(qh, fp, 9251, "\n");
+    }else if ((facet= SETelemt_(coplanar_points, vertex_i, facetT)))
+      qh_fprintf(qh, fp, 9252, "1 %d\n",
+                  facet->visitid ? facet->visitid - 1 : 0 - facet->id);
+    else
+      qh_fprintf(qh, fp, 9253, "0\n");
+  }
+  qh_settempfree(qh, &coplanar_points);
+  qh_settempfree(qh, &vertex_points);
+  qh_settempfree(qh, &vertices);
+} /* printvneighbors */
+
+/*-<a                             href="qh-io_r.htm#TOC"
+  >-------------------------------</a><a name="printvoronoi">-</a>
+
+  qh_printvoronoi(qh, fp, format, facetlist, facets, printall )
+    print voronoi diagram in 'o' or 'G' format
+    for 'o' format
+      prints voronoi centers for each facet and for infinity
+      for each vertex, lists ids of printed facets or infinity
+      assumes facetlist and facets are disjoint
+    for 'G' format
+      prints an OFF object
+      adds a 0 coordinate to center
+      prints infinity but does not list in vertices
+
+  see:
+    qh_printvdiagram()
+
+  notes:
+    if 'o',
+      prints a line for each point except "at-infinity"
+    if all facets are upperdelaunay,
+      reverses lower and upper hull
+*/
+void qh_printvoronoi(qhT *qh, FILE *fp, qh_PRINT format, facetT *facetlist, setT *facets, boolT printall) {
+  int k, numcenters, numvertices= 0, numneighbors, numinf, vid=1, vertex_i, vertex_n;
+  facetT *facet, **facetp, *neighbor, **neighborp;
+  setT *vertices;
+  vertexT *vertex;
+  boolT isLower;
+  unsigned int numfacets= (unsigned int) qh->num_facets;
+
+  vertices= qh_markvoronoi(qh, facetlist, facets, printall, &isLower, &numcenters);
+  FOREACHvertex_i_(qh, vertices) {
+    if (vertex) {
+      numvertices++;
+      numneighbors = numinf = 0;
+      FOREACHneighbor_(vertex) {
+        if (neighbor->visitid == 0)
+          numinf= 1;
+        else if (neighbor->visitid < numfacets)
+          numneighbors++;
+      }
+      if (numinf && !numneighbors) {
+        SETelem_(vertices, vertex_i)= NULL;
+        numvertices--;
+      }
+    }
+  }
+  if (format == qh_PRINTgeom)
+    qh_fprintf(qh, fp, 9254, "{appearance {+edge -face} OFF %d %d 1 # Voronoi centers and cells\n",
+                numcenters, numvertices);
+  else
+    qh_fprintf(qh, fp, 9255, "%d\n%d %d 1\n", qh->hull_dim-1, numcenters, qh_setsize(qh, vertices));
+  if (format == qh_PRINTgeom) {
+    for (k=qh->hull_dim-1; k--; )
+      qh_fprintf(qh, fp, 9256, qh_REAL_1, 0.0);
+    qh_fprintf(qh, fp, 9257, " 0 # infinity not used\n");
+  }else {
+    for (k=qh->hull_dim-1; k--; )
+      qh_fprintf(qh, fp, 9258, qh_REAL_1, qh_INFINITE);
+    qh_fprintf(qh, fp, 9259, "\n");
+  }
+  FORALLfacet_(facetlist) {
+    if (facet->visitid && facet->visitid < numfacets) {
+      if (format == qh_PRINTgeom)
+        qh_fprintf(qh, fp, 9260, "# %d f%d\n", vid++, facet->id);
+      qh_printcenter(qh, fp, format, NULL, facet);
+    }
+  }
+  FOREACHfacet_(facets) {
+    if (facet->visitid && facet->visitid < numfacets) {
+      if (format == qh_PRINTgeom)
+        qh_fprintf(qh, fp, 9261, "# %d f%d\n", vid++, facet->id);
+      qh_printcenter(qh, fp, format, NULL, facet);
+    }
+  }
+  FOREACHvertex_i_(qh, vertices) {
+    numneighbors= 0;
+    numinf=0;
+    if (vertex) {
+      if (qh->hull_dim == 3)
+        qh_order_vertexneighbors(qh, vertex);
+      else if (qh->hull_dim >= 4)
+        qsort(SETaddr_(vertex->neighbors, facetT),
+             (size_t)qh_setsize(qh, vertex->neighbors),
+             sizeof(facetT *), qh_compare_facetvisit);
+      FOREACHneighbor_(vertex) {
+        if (neighbor->visitid == 0)
+          numinf= 1;
+        else if (neighbor->visitid < numfacets)
+          numneighbors++;
+      }
+    }
+    if (format == qh_PRINTgeom) {
+      if (vertex) {
+        qh_fprintf(qh, fp, 9262, "%d", numneighbors);
+        FOREACHneighbor_(vertex) {
+          if (neighbor->visitid && neighbor->visitid < numfacets)
+            qh_fprintf(qh, fp, 9263, " %d", neighbor->visitid);
+        }
+        qh_fprintf(qh, fp, 9264, " # p%d(v%d)\n", vertex_i, vertex->id);
+      }else
+        qh_fprintf(qh, fp, 9265, " # p%d is coplanar or isolated\n", vertex_i);
+    }else {
+      if (numinf)
+        numneighbors++;
+      qh_fprintf(qh, fp, 9266, "%d", numneighbors);
+      if (vertex) {
+        FOREACHneighbor_(vertex) {
+          if (neighbor->visitid == 0) {
+            if (numinf) {
+              numinf= 0;
+              qh_fprintf(qh, fp, 9267, " %d", neighbor->visitid);
+            }
+          }else if (neighbor->visitid < numfacets)
+            qh_fprintf(qh, fp, 9268, " %d", neighbor->visitid);
+        }
+      }
+      qh_fprintf(qh, fp, 9269, "\n");
+    }
+  }
+  if (format == qh_PRINTgeom)
+    qh_fprintf(qh, fp, 9270, "}\n");
+  qh_settempfree(qh, &vertices);
+} /* printvoronoi */
+
+/*-<a                             href="qh-io_r.htm#TOC"
+  >-------------------------------</a><a name="printvnorm">-</a>
+
+  qh_printvnorm(qh, fp, vertex, vertexA, centers, unbounded )
+    print one separating plane of the Voronoi diagram for a pair of input sites
+    unbounded==True if centers includes vertex-at-infinity
+
+  assumes:
+    qh_ASvoronoi and qh_vertexneighbors() already set
+
+  note:
+    parameter unbounded is UNUSED by this callback
+
+  see:
+    qh_printvdiagram()
+    qh_eachvoronoi()
+*/
+void qh_printvnorm(qhT *qh, FILE *fp, vertexT *vertex, vertexT *vertexA, setT *centers, boolT unbounded) {
+  pointT *normal;
+  realT offset;
+  int k;
+  QHULL_UNUSED(unbounded);
+
+  normal= qh_detvnorm(qh, vertex, vertexA, centers, &offset);
+  qh_fprintf(qh, fp, 9271, "%d %d %d ",
+      2+qh->hull_dim, qh_pointid(qh, vertex->point), qh_pointid(qh, vertexA->point));
+  for (k=0; k< qh->hull_dim-1; k++)
+    qh_fprintf(qh, fp, 9272, qh_REAL_1, normal[k]);
+  qh_fprintf(qh, fp, 9273, qh_REAL_1, offset);
+  qh_fprintf(qh, fp, 9274, "\n");
+} /* printvnorm */
+
+/*-<a                             href="qh-io_r.htm#TOC"
+  >-------------------------------</a><a name="printvridge">-</a>
+
+  qh_printvridge(qh, fp, vertex, vertexA, centers, unbounded )
+    print one ridge of the Voronoi diagram for a pair of input sites
+    unbounded==True if centers includes vertex-at-infinity
+
+  see:
+    qh_printvdiagram()
+
+  notes:
+    the user may use a different function
+    parameter unbounded is UNUSED
+*/
+void qh_printvridge(qhT *qh, FILE *fp, vertexT *vertex, vertexT *vertexA, setT *centers, boolT unbounded) {
+  facetT *facet, **facetp;
+  QHULL_UNUSED(unbounded);
+
+  qh_fprintf(qh, fp, 9275, "%d %d %d", qh_setsize(qh, centers)+2,
+       qh_pointid(qh, vertex->point), qh_pointid(qh, vertexA->point));
+  FOREACHfacet_(centers)
+    qh_fprintf(qh, fp, 9276, " %d", facet->visitid);
+  qh_fprintf(qh, fp, 9277, "\n");
+} /* printvridge */
+
+/*-<a                             href="qh-io_r.htm#TOC"
+  >-------------------------------</a><a name="projectdim3">-</a>
+
+  qh_projectdim3(qh, source, destination )
+    project 2-d 3-d or 4-d point to a 3-d point
+    uses qh.DROPdim and qh.hull_dim
+    source and destination may be the same
+
+  notes:
+    allocate 4 elements to destination just in case
+*/
+void qh_projectdim3(qhT *qh, pointT *source, pointT *destination) {
+  int i,k;
+
+  for (k=0, i=0; k < qh->hull_dim; k++) {
+    if (qh->hull_dim == 4) {
+      if (k != qh->DROPdim)
+        destination[i++]= source[k];
+    }else if (k == qh->DROPdim)
+      destination[i++]= 0;
+    else
+      destination[i++]= source[k];
+  }
+  while (i < 3)
+    destination[i++]= 0.0;
+} /* projectdim3 */
+
+/*-<a                             href="qh-io_r.htm#TOC"
+  >-------------------------------</a><a name="readfeasible">-</a>
+
+  qh_readfeasible(qh, dim, curline )
+    read feasible point from current line and qh.fin
+
+  returns:
+    number of lines read from qh.fin
+    sets qh.feasible_point with malloc'd coordinates
+
+  notes:
+    checks for qh.HALFspace
+    assumes dim > 1
+
+  see:
+    qh_setfeasible
+*/
+int qh_readfeasible(qhT *qh, int dim, const char *curline) {
+  boolT isfirst= True;
+  int linecount= 0, tokcount= 0;
+  const char *s;
+  char *t, firstline[qh_MAXfirst+1];
+  coordT *coords, value;
+
+  if (!qh->HALFspace) {
+    qh_fprintf(qh, qh->ferr, 6070, "qhull input error: feasible point(dim 1 coords) is only valid for halfspace intersection\n");
+    qh_errexit(qh, qh_ERRinput, NULL, NULL);
+  }
+  if (qh->feasible_string)
+    qh_fprintf(qh, qh->ferr, 7057, "qhull input warning: feasible point(dim 1 coords) overrides 'Hn,n,n' feasible point for halfspace intersection\n");
+  if (!(qh->feasible_point= (coordT*)qh_malloc(dim* sizeof(coordT)))) {
+    qh_fprintf(qh, qh->ferr, 6071, "qhull error: insufficient memory for feasible point\n");
+    qh_errexit(qh, qh_ERRmem, NULL, NULL);
+  }
+  coords= qh->feasible_point;
+  while ((s= (isfirst ?  curline : fgets(firstline, qh_MAXfirst, qh->fin)))) {
+    if (isfirst)
+      isfirst= False;
+    else
+      linecount++;
+    while (*s) {
+      while (isspace(*s))
+        s++;
+      value= qh_strtod(s, &t);
+      if (s == t)
+        break;
+      s= t;
+      *(coords++)= value;
+      if (++tokcount == dim) {
+        while (isspace(*s))
+          s++;
+        qh_strtod(s, &t);
+        if (s != t) {
+          qh_fprintf(qh, qh->ferr, 6072, "qhull input error: coordinates for feasible point do not finish out the line: %s\n",
+               s);
+          qh_errexit(qh, qh_ERRinput, NULL, NULL);
+        }
+        return linecount;
+      }
+    }
+  }
+  qh_fprintf(qh, qh->ferr, 6073, "qhull input error: only %d coordinates.  Could not read %d-d feasible point.\n",
+           tokcount, dim);
+  qh_errexit(qh, qh_ERRinput, NULL, NULL);
+  return 0;
+} /* readfeasible */
+
+/*-<a                             href="qh-io_r.htm#TOC"
+  >-------------------------------</a><a name="readpoints">-</a>
+
+  qh_readpoints(qh, numpoints, dimension, ismalloc )
+    read points from qh.fin into qh.first_point, qh.num_points
+    qh.fin is lines of coordinates, one per vertex, first line number of points
+    if 'rbox D4',
+      gives message
+    if qh.ATinfinity,
+      adds point-at-infinity for Delaunay triangulations
+
+  returns:
+    number of points, array of point coordinates, dimension, ismalloc True
+    if qh.DELAUNAY & !qh.PROJECTinput, projects points to paraboloid
+        and clears qh.PROJECTdelaunay
+    if qh.HALFspace, reads optional feasible point, reads halfspaces,
+        converts to dual.
+
+  for feasible point in "cdd format" in 3-d:
+    3 1
+    coordinates
+    comments
+    begin
+    n 4 real/integer
+    ...
+    end
+
+  notes:
+    dimension will change in qh_initqhull_globals if qh.PROJECTinput
+    uses malloc() since qh_mem not initialized
+    FIXUP QH11012: qh_readpoints needs rewriting, too long
+*/
+coordT *qh_readpoints(qhT *qh, int *numpoints, int *dimension, boolT *ismalloc) {
+  coordT *points, *coords, *infinity= NULL;
+  realT paraboloid, maxboloid= -REALmax, value;
+  realT *coordp= NULL, *offsetp= NULL, *normalp= NULL;
+  char *s= 0, *t, firstline[qh_MAXfirst+1];
+  int diminput=0, numinput=0, dimfeasible= 0, newnum, k, tempi;
+  int firsttext=0, firstshort=0, firstlong=0, firstpoint=0;
+  int tokcount= 0, linecount=0, maxcount, coordcount=0;
+  boolT islong, isfirst= True, wasbegin= False;
+  boolT isdelaunay= qh->DELAUNAY && !qh->PROJECTinput;
+
+  if (qh->CDDinput) {
+    while ((s= fgets(firstline, qh_MAXfirst, qh->fin))) {
+      linecount++;
+      if (qh->HALFspace && linecount == 1 && isdigit(*s)) {
+        dimfeasible= qh_strtol(s, &s);
+        while (isspace(*s))
+          s++;
+        if (qh_strtol(s, &s) == 1)
+          linecount += qh_readfeasible(qh, dimfeasible, s);
+        else
+          dimfeasible= 0;
+      }else if (!memcmp(firstline, "begin", (size_t)5) || !memcmp(firstline, "BEGIN", (size_t)5))
+        break;
+      else if (!*qh->rbox_command)
+        strncat(qh->rbox_command, s, sizeof(qh->rbox_command)-1);
+    }
+    if (!s) {
+      qh_fprintf(qh, qh->ferr, 6074, "qhull input error: missing \"begin\" for cdd-formated input\n");
+      qh_errexit(qh, qh_ERRinput, NULL, NULL);
+    }
+  }
+  while (!numinput && (s= fgets(firstline, qh_MAXfirst, qh->fin))) {
+    linecount++;
+    if (!memcmp(s, "begin", (size_t)5) || !memcmp(s, "BEGIN", (size_t)5))
+      wasbegin= True;
+    while (*s) {
+      while (isspace(*s))
+        s++;
+      if (!*s)
+        break;
+      if (!isdigit(*s)) {
+        if (!*qh->rbox_command) {
+          strncat(qh->rbox_command, s, sizeof(qh->rbox_command)-1);
+          firsttext= linecount;
+        }
+        break;
+      }
+      if (!diminput)
+        diminput= qh_strtol(s, &s);
+      else {
+        numinput= qh_strtol(s, &s);
+        if (numinput == 1 && diminput >= 2 && qh->HALFspace && !qh->CDDinput) {
+          linecount += qh_readfeasible(qh, diminput, s); /* checks if ok */
+          dimfeasible= diminput;
+          diminput= numinput= 0;
+        }else
+          break;
+      }
+    }
+  }
+  if (!s) {
+    qh_fprintf(qh, qh->ferr, 6075, "qhull input error: short input file.  Did not find dimension and number of points\n");
+    qh_errexit(qh, qh_ERRinput, NULL, NULL);
+  }
+  if (diminput > numinput) {
+    tempi= diminput;    /* exchange dim and n, e.g., for cdd input format */
+    diminput= numinput;
+    numinput= tempi;
+  }
+  if (diminput < 2) {
+    qh_fprintf(qh, qh->ferr, 6220,"qhull input error: dimension %d(first number) should be at least 2\n",
+            diminput);
+    qh_errexit(qh, qh_ERRinput, NULL, NULL);
+  }
+  if (isdelaunay) {
+    qh->PROJECTdelaunay= False;
+    if (qh->CDDinput)
+      *dimension= diminput;
+    else
+      *dimension= diminput+1;
+    *numpoints= numinput;
+    if (qh->ATinfinity)
+      (*numpoints)++;
+  }else if (qh->HALFspace) {
+    *dimension= diminput - 1;
+    *numpoints= numinput;
+    if (diminput < 3) {
+      qh_fprintf(qh, qh->ferr, 6221,"qhull input error: dimension %d(first number, includes offset) should be at least 3 for halfspaces\n",
+            diminput);
+      qh_errexit(qh, qh_ERRinput, NULL, NULL);
+    }
+    if (dimfeasible) {
+      if (dimfeasible != *dimension) {
+        qh_fprintf(qh, qh->ferr, 6222,"qhull input error: dimension %d of feasible point is not one less than dimension %d for halfspaces\n",
+          dimfeasible, diminput);
+        qh_errexit(qh, qh_ERRinput, NULL, NULL);
+      }
+    }else
+      qh_setfeasible(qh, *dimension);
+  }else {
+    if (qh->CDDinput)
+      *dimension= diminput-1;
+    else
+      *dimension= diminput;
+    *numpoints= numinput;
+  }
+  qh->normal_size= *dimension * sizeof(coordT); /* for tracing with qh_printpoint */
+  if (qh->HALFspace) {
+    qh->half_space= coordp= (coordT*)qh_malloc(qh->normal_size + sizeof(coordT));
+    if (qh->CDDinput) {
+      offsetp= qh->half_space;
+      normalp= offsetp + 1;
+    }else {
+      normalp= qh->half_space;
+      offsetp= normalp + *dimension;
+    }
+  }
+  qh->maxline= diminput * (qh_REALdigits + 5);
+  maximize_(qh->maxline, 500);
+  qh->line= (char*)qh_malloc((qh->maxline+1) * sizeof(char));
+  *ismalloc= True;  /* use malloc since memory not setup */
+  coords= points= qh->temp_malloc=  /* numinput and diminput >=2 by QH6220 */
+        (coordT*)qh_malloc((*numpoints)*(*dimension)*sizeof(coordT));
+  if (!coords || !qh->line || (qh->HALFspace && !qh->half_space)) {
+    qh_fprintf(qh, qh->ferr, 6076, "qhull error: insufficient memory to read %d points\n",
+            numinput);
+    qh_errexit(qh, qh_ERRmem, NULL, NULL);
+  }
+  if (isdelaunay && qh->ATinfinity) {
+    infinity= points + numinput * (*dimension);
+    for (k= (*dimension) - 1; k--; )
+      infinity[k]= 0.0;
+  }
+  maxcount= numinput * diminput;
+  paraboloid= 0.0;
+  while ((s= (isfirst ?  s : fgets(qh->line, qh->maxline, qh->fin)))) {
+    if (!isfirst) {
+      linecount++;
+      if (*s == 'e' || *s == 'E') {
+        if (!memcmp(s, "end", (size_t)3) || !memcmp(s, "END", (size_t)3)) {
+          if (qh->CDDinput )
+            break;
+          else if (wasbegin)
+            qh_fprintf(qh, qh->ferr, 7058, "qhull input warning: the input appears to be in cdd format.  If so, use 'Fd'\n");
+        }
+      }
+    }
+    islong= False;
+    while (*s) {
+      while (isspace(*s))
+        s++;
+      value= qh_strtod(s, &t);
+      if (s == t) {
+        if (!*qh->rbox_command)
+         strncat(qh->rbox_command, s, sizeof(qh->rbox_command)-1);
+        if (*s && !firsttext)
+          firsttext= linecount;
+        if (!islong && !firstshort && coordcount)
+          firstshort= linecount;
+        break;
+      }
+      if (!firstpoint)
+        firstpoint= linecount;
+      s= t;
+      if (++tokcount > maxcount)
+        continue;
+      if (qh->HALFspace) {
+        if (qh->CDDinput)
+          *(coordp++)= -value; /* both coefficients and offset */
+        else
+          *(coordp++)= value;
+      }else {
+        *(coords++)= value;
+        if (qh->CDDinput && !coordcount) {
+          if (value != 1.0) {
+            qh_fprintf(qh, qh->ferr, 6077, "qhull input error: for cdd format, point at line %d does not start with '1'\n",
+                   linecount);
+            qh_errexit(qh, qh_ERRinput, NULL, NULL);
+          }
+          coords--;
+        }else if (isdelaunay) {
+          paraboloid += value * value;
+          if (qh->ATinfinity) {
+            if (qh->CDDinput)
+              infinity[coordcount-1] += value;
+            else
+              infinity[coordcount] += value;
+          }
+        }
+      }
+      if (++coordcount == diminput) {
+        coordcount= 0;
+        if (isdelaunay) {
+          *(coords++)= paraboloid;
+          maximize_(maxboloid, paraboloid);
+          paraboloid= 0.0;
+        }else if (qh->HALFspace) {
+          if (!qh_sethalfspace(qh, *dimension, coords, &coords, normalp, offsetp, qh->feasible_point)) {
+            qh_fprintf(qh, qh->ferr, 8048, "The halfspace was on line %d\n", linecount);
+            if (wasbegin)
+              qh_fprintf(qh, qh->ferr, 8049, "The input appears to be in cdd format.  If so, you should use option 'Fd'\n");
+            qh_errexit(qh, qh_ERRinput, NULL, NULL);
+          }
+          coordp= qh->half_space;
+        }
+        while (isspace(*s))
+          s++;
+        if (*s) {
+          islong= True;
+          if (!firstlong)
+            firstlong= linecount;
+        }
+      }
+    }
+    if (!islong && !firstshort && coordcount)
+      firstshort= linecount;
+    if (!isfirst && s - qh->line >= qh->maxline) {
+      qh_fprintf(qh, qh->ferr, 6078, "qhull input error: line %d contained more than %d characters\n",
+              linecount, (int) (s - qh->line));   /* WARN64 */
+      qh_errexit(qh, qh_ERRinput, NULL, NULL);
+    }
+    isfirst= False;
+  }
+  if (tokcount != maxcount) {
+    newnum= fmin_(numinput, tokcount/diminput);
+    qh_fprintf(qh, qh->ferr, 7073,"\
+qhull warning: instead of %d %d-dimensional points, input contains\n\
+%d points and %d extra coordinates.  Line %d is the first\npoint",
+       numinput, diminput, tokcount/diminput, tokcount % diminput, firstpoint);
+    if (firsttext)
+      qh_fprintf(qh, qh->ferr, 8051, ", line %d is the first comment", firsttext);
+    if (firstshort)
+      qh_fprintf(qh, qh->ferr, 8052, ", line %d is the first short\nline", firstshort);
+    if (firstlong)
+      qh_fprintf(qh, qh->ferr, 8053, ", line %d is the first long line", firstlong);
+    qh_fprintf(qh, qh->ferr, 8054, ".  Continue with %d points.\n", newnum);
+    numinput= newnum;
+    if (isdelaunay && qh->ATinfinity) {
+      for (k= tokcount % diminput; k--; )
+        infinity[k] -= *(--coords);
+      *numpoints= newnum+1;
+    }else {
+      coords -= tokcount % diminput;
+      *numpoints= newnum;
+    }
+  }
+  if (isdelaunay && qh->ATinfinity) {
+    for (k= (*dimension) -1; k--; )
+      infinity[k] /= numinput;
+    if (coords == infinity)
+      coords += (*dimension) -1;
+    else {
+      for (k=0; k < (*dimension) -1; k++)
+        *(coords++)= infinity[k];
+    }
+    *(coords++)= maxboloid * 1.1;
+  }
+  if (qh->rbox_command[0]) {
+    qh->rbox_command[strlen(qh->rbox_command)-1]= '\0';
+    if (!strcmp(qh->rbox_command, "./rbox D4"))
+      qh_fprintf(qh, qh->ferr, 8055, "\n\
+This is the qhull test case.  If any errors or core dumps occur,\n\
+recompile qhull with 'make new'.  If errors still occur, there is\n\
+an incompatibility.  You should try a different compiler.  You can also\n\
+change the choices in user.h.  If you discover the source of the problem,\n\
+please send mail to qhull_bug@qhull.org.\n\
+\n\
+Type 'qhull' for a short list of options.\n");
+  }
+  qh_free(qh->line);
+  qh->line= NULL;
+  if (qh->half_space) {
+    qh_free(qh->half_space);
+    qh->half_space= NULL;
+  }
+  qh->temp_malloc= NULL;
+  trace1((qh, qh->ferr, 1008,"qh_readpoints: read in %d %d-dimensional points\n",
+          numinput, diminput));
+  return(points);
+} /* readpoints */
+
+
+/*-<a                             href="qh-io_r.htm#TOC"
+  >-------------------------------</a><a name="setfeasible">-</a>
+
+  qh_setfeasible(qh, dim )
+    set qh.feasible_point from qh.feasible_string in "n,n,n" or "n n n" format
+
+  notes:
+    "n,n,n" already checked by qh_initflags()
+    see qh_readfeasible()
+    called only once from qh_new_qhull, otherwise leaks memory
+*/
+void qh_setfeasible(qhT *qh, int dim) {
+  int tokcount= 0;
+  char *s;
+  coordT *coords, value;
+
+  if (!(s= qh->feasible_string)) {
+    qh_fprintf(qh, qh->ferr, 6223, "\
+qhull input error: halfspace intersection needs a feasible point.\n\
+Either prepend the input with 1 point or use 'Hn,n,n'.  See manual.\n");
+    qh_errexit(qh, qh_ERRinput, NULL, NULL);
+  }
+  if (!(qh->feasible_point= (pointT*)qh_malloc(dim * sizeof(coordT)))) {
+    qh_fprintf(qh, qh->ferr, 6079, "qhull error: insufficient memory for 'Hn,n,n'\n");
+    qh_errexit(qh, qh_ERRmem, NULL, NULL);
+  }
+  coords= qh->feasible_point;
+  while (*s) {
+    value= qh_strtod(s, &s);
+    if (++tokcount > dim) {
+      qh_fprintf(qh, qh->ferr, 7059, "qhull input warning: more coordinates for 'H%s' than dimension %d\n",
+          qh->feasible_string, dim);
+      break;
+    }
+    *(coords++)= value;
+    if (*s)
+      s++;
+  }
+  while (++tokcount <= dim)
+    *(coords++)= 0.0;
+} /* setfeasible */
+
+/*-<a                             href="qh-io_r.htm#TOC"
+  >-------------------------------</a><a name="skipfacet">-</a>
+
+  qh_skipfacet(qh, facet )
+    returns 'True' if this facet is not to be printed
+
+  notes:
+    based on the user provided slice thresholds and 'good' specifications
+*/
+boolT qh_skipfacet(qhT *qh, facetT *facet) {
+  facetT *neighbor, **neighborp;
+
+  if (qh->PRINTneighbors) {
+    if (facet->good)
+      return !qh->PRINTgood;
+    FOREACHneighbor_(facet) {
+      if (neighbor->good)
+        return False;
+    }
+    return True;
+  }else if (qh->PRINTgood)
+    return !facet->good;
+  else if (!facet->normal)
+    return True;
+  return(!qh_inthresholds(qh, facet->normal, NULL));
+} /* skipfacet */
+
+/*-<a                             href="qh-io_r.htm#TOC"
+  >-------------------------------</a><a name="skipfilename">-</a>
+
+  qh_skipfilename(qh, string )
+    returns pointer to character after filename
+
+  notes:
+    skips leading spaces
+    ends with spacing or eol
+    if starts with ' or " ends with the same, skipping \' or \"
+    For qhull, qh_argv_to_command() only uses double quotes
+*/
+char *qh_skipfilename(qhT *qh, char *filename) {
+  char *s= filename;  /* non-const due to return */
+  char c;
+
+  while (*s && isspace(*s))
+    s++;
+  c= *s++;
+  if (c == '\0') {
+    qh_fprintf(qh, qh->ferr, 6204, "qhull input error: filename expected, none found.\n");
+    qh_errexit(qh, qh_ERRinput, NULL, NULL);
+  }
+  if (c == '\'' || c == '"') {
+    while (*s !=c || s[-1] == '\\') {
+      if (!*s) {
+        qh_fprintf(qh, qh->ferr, 6203, "qhull input error: missing quote after filename -- %s\n", filename);
+        qh_errexit(qh, qh_ERRinput, NULL, NULL);
+      }
+      s++;
+    }
+    s++;
+  }
+  else while (*s && !isspace(*s))
+      s++;
+  return s;
+} /* skipfilename */
+
diff --git a/C/libqhull_r.c b/C/libqhull_r.c
new file mode 100644
--- /dev/null
+++ b/C/libqhull_r.c
@@ -0,0 +1,1403 @@
+/*<html><pre>  -<a                             href="qh-qhull_r.htm"
+  >-------------------------------</a><a name="TOP">-</a>
+
+   libqhull_r.c
+   Quickhull algorithm for convex hulls
+
+   qhull() and top-level routines
+
+   see qh-qhull_r.htm, libqhull.h, unix_r.c
+
+   see qhull_ra.h for internal functions
+
+   Copyright (c) 1993-2015 The Geometry Center.
+   $Id: //main/2015/qhull/src/libqhull_r/libqhull_r.c#2 $$Change: 2047 $
+   $DateTime: 2016/01/04 22:03:18 $$Author: bbarber $
+*/
+
+#include "qhull_ra.h"
+
+/*============= functions in alphabetic order after qhull() =======*/
+
+/*-<a                             href="qh-qhull_r.htm#TOC"
+  >-------------------------------</a><a name="qhull">-</a>
+
+  qh_qhull(qh)
+    compute DIM3 convex hull of qh.num_points starting at qh.first_point
+    qh->contains all global options and variables
+
+  returns:
+    returns polyhedron
+      qh.facet_list, qh.num_facets, qh.vertex_list, qh.num_vertices,
+
+    returns global variables
+      qh.hulltime, qh.max_outside, qh.interior_point, qh.max_vertex, qh.min_vertex
+
+    returns precision constants
+      qh.ANGLEround, centrum_radius, cos_max, DISTround, MAXabs_coord, ONEmerge
+
+  notes:
+    unless needed for output
+      qh.max_vertex and qh.min_vertex are max/min due to merges
+
+  see:
+    to add individual points to either qh.num_points
+      use qh_addpoint()
+
+    if qh.GETarea
+      qh_produceoutput() returns qh.totarea and qh.totvol via qh_getarea()
+
+  design:
+    record starting time
+    initialize hull and partition points
+    build convex hull
+    unless early termination
+      update facet->maxoutside for vertices, coplanar, and near-inside points
+    error if temporary sets exist
+    record end time
+*/
+
+void qh_qhull(qhT *qh) {
+  int numoutside;
+
+  qh->hulltime= qh_CPUclock;
+  if (qh->RERUN || qh->JOGGLEmax < REALmax/2)
+    qh_build_withrestart(qh);
+  else {
+    qh_initbuild(qh);
+    qh_buildhull(qh);
+  }
+  if (!qh->STOPpoint && !qh->STOPcone) {
+    if (qh->ZEROall_ok && !qh->TESTvneighbors && qh->MERGEexact)
+      qh_checkzero(qh, qh_ALL);
+    if (qh->ZEROall_ok && !qh->TESTvneighbors && !qh->WAScoplanar) {
+      trace2((qh, qh->ferr, 2055, "qh_qhull: all facets are clearly convex and no coplanar points.  Post-merging and check of maxout not needed.\n"));
+      qh->DOcheckmax= False;
+    }else {
+      if (qh->MERGEexact || (qh->hull_dim > qh_DIMreduceBuild && qh->PREmerge))
+        qh_postmerge(qh, "First post-merge", qh->premerge_centrum, qh->premerge_cos,
+             (qh->POSTmerge ? False : qh->TESTvneighbors));
+      else if (!qh->POSTmerge && qh->TESTvneighbors)
+        qh_postmerge(qh, "For testing vertex neighbors", qh->premerge_centrum,
+             qh->premerge_cos, True);
+      if (qh->POSTmerge)
+        qh_postmerge(qh, "For post-merging", qh->postmerge_centrum,
+             qh->postmerge_cos, qh->TESTvneighbors);
+      if (qh->visible_list == qh->facet_list) { /* i.e., merging done */
+        qh->findbestnew= True;
+        qh_partitionvisible(qh /*qh.visible_list*/, !qh_ALL, &numoutside);
+        qh->findbestnew= False;
+        qh_deletevisible(qh /*qh.visible_list*/);
+        qh_resetlists(qh, False, qh_RESETvisible /*qh.visible_list newvertex_list newfacet_list */);
+      }
+    }
+    if (qh->DOcheckmax){
+      if (qh->REPORTfreq) {
+        qh_buildtracing(qh, NULL, NULL);
+        qh_fprintf(qh, qh->ferr, 8115, "\nTesting all coplanar points.\n");
+      }
+      qh_check_maxout(qh);
+    }
+    if (qh->KEEPnearinside && !qh->maxoutdone)
+      qh_nearcoplanar(qh);
+  }
+  if (qh_setsize(qh, qh->qhmem.tempstack) != 0) {
+    qh_fprintf(qh, qh->ferr, 6164, "qhull internal error (qh_qhull): temporary sets not empty(%d)\n",
+             qh_setsize(qh, qh->qhmem.tempstack));
+    qh_errexit(qh, qh_ERRqhull, NULL, NULL);
+  }
+  qh->hulltime= qh_CPUclock - qh->hulltime;
+  qh->QHULLfinished= True;
+  trace1((qh, qh->ferr, 1036, "Qhull: algorithm completed\n"));
+} /* qhull */
+
+/*-<a                             href="qh-qhull_r.htm#TOC"
+  >-------------------------------</a><a name="addpoint">-</a>
+
+  qh_addpoint(qh, furthest, facet, checkdist )
+    add point (usually furthest point) above facet to hull
+    if checkdist,
+      check that point is above facet.
+      if point is not outside of the hull, uses qh_partitioncoplanar()
+      assumes that facet is defined by qh_findbestfacet()
+    else if facet specified,
+      assumes that point is above facet (major damage if below)
+    for Delaunay triangulations,
+      Use qh_setdelaunay() to lift point to paraboloid and scale by 'Qbb' if needed
+      Do not use options 'Qbk', 'QBk', or 'QbB' since they scale the coordinates.
+
+  returns:
+    returns False if user requested an early termination
+     qh.visible_list, newfacet_list, delvertex_list, NEWfacets may be defined
+    updates qh.facet_list, qh.num_facets, qh.vertex_list, qh.num_vertices
+    clear qh.maxoutdone (will need to call qh_check_maxout() for facet->maxoutside)
+    if unknown point, adds a pointer to qh.other_points
+      do not deallocate the point's coordinates
+
+  notes:
+    assumes point is near its best facet and not at a local minimum of a lens
+      distributions.  Use qh_findbestfacet to avoid this case.
+    uses qh.visible_list, qh.newfacet_list, qh.delvertex_list, qh.NEWfacets
+
+  see also:
+    qh_triangulate() -- triangulate non-simplicial facets
+
+  design:
+    add point to other_points if needed
+    if checkdist
+      if point not above facet
+        partition coplanar point
+        exit
+    exit if pre STOPpoint requested
+    find horizon and visible facets for point
+    make new facets for point to horizon
+    make hyperplanes for point
+    compute balance statistics
+    match neighboring new facets
+    update vertex neighbors and delete interior vertices
+    exit if STOPcone requested
+    merge non-convex new facets
+    if merge found, many merges, or 'Qf'
+       use qh_findbestnew() instead of qh_findbest()
+    partition outside points from visible facets
+    delete visible facets
+    check polyhedron if requested
+    exit if post STOPpoint requested
+    reset working lists of facets and vertices
+*/
+boolT qh_addpoint(qhT *qh, pointT *furthest, facetT *facet, boolT checkdist) {
+  int goodvisible, goodhorizon;
+  vertexT *vertex;
+  facetT *newfacet;
+  realT dist, newbalance, pbalance;
+  boolT isoutside= False;
+  int numpart, numpoints, numnew, firstnew;
+
+  qh->maxoutdone= False;
+  if (qh_pointid(qh, furthest) == qh_IDunknown)
+    qh_setappend(qh, &qh->other_points, furthest);
+  if (!facet) {
+    qh_fprintf(qh, qh->ferr, 6213, "qhull internal error (qh_addpoint): NULL facet.  Need to call qh_findbestfacet first\n");
+    qh_errexit(qh, qh_ERRqhull, NULL, NULL);
+  }
+  if (checkdist) {
+    facet= qh_findbest(qh, furthest, facet, !qh_ALL, !qh_ISnewfacets, !qh_NOupper,
+                        &dist, &isoutside, &numpart);
+    zzadd_(Zpartition, numpart);
+    if (!isoutside) {
+      zinc_(Znotmax);  /* last point of outsideset is no longer furthest. */
+      facet->notfurthest= True;
+      qh_partitioncoplanar(qh, furthest, facet, &dist);
+      return True;
+    }
+  }
+  qh_buildtracing(qh, furthest, facet);
+  if (qh->STOPpoint < 0 && qh->furthest_id == -qh->STOPpoint-1) {
+    facet->notfurthest= True;
+    return False;
+  }
+  qh_findhorizon(qh, furthest, facet, &goodvisible, &goodhorizon);
+  if (qh->ONLYgood && !(goodvisible+goodhorizon) && !qh->GOODclosest) {
+    zinc_(Znotgood);
+    facet->notfurthest= True;
+    /* last point of outsideset is no longer furthest.  This is ok
+       since all points of the outside are likely to be bad */
+    qh_resetlists(qh, False, qh_RESETvisible /*qh.visible_list newvertex_list newfacet_list */);
+    return True;
+  }
+  zzinc_(Zprocessed);
+  firstnew= qh->facet_id;
+  vertex= qh_makenewfacets(qh, furthest /*visible_list, attaches if !ONLYgood */);
+  qh_makenewplanes(qh /* newfacet_list */);
+  numnew= qh->facet_id - firstnew;
+  newbalance= numnew - (realT) (qh->num_facets-qh->num_visible)
+                         * qh->hull_dim/qh->num_vertices;
+  wadd_(Wnewbalance, newbalance);
+  wadd_(Wnewbalance2, newbalance * newbalance);
+  if (qh->ONLYgood
+  && !qh_findgood(qh, qh->newfacet_list, goodhorizon) && !qh->GOODclosest) {
+    FORALLnew_facets
+      qh_delfacet(qh, newfacet);
+    qh_delvertex(qh, vertex);
+    qh_resetlists(qh, True, qh_RESETvisible /*qh.visible_list newvertex_list newfacet_list */);
+    zinc_(Znotgoodnew);
+    facet->notfurthest= True;
+    return True;
+  }
+  if (qh->ONLYgood)
+    qh_attachnewfacets(qh /*visible_list*/);
+  qh_matchnewfacets(qh);
+  qh_updatevertices(qh);
+  if (qh->STOPcone && qh->furthest_id == qh->STOPcone-1) {
+    facet->notfurthest= True;
+    return False;  /* visible_list etc. still defined */
+  }
+  qh->findbestnew= False;
+  if (qh->PREmerge || qh->MERGEexact) {
+    qh_premerge(qh, vertex, qh->premerge_centrum, qh->premerge_cos);
+    if (qh_USEfindbestnew)
+      qh->findbestnew= True;
+    else {
+      FORALLnew_facets {
+        if (!newfacet->simplicial) {
+          qh->findbestnew= True;  /* use qh_findbestnew instead of qh_findbest*/
+          break;
+        }
+      }
+    }
+  }else if (qh->BESToutside)
+    qh->findbestnew= True;
+  qh_partitionvisible(qh /*qh.visible_list*/, !qh_ALL, &numpoints);
+  qh->findbestnew= False;
+  qh->findbest_notsharp= False;
+  zinc_(Zpbalance);
+  pbalance= numpoints - (realT) qh->hull_dim /* assumes all points extreme */
+                * (qh->num_points - qh->num_vertices)/qh->num_vertices;
+  wadd_(Wpbalance, pbalance);
+  wadd_(Wpbalance2, pbalance * pbalance);
+  qh_deletevisible(qh /*qh.visible_list*/);
+  zmax_(Zmaxvertex, qh->num_vertices);
+  qh->NEWfacets= False;
+  if (qh->IStracing >= 4) {
+    if (qh->num_facets < 2000)
+      qh_printlists(qh);
+    qh_printfacetlist(qh, qh->newfacet_list, NULL, True);
+    qh_checkpolygon(qh, qh->facet_list);
+  }else if (qh->CHECKfrequently) {
+    if (qh->num_facets < 50)
+      qh_checkpolygon(qh, qh->facet_list);
+    else
+      qh_checkpolygon(qh, qh->newfacet_list);
+  }
+  if (qh->STOPpoint > 0 && qh->furthest_id == qh->STOPpoint-1)
+    return False;
+  qh_resetlists(qh, True, qh_RESETvisible /*qh.visible_list newvertex_list newfacet_list */);
+  /* qh_triangulate(qh); to test qh.TRInormals */
+  trace2((qh, qh->ferr, 2056, "qh_addpoint: added p%d new facets %d new balance %2.2g point balance %2.2g\n",
+    qh_pointid(qh, furthest), numnew, newbalance, pbalance));
+  return True;
+} /* addpoint */
+
+/*-<a                             href="qh-qhull_r.htm#TOC"
+  >-------------------------------</a><a name="build_withrestart">-</a>
+
+  qh_build_withrestart(qh)
+    allow restarts due to qh.JOGGLEmax while calling qh_buildhull()
+       qh_errexit always undoes qh_build_withrestart()
+    qh.FIRSTpoint/qh.NUMpoints is point array
+       it may be moved by qh_joggleinput(qh)
+*/
+void qh_build_withrestart(qhT *qh) {
+  int restart;
+
+  qh->ALLOWrestart= True;
+  while (True) {
+    restart= setjmp(qh->restartexit); /* simple statement for CRAY J916 */
+    if (restart) {       /* only from qh_precision() */
+      zzinc_(Zretry);
+      wmax_(Wretrymax, qh->JOGGLEmax);
+      /* QH7078 warns about using 'TCn' with 'QJn' */
+      qh->STOPcone= qh_IDunknown; /* if break from joggle, prevents normal output */
+    }
+    if (!qh->RERUN && qh->JOGGLEmax < REALmax/2) {
+      if (qh->build_cnt > qh_JOGGLEmaxretry) {
+        qh_fprintf(qh, qh->ferr, 6229, "qhull precision error: %d attempts to construct a convex hull\n\
+        with joggled input.  Increase joggle above 'QJ%2.2g'\n\
+        or modify qh_JOGGLE... parameters in user.h\n",
+           qh->build_cnt, qh->JOGGLEmax);
+        qh_errexit(qh, qh_ERRqhull, NULL, NULL);
+      }
+      if (qh->build_cnt && !restart)
+        break;
+    }else if (qh->build_cnt && qh->build_cnt >= qh->RERUN)
+      break;
+    qh->STOPcone= 0;
+    qh_freebuild(qh, True);  /* first call is a nop */
+    qh->build_cnt++;
+    if (!qh->qhull_optionsiz)
+      qh->qhull_optionsiz= (int)strlen(qh->qhull_options);   /* WARN64 */
+    else {
+      qh->qhull_options [qh->qhull_optionsiz]= '\0';
+      qh->qhull_optionlen= qh_OPTIONline;  /* starts a new line */
+    }
+    qh_option(qh, "_run", &qh->build_cnt, NULL);
+    if (qh->build_cnt == qh->RERUN) {
+      qh->IStracing= qh->TRACElastrun;  /* duplicated from qh_initqhull_globals */
+      if (qh->TRACEpoint != qh_IDunknown || qh->TRACEdist < REALmax/2 || qh->TRACEmerge) {
+        qh->TRACElevel= (qh->IStracing? qh->IStracing : 3);
+        qh->IStracing= 0;
+      }
+      qh->qhmem.IStracing= qh->IStracing;
+    }
+    if (qh->JOGGLEmax < REALmax/2)
+      qh_joggleinput(qh);
+    qh_initbuild(qh);
+    qh_buildhull(qh);
+    if (qh->JOGGLEmax < REALmax/2 && !qh->MERGING)
+      qh_checkconvex(qh, qh->facet_list, qh_ALGORITHMfault);
+  }
+  qh->ALLOWrestart= False;
+} /* qh_build_withrestart */
+
+/*-<a                             href="qh-qhull_r.htm#TOC"
+  >-------------------------------</a><a name="buildhull">-</a>
+
+  qh_buildhull(qh)
+    construct a convex hull by adding outside points one at a time
+
+  returns:
+
+  notes:
+    may be called multiple times
+    checks facet and vertex lists for incorrect flags
+    to recover from STOPcone, call qh_deletevisible and qh_resetlists
+
+  design:
+    check visible facet and newfacet flags
+    check newlist vertex flags and qh.STOPcone/STOPpoint
+    for each facet with a furthest outside point
+      add point to facet
+      exit if qh.STOPcone or qh.STOPpoint requested
+    if qh.NARROWhull for initial simplex
+      partition remaining outside points to coplanar sets
+*/
+void qh_buildhull(qhT *qh) {
+  facetT *facet;
+  pointT *furthest;
+  vertexT *vertex;
+  int id;
+
+  trace1((qh, qh->ferr, 1037, "qh_buildhull: start build hull\n"));
+  FORALLfacets {
+    if (facet->visible || facet->newfacet) {
+      qh_fprintf(qh, qh->ferr, 6165, "qhull internal error (qh_buildhull): visible or new facet f%d in facet list\n",
+                   facet->id);
+      qh_errexit(qh, qh_ERRqhull, facet, NULL);
+    }
+  }
+  FORALLvertices {
+    if (vertex->newlist) {
+      qh_fprintf(qh, qh->ferr, 6166, "qhull internal error (qh_buildhull): new vertex f%d in vertex list\n",
+                   vertex->id);
+      qh_errprint(qh, "ERRONEOUS", NULL, NULL, NULL, vertex);
+      qh_errexit(qh, qh_ERRqhull, NULL, NULL);
+    }
+    id= qh_pointid(qh, vertex->point);
+    if ((qh->STOPpoint>0 && id == qh->STOPpoint-1) ||
+        (qh->STOPpoint<0 && id == -qh->STOPpoint-1) ||
+        (qh->STOPcone>0 && id == qh->STOPcone-1)) {
+      trace1((qh, qh->ferr, 1038,"qh_buildhull: stop point or cone P%d in initial hull\n", id));
+      return;
+    }
+  }
+  qh->facet_next= qh->facet_list;      /* advance facet when processed */
+  while ((furthest= qh_nextfurthest(qh, &facet))) {
+    qh->num_outside--;  /* if ONLYmax, furthest may not be outside */
+    if (!qh_addpoint(qh, furthest, facet, qh->ONLYmax))
+      break;
+  }
+  if (qh->NARROWhull) /* move points from outsideset to coplanarset */
+    qh_outcoplanar(qh /* facet_list */ );
+  if (qh->num_outside && !furthest) {
+    qh_fprintf(qh, qh->ferr, 6167, "qhull internal error (qh_buildhull): %d outside points were never processed.\n", qh->num_outside);
+    qh_errexit(qh, qh_ERRqhull, NULL, NULL);
+  }
+  trace1((qh, qh->ferr, 1039, "qh_buildhull: completed the hull construction\n"));
+} /* buildhull */
+
+
+/*-<a                             href="qh-qhull_r.htm#TOC"
+  >-------------------------------</a><a name="buildtracing">-</a>
+
+  qh_buildtracing(qh, furthest, facet )
+    trace an iteration of qh_buildhull() for furthest point and facet
+    if !furthest, prints progress message
+
+  returns:
+    tracks progress with qh.lastreport
+    updates qh.furthest_id (-3 if furthest is NULL)
+    also resets visit_id, vertext_visit on wrap around
+
+  see:
+    qh_tracemerging()
+
+  design:
+    if !furthest
+      print progress message
+      exit
+    if 'TFn' iteration
+      print progress message
+    else if tracing
+      trace furthest point and facet
+    reset qh.visit_id and qh.vertex_visit if overflow may occur
+    set qh.furthest_id for tracing
+*/
+void qh_buildtracing(qhT *qh, pointT *furthest, facetT *facet) {
+  realT dist= 0;
+  float cpu;
+  int total, furthestid;
+  time_t timedata;
+  struct tm *tp;
+  vertexT *vertex;
+
+  qh->old_randomdist= qh->RANDOMdist;
+  qh->RANDOMdist= False;
+  if (!furthest) {
+    time(&timedata);
+    tp= localtime(&timedata);
+    cpu= (float)qh_CPUclock - (float)qh->hulltime;
+    cpu /= (float)qh_SECticks;
+    total= zzval_(Ztotmerge) - zzval_(Zcyclehorizon) + zzval_(Zcyclefacettot);
+    qh_fprintf(qh, qh->ferr, 8118, "\n\
+At %02d:%02d:%02d & %2.5g CPU secs, qhull has created %d facets and merged %d.\n\
+ The current hull contains %d facets and %d vertices.  Last point was p%d\n",
+      tp->tm_hour, tp->tm_min, tp->tm_sec, cpu, qh->facet_id -1,
+      total, qh->num_facets, qh->num_vertices, qh->furthest_id);
+    return;
+  }
+  furthestid= qh_pointid(qh, furthest);
+  if (qh->TRACEpoint == furthestid) {
+    qh->IStracing= qh->TRACElevel;
+    qh->qhmem.IStracing= qh->TRACElevel;
+  }else if (qh->TRACEpoint != qh_IDunknown && qh->TRACEdist < REALmax/2) {
+    qh->IStracing= 0;
+    qh->qhmem.IStracing= 0;
+  }
+  if (qh->REPORTfreq && (qh->facet_id-1 > qh->lastreport+qh->REPORTfreq)) {
+    qh->lastreport= qh->facet_id-1;
+    time(&timedata);
+    tp= localtime(&timedata);
+    cpu= (float)qh_CPUclock - (float)qh->hulltime;
+    cpu /= (float)qh_SECticks;
+    total= zzval_(Ztotmerge) - zzval_(Zcyclehorizon) + zzval_(Zcyclefacettot);
+    zinc_(Zdistio);
+    qh_distplane(qh, furthest, facet, &dist);
+    qh_fprintf(qh, qh->ferr, 8119, "\n\
+At %02d:%02d:%02d & %2.5g CPU secs, qhull has created %d facets and merged %d.\n\
+ The current hull contains %d facets and %d vertices.  There are %d\n\
+ outside points.  Next is point p%d(v%d), %2.2g above f%d.\n",
+      tp->tm_hour, tp->tm_min, tp->tm_sec, cpu, qh->facet_id -1,
+      total, qh->num_facets, qh->num_vertices, qh->num_outside+1,
+      furthestid, qh->vertex_id, dist, getid_(facet));
+  }else if (qh->IStracing >=1) {
+    cpu= (float)qh_CPUclock - (float)qh->hulltime;
+    cpu /= (float)qh_SECticks;
+    qh_distplane(qh, furthest, facet, &dist);
+    qh_fprintf(qh, qh->ferr, 8120, "qh_addpoint: add p%d(v%d) to hull of %d facets(%2.2g above f%d) and %d outside at %4.4g CPU secs.  Previous was p%d.\n",
+      furthestid, qh->vertex_id, qh->num_facets, dist,
+      getid_(facet), qh->num_outside+1, cpu, qh->furthest_id);
+  }
+  zmax_(Zvisit2max, (int)qh->visit_id/2);
+  if (qh->visit_id > (unsigned) INT_MAX) { /* 31 bits */
+    zinc_(Zvisit);
+    qh->visit_id= 0;
+    FORALLfacets
+      facet->visitid= 0;
+  }
+  zmax_(Zvvisit2max, (int)qh->vertex_visit/2);
+  if (qh->vertex_visit > (unsigned) INT_MAX) { /* 31 bits */ 
+    zinc_(Zvvisit);
+    qh->vertex_visit= 0;
+    FORALLvertices
+      vertex->visitid= 0;
+  }
+  qh->furthest_id= furthestid;
+  qh->RANDOMdist= qh->old_randomdist;
+} /* buildtracing */
+
+/*-<a                             href="qh-qhull_r.htm#TOC"
+  >-------------------------------</a><a name="errexit2">-</a>
+
+  qh_errexit2(qh, exitcode, facet, otherfacet )
+    return exitcode to system after an error
+    report two facets
+
+  returns:
+    assumes exitcode non-zero
+
+  see:
+    normally use qh_errexit() in user.c(reports a facet and a ridge)
+*/
+void qh_errexit2(qhT *qh, int exitcode, facetT *facet, facetT *otherfacet) {
+
+  qh_errprint(qh, "ERRONEOUS", facet, otherfacet, NULL, NULL);
+  qh_errexit(qh, exitcode, NULL, NULL);
+} /* errexit2 */
+
+
+/*-<a                             href="qh-qhull_r.htm#TOC"
+  >-------------------------------</a><a name="findhorizon">-</a>
+
+  qh_findhorizon(qh, point, facet, goodvisible, goodhorizon )
+    given a visible facet, find the point's horizon and visible facets
+    for all facets, !facet-visible
+
+  returns:
+    returns qh.visible_list/num_visible with all visible facets
+      marks visible facets with ->visible
+    updates count of good visible and good horizon facets
+    updates qh.max_outside, qh.max_vertex, facet->maxoutside
+
+  see:
+    similar to qh_delpoint()
+
+  design:
+    move facet to qh.visible_list at end of qh.facet_list
+    for all visible facets
+     for each unvisited neighbor of a visible facet
+       compute distance of point to neighbor
+       if point above neighbor
+         move neighbor to end of qh.visible_list
+       else if point is coplanar with neighbor
+         update qh.max_outside, qh.max_vertex, neighbor->maxoutside
+         mark neighbor coplanar (will create a samecycle later)
+         update horizon statistics
+*/
+void qh_findhorizon(qhT *qh, pointT *point, facetT *facet, int *goodvisible, int *goodhorizon) {
+  facetT *neighbor, **neighborp, *visible;
+  int numhorizon= 0, coplanar= 0;
+  realT dist;
+
+  trace1((qh, qh->ferr, 1040,"qh_findhorizon: find horizon for point p%d facet f%d\n",qh_pointid(qh, point),facet->id));
+  *goodvisible= *goodhorizon= 0;
+  zinc_(Ztotvisible);
+  qh_removefacet(qh, facet);  /* visible_list at end of qh->facet_list */
+  qh_appendfacet(qh, facet);
+  qh->num_visible= 1;
+  if (facet->good)
+    (*goodvisible)++;
+  qh->visible_list= facet;
+  facet->visible= True;
+  facet->f.replace= NULL;
+  if (qh->IStracing >=4)
+    qh_errprint(qh, "visible", facet, NULL, NULL, NULL);
+  qh->visit_id++;
+  FORALLvisible_facets {
+    if (visible->tricoplanar && !qh->TRInormals) {
+      qh_fprintf(qh, qh->ferr, 6230, "Qhull internal error (qh_findhorizon): does not work for tricoplanar facets.  Use option 'Q11'\n");
+      qh_errexit(qh, qh_ERRqhull, visible, NULL);
+    }
+    visible->visitid= qh->visit_id;
+    FOREACHneighbor_(visible) {
+      if (neighbor->visitid == qh->visit_id)
+        continue;
+      neighbor->visitid= qh->visit_id;
+      zzinc_(Znumvisibility);
+      qh_distplane(qh, point, neighbor, &dist);
+      if (dist > qh->MINvisible) {
+        zinc_(Ztotvisible);
+        qh_removefacet(qh, neighbor);  /* append to end of qh->visible_list */
+        qh_appendfacet(qh, neighbor);
+        neighbor->visible= True;
+        neighbor->f.replace= NULL;
+        qh->num_visible++;
+        if (neighbor->good)
+          (*goodvisible)++;
+        if (qh->IStracing >=4)
+          qh_errprint(qh, "visible", neighbor, NULL, NULL, NULL);
+      }else {
+        if (dist > - qh->MAXcoplanar) {
+          neighbor->coplanar= True;
+          zzinc_(Zcoplanarhorizon);
+          qh_precision(qh, "coplanar horizon");
+          coplanar++;
+          if (qh->MERGING) {
+            if (dist > 0) {
+              maximize_(qh->max_outside, dist);
+              maximize_(qh->max_vertex, dist);
+#if qh_MAXoutside
+              maximize_(neighbor->maxoutside, dist);
+#endif
+            }else
+              minimize_(qh->min_vertex, dist);  /* due to merge later */
+          }
+          trace2((qh, qh->ferr, 2057, "qh_findhorizon: point p%d is coplanar to horizon f%d, dist=%2.7g < qh->MINvisible(%2.7g)\n",
+              qh_pointid(qh, point), neighbor->id, dist, qh->MINvisible));
+        }else
+          neighbor->coplanar= False;
+        zinc_(Ztothorizon);
+        numhorizon++;
+        if (neighbor->good)
+          (*goodhorizon)++;
+        if (qh->IStracing >=4)
+          qh_errprint(qh, "horizon", neighbor, NULL, NULL, NULL);
+      }
+    }
+  }
+  if (!numhorizon) {
+    qh_precision(qh, "empty horizon");
+    qh_fprintf(qh, qh->ferr, 6168, "qhull precision error (qh_findhorizon): empty horizon\n\
+QhullPoint p%d was above all facets.\n", qh_pointid(qh, point));
+    qh_printfacetlist(qh, qh->facet_list, NULL, True);
+    qh_errexit(qh, qh_ERRprec, NULL, NULL);
+  }
+  trace1((qh, qh->ferr, 1041, "qh_findhorizon: %d horizon facets(good %d), %d visible(good %d), %d coplanar\n",
+       numhorizon, *goodhorizon, qh->num_visible, *goodvisible, coplanar));
+  if (qh->IStracing >= 4 && qh->num_facets < 50)
+    qh_printlists(qh);
+} /* findhorizon */
+
+/*-<a                             href="qh-qhull_r.htm#TOC"
+  >-------------------------------</a><a name="nextfurthest">-</a>
+
+  qh_nextfurthest(qh, visible )
+    returns next furthest point and visible facet for qh_addpoint()
+    starts search at qh.facet_next
+
+  returns:
+    removes furthest point from outside set
+    NULL if none available
+    advances qh.facet_next over facets with empty outside sets
+
+  design:
+    for each facet from qh.facet_next
+      if empty outside set
+        advance qh.facet_next
+      else if qh.NARROWhull
+        determine furthest outside point
+        if furthest point is not outside
+          advance qh.facet_next(point will be coplanar)
+    remove furthest point from outside set
+*/
+pointT *qh_nextfurthest(qhT *qh, facetT **visible) {
+  facetT *facet;
+  int size, idx;
+  realT randr, dist;
+  pointT *furthest;
+
+  while ((facet= qh->facet_next) != qh->facet_tail) {
+    if (!facet->outsideset) {
+      qh->facet_next= facet->next;
+      continue;
+    }
+    SETreturnsize_(facet->outsideset, size);
+    if (!size) {
+      qh_setfree(qh, &facet->outsideset);
+      qh->facet_next= facet->next;
+      continue;
+    }
+    if (qh->NARROWhull) {
+      if (facet->notfurthest)
+        qh_furthestout(qh, facet);
+      furthest= (pointT*)qh_setlast(facet->outsideset);
+#if qh_COMPUTEfurthest
+      qh_distplane(qh, furthest, facet, &dist);
+      zinc_(Zcomputefurthest);
+#else
+      dist= facet->furthestdist;
+#endif
+      if (dist < qh->MINoutside) { /* remainder of outside set is coplanar for qh_outcoplanar */
+        qh->facet_next= facet->next;
+        continue;
+      }
+    }
+    if (!qh->RANDOMoutside && !qh->VIRTUALmemory) {
+      if (qh->PICKfurthest) {
+        qh_furthestnext(qh /* qh->facet_list */);
+        facet= qh->facet_next;
+      }
+      *visible= facet;
+      return((pointT*)qh_setdellast(facet->outsideset));
+    }
+    if (qh->RANDOMoutside) {
+      int outcoplanar = 0;
+      if (qh->NARROWhull) {
+        FORALLfacets {
+          if (facet == qh->facet_next)
+            break;
+          if (facet->outsideset)
+            outcoplanar += qh_setsize(qh, facet->outsideset);
+        }
+      }
+      randr= qh_RANDOMint;
+      randr= randr/(qh_RANDOMmax+1);
+      idx= (int)floor((qh->num_outside - outcoplanar) * randr);
+      FORALLfacet_(qh->facet_next) {
+        if (facet->outsideset) {
+          SETreturnsize_(facet->outsideset, size);
+          if (!size)
+            qh_setfree(qh, &facet->outsideset);
+          else if (size > idx) {
+            *visible= facet;
+            return((pointT*)qh_setdelnth(qh, facet->outsideset, idx));
+          }else
+            idx -= size;
+        }
+      }
+      qh_fprintf(qh, qh->ferr, 6169, "qhull internal error (qh_nextfurthest): num_outside %d is too low\nby at least %d, or a random real %g >= 1.0\n",
+              qh->num_outside, idx+1, randr);
+      qh_errexit(qh, qh_ERRqhull, NULL, NULL);
+    }else { /* VIRTUALmemory */
+      facet= qh->facet_tail->previous;
+      if (!(furthest= (pointT*)qh_setdellast(facet->outsideset))) {
+        if (facet->outsideset)
+          qh_setfree(qh, &facet->outsideset);
+        qh_removefacet(qh, facet);
+        qh_prependfacet(qh, facet, &qh->facet_list);
+        continue;
+      }
+      *visible= facet;
+      return furthest;
+    }
+  }
+  return NULL;
+} /* nextfurthest */
+
+/*-<a                             href="qh-qhull_r.htm#TOC"
+  >-------------------------------</a><a name="partitionall">-</a>
+
+  qh_partitionall(qh, vertices, points, numpoints )
+    partitions all points in points/numpoints to the outsidesets of facets
+    vertices= vertices in qh.facet_list(!partitioned)
+
+  returns:
+    builds facet->outsideset
+    does not partition qh.GOODpoint
+    if qh.ONLYgood && !qh.MERGING,
+      does not partition qh.GOODvertex
+
+  notes:
+    faster if qh.facet_list sorted by anticipated size of outside set
+
+  design:
+    initialize pointset with all points
+    remove vertices from pointset
+    remove qh.GOODpointp from pointset (unless it's qh.STOPcone or qh.STOPpoint)
+    for all facets
+      for all remaining points in pointset
+        compute distance from point to facet
+        if point is outside facet
+          remove point from pointset (by not reappending)
+          update bestpoint
+          append point or old bestpoint to facet's outside set
+      append bestpoint to facet's outside set (furthest)
+    for all points remaining in pointset
+      partition point into facets' outside sets and coplanar sets
+*/
+void qh_partitionall(qhT *qh, setT *vertices, pointT *points, int numpoints){
+  setT *pointset;
+  vertexT *vertex, **vertexp;
+  pointT *point, **pointp, *bestpoint;
+  int size, point_i, point_n, point_end, remaining, i, id;
+  facetT *facet;
+  realT bestdist= -REALmax, dist, distoutside;
+
+  trace1((qh, qh->ferr, 1042, "qh_partitionall: partition all points into outside sets\n"));
+  pointset= qh_settemp(qh, numpoints);
+  qh->num_outside= 0;
+  pointp= SETaddr_(pointset, pointT);
+  for (i=numpoints, point= points; i--; point += qh->hull_dim)
+    *(pointp++)= point;
+  qh_settruncate(qh, pointset, numpoints);
+  FOREACHvertex_(vertices) {
+    if ((id= qh_pointid(qh, vertex->point)) >= 0)
+      SETelem_(pointset, id)= NULL;
+  }
+  id= qh_pointid(qh, qh->GOODpointp);
+  if (id >=0 && qh->STOPcone-1 != id && -qh->STOPpoint-1 != id)
+    SETelem_(pointset, id)= NULL;
+  if (qh->GOODvertexp && qh->ONLYgood && !qh->MERGING) { /* matches qhull()*/
+    if ((id= qh_pointid(qh, qh->GOODvertexp)) >= 0)
+      SETelem_(pointset, id)= NULL;
+  }
+  if (!qh->BESToutside) {  /* matches conditional for qh_partitionpoint below */
+    distoutside= qh_DISToutside; /* multiple of qh.MINoutside & qh.max_outside, see user.h */
+    zval_(Ztotpartition)= qh->num_points - qh->hull_dim - 1; /*misses GOOD... */
+    remaining= qh->num_facets;
+    point_end= numpoints;
+    FORALLfacets {
+      size= point_end/(remaining--) + 100;
+      facet->outsideset= qh_setnew(qh, size);
+      bestpoint= NULL;
+      point_end= 0;
+      FOREACHpoint_i_(qh, pointset) {
+        if (point) {
+          zzinc_(Zpartitionall);
+          qh_distplane(qh, point, facet, &dist);
+          if (dist < distoutside)
+            SETelem_(pointset, point_end++)= point;
+          else {
+            qh->num_outside++;
+            if (!bestpoint) {
+              bestpoint= point;
+              bestdist= dist;
+            }else if (dist > bestdist) {
+              qh_setappend(qh, &facet->outsideset, bestpoint);
+              bestpoint= point;
+              bestdist= dist;
+            }else
+              qh_setappend(qh, &facet->outsideset, point);
+          }
+        }
+      }
+      if (bestpoint) {
+        qh_setappend(qh, &facet->outsideset, bestpoint);
+#if !qh_COMPUTEfurthest
+        facet->furthestdist= bestdist;
+#endif
+      }else
+        qh_setfree(qh, &facet->outsideset);
+      qh_settruncate(qh, pointset, point_end);
+    }
+  }
+  /* if !qh->BESToutside, pointset contains points not assigned to outsideset */
+  if (qh->BESToutside || qh->MERGING || qh->KEEPcoplanar || qh->KEEPinside) {
+    qh->findbestnew= True;
+    FOREACHpoint_i_(qh, pointset) {
+      if (point)
+        qh_partitionpoint(qh, point, qh->facet_list);
+    }
+    qh->findbestnew= False;
+  }
+  zzadd_(Zpartitionall, zzval_(Zpartition));
+  zzval_(Zpartition)= 0;
+  qh_settempfree(qh, &pointset);
+  if (qh->IStracing >= 4)
+    qh_printfacetlist(qh, qh->facet_list, NULL, True);
+} /* partitionall */
+
+
+/*-<a                             href="qh-qhull_r.htm#TOC"
+  >-------------------------------</a><a name="partitioncoplanar">-</a>
+
+  qh_partitioncoplanar(qh, point, facet, dist )
+    partition coplanar point to a facet
+    dist is distance from point to facet
+    if dist NULL,
+      searches for bestfacet and does nothing if inside
+    if qh.findbestnew set,
+      searches new facets instead of using qh_findbest()
+
+  returns:
+    qh.max_ouside updated
+    if qh.KEEPcoplanar or qh.KEEPinside
+      point assigned to best coplanarset
+
+  notes:
+    facet->maxoutside is updated at end by qh_check_maxout
+
+  design:
+    if dist undefined
+      find best facet for point
+      if point sufficiently below facet (depends on qh.NEARinside and qh.KEEPinside)
+        exit
+    if keeping coplanar/nearinside/inside points
+      if point is above furthest coplanar point
+        append point to coplanar set (it is the new furthest)
+        update qh.max_outside
+      else
+        append point one before end of coplanar set
+    else if point is clearly outside of qh.max_outside and bestfacet->coplanarset
+    and bestfacet is more than perpendicular to facet
+      repartition the point using qh_findbest() -- it may be put on an outsideset
+    else
+      update qh.max_outside
+*/
+void qh_partitioncoplanar(qhT *qh, pointT *point, facetT *facet, realT *dist) {
+  facetT *bestfacet;
+  pointT *oldfurthest;
+  realT bestdist, dist2= 0, angle;
+  int numpart= 0, oldfindbest;
+  boolT isoutside;
+
+  qh->WAScoplanar= True;
+  if (!dist) {
+    if (qh->findbestnew)
+      bestfacet= qh_findbestnew(qh, point, facet, &bestdist, qh_ALL, &isoutside, &numpart);
+    else
+      bestfacet= qh_findbest(qh, point, facet, qh_ALL, !qh_ISnewfacets, qh->DELAUNAY,
+                          &bestdist, &isoutside, &numpart);
+    zinc_(Ztotpartcoplanar);
+    zzadd_(Zpartcoplanar, numpart);
+    if (!qh->DELAUNAY && !qh->KEEPinside) { /*  for 'd', bestdist skips upperDelaunay facets */
+      if (qh->KEEPnearinside) {
+        if (bestdist < -qh->NEARinside) {
+          zinc_(Zcoplanarinside);
+          trace4((qh, qh->ferr, 4062, "qh_partitioncoplanar: point p%d is more than near-inside facet f%d dist %2.2g findbestnew %d\n",
+                  qh_pointid(qh, point), bestfacet->id, bestdist, qh->findbestnew));
+          return;
+        }
+      }else if (bestdist < -qh->MAXcoplanar) {
+          trace4((qh, qh->ferr, 4063, "qh_partitioncoplanar: point p%d is inside facet f%d dist %2.2g findbestnew %d\n",
+                  qh_pointid(qh, point), bestfacet->id, bestdist, qh->findbestnew));
+        zinc_(Zcoplanarinside);
+        return;
+      }
+    }
+  }else {
+    bestfacet= facet;
+    bestdist= *dist;
+  }
+  if (bestdist > qh->max_outside) {
+    if (!dist && facet != bestfacet) {
+      zinc_(Zpartangle);
+      angle= qh_getangle(qh, facet->normal, bestfacet->normal);
+      if (angle < 0) {
+        /* typically due to deleted vertex and coplanar facets, e.g.,
+             RBOX 1000 s Z1 G1e-13 t1001185205 | QHULL Tv */
+        zinc_(Zpartflip);
+        trace2((qh, qh->ferr, 2058, "qh_partitioncoplanar: repartition point p%d from f%d.  It is above flipped facet f%d dist %2.2g\n",
+                qh_pointid(qh, point), facet->id, bestfacet->id, bestdist));
+        oldfindbest= qh->findbestnew;
+        qh->findbestnew= False;
+        qh_partitionpoint(qh, point, bestfacet);
+        qh->findbestnew= oldfindbest;
+        return;
+      }
+    }
+    qh->max_outside= bestdist;
+    if (bestdist > qh->TRACEdist) {
+      qh_fprintf(qh, qh->ferr, 8122, "qh_partitioncoplanar: ====== p%d from f%d increases max_outside to %2.2g of f%d last p%d\n",
+                     qh_pointid(qh, point), facet->id, bestdist, bestfacet->id, qh->furthest_id);
+      qh_errprint(qh, "DISTANT", facet, bestfacet, NULL, NULL);
+    }
+  }
+  if (qh->KEEPcoplanar + qh->KEEPinside + qh->KEEPnearinside) {
+    oldfurthest= (pointT*)qh_setlast(bestfacet->coplanarset);
+    if (oldfurthest) {
+      zinc_(Zcomputefurthest);
+      qh_distplane(qh, oldfurthest, bestfacet, &dist2);
+    }
+    if (!oldfurthest || dist2 < bestdist)
+      qh_setappend(qh, &bestfacet->coplanarset, point);
+    else
+      qh_setappend2ndlast(qh, &bestfacet->coplanarset, point);
+  }
+  trace4((qh, qh->ferr, 4064, "qh_partitioncoplanar: point p%d is coplanar with facet f%d(or inside) dist %2.2g\n",
+          qh_pointid(qh, point), bestfacet->id, bestdist));
+} /* partitioncoplanar */
+
+/*-<a                             href="qh-qhull_r.htm#TOC"
+  >-------------------------------</a><a name="partitionpoint">-</a>
+
+  qh_partitionpoint(qh, point, facet )
+    assigns point to an outside set, coplanar set, or inside set (i.e., dropt)
+    if qh.findbestnew
+      uses qh_findbestnew() to search all new facets
+    else
+      uses qh_findbest()
+
+  notes:
+    after qh_distplane(), this and qh_findbest() are most expensive in 3-d
+
+  design:
+    find best facet for point
+      (either exhaustive search of new facets or directed search from facet)
+    if qh.NARROWhull
+      retain coplanar and nearinside points as outside points
+    if point is outside bestfacet
+      if point above furthest point for bestfacet
+        append point to outside set (it becomes the new furthest)
+        if outside set was empty
+          move bestfacet to end of qh.facet_list (i.e., after qh.facet_next)
+        update bestfacet->furthestdist
+      else
+        append point one before end of outside set
+    else if point is coplanar to bestfacet
+      if keeping coplanar points or need to update qh.max_outside
+        partition coplanar point into bestfacet
+    else if near-inside point
+      partition as coplanar point into bestfacet
+    else is an inside point
+      if keeping inside points
+        partition as coplanar point into bestfacet
+*/
+void qh_partitionpoint(qhT *qh, pointT *point, facetT *facet) {
+  realT bestdist;
+  boolT isoutside;
+  facetT *bestfacet;
+  int numpart;
+#if qh_COMPUTEfurthest
+  realT dist;
+#endif
+
+  if (qh->findbestnew)
+    bestfacet= qh_findbestnew(qh, point, facet, &bestdist, qh->BESToutside, &isoutside, &numpart);
+  else
+    bestfacet= qh_findbest(qh, point, facet, qh->BESToutside, qh_ISnewfacets, !qh_NOupper,
+                          &bestdist, &isoutside, &numpart);
+  zinc_(Ztotpartition);
+  zzadd_(Zpartition, numpart);
+  if (qh->NARROWhull) {
+    if (qh->DELAUNAY && !isoutside && bestdist >= -qh->MAXcoplanar)
+      qh_precision(qh, "nearly incident point(narrow hull)");
+    if (qh->KEEPnearinside) {
+      if (bestdist >= -qh->NEARinside)
+        isoutside= True;
+    }else if (bestdist >= -qh->MAXcoplanar)
+      isoutside= True;
+  }
+
+  if (isoutside) {
+    if (!bestfacet->outsideset
+    || !qh_setlast(bestfacet->outsideset)) {
+      qh_setappend(qh, &(bestfacet->outsideset), point);
+      if (!bestfacet->newfacet) {
+        qh_removefacet(qh, bestfacet);  /* make sure it's after qh->facet_next */
+        qh_appendfacet(qh, bestfacet);
+      }
+#if !qh_COMPUTEfurthest
+      bestfacet->furthestdist= bestdist;
+#endif
+    }else {
+#if qh_COMPUTEfurthest
+      zinc_(Zcomputefurthest);
+      qh_distplane(qh, oldfurthest, bestfacet, &dist);
+      if (dist < bestdist)
+        qh_setappend(qh, &(bestfacet->outsideset), point);
+      else
+        qh_setappend2ndlast(qh, &(bestfacet->outsideset), point);
+#else
+      if (bestfacet->furthestdist < bestdist) {
+        qh_setappend(qh, &(bestfacet->outsideset), point);
+        bestfacet->furthestdist= bestdist;
+      }else
+        qh_setappend2ndlast(qh, &(bestfacet->outsideset), point);
+#endif
+    }
+    qh->num_outside++;
+    trace4((qh, qh->ferr, 4065, "qh_partitionpoint: point p%d is outside facet f%d new? %d (or narrowhull)\n",
+          qh_pointid(qh, point), bestfacet->id, bestfacet->newfacet));
+  }else if (qh->DELAUNAY || bestdist >= -qh->MAXcoplanar) { /* for 'd', bestdist skips upperDelaunay facets */
+    zzinc_(Zcoplanarpart);
+    if (qh->DELAUNAY)
+      qh_precision(qh, "nearly incident point");
+    if ((qh->KEEPcoplanar + qh->KEEPnearinside) || bestdist > qh->max_outside)
+      qh_partitioncoplanar(qh, point, bestfacet, &bestdist);
+    else {
+      trace4((qh, qh->ferr, 4066, "qh_partitionpoint: point p%d is coplanar to facet f%d (dropped)\n",
+          qh_pointid(qh, point), bestfacet->id));
+    }
+  }else if (qh->KEEPnearinside && bestdist > -qh->NEARinside) {
+    zinc_(Zpartnear);
+    qh_partitioncoplanar(qh, point, bestfacet, &bestdist);
+  }else {
+    zinc_(Zpartinside);
+    trace4((qh, qh->ferr, 4067, "qh_partitionpoint: point p%d is inside all facets, closest to f%d dist %2.2g\n",
+          qh_pointid(qh, point), bestfacet->id, bestdist));
+    if (qh->KEEPinside)
+      qh_partitioncoplanar(qh, point, bestfacet, &bestdist);
+  }
+} /* partitionpoint */
+
+/*-<a                             href="qh-qhull_r.htm#TOC"
+  >-------------------------------</a><a name="partitionvisible">-</a>
+
+  qh_partitionvisible(qh, allpoints, numoutside )
+    partitions points in visible facets to qh.newfacet_list
+    qh.visible_list= visible facets
+    for visible facets
+      1st neighbor (if any) points to a horizon facet or a new facet
+    if allpoints(!used),
+      repartitions coplanar points
+
+  returns:
+    updates outside sets and coplanar sets of qh.newfacet_list
+    updates qh.num_outside (count of outside points)
+
+  notes:
+    qh.findbest_notsharp should be clear (extra work if set)
+
+  design:
+    for all visible facets with outside set or coplanar set
+      select a newfacet for visible facet
+      if outside set
+        partition outside set into new facets
+      if coplanar set and keeping coplanar/near-inside/inside points
+        if allpoints
+          partition coplanar set into new facets, may be assigned outside
+        else
+          partition coplanar set into coplanar sets of new facets
+    for each deleted vertex
+      if allpoints
+        partition vertex into new facets, may be assigned outside
+      else
+        partition vertex into coplanar sets of new facets
+*/
+void qh_partitionvisible(qhT *qh /*qh.visible_list*/, boolT allpoints, int *numoutside) {
+  facetT *visible, *newfacet;
+  pointT *point, **pointp;
+  int coplanar=0, size;
+  unsigned count;
+  vertexT *vertex, **vertexp;
+
+  if (qh->ONLYmax)
+    maximize_(qh->MINoutside, qh->max_vertex);
+  *numoutside= 0;
+  FORALLvisible_facets {
+    if (!visible->outsideset && !visible->coplanarset)
+      continue;
+    newfacet= visible->f.replace;
+    count= 0;
+    while (newfacet && newfacet->visible) {
+      newfacet= newfacet->f.replace;
+      if (count++ > qh->facet_id)
+        qh_infiniteloop(qh, visible);
+    }
+    if (!newfacet)
+      newfacet= qh->newfacet_list;
+    if (newfacet == qh->facet_tail) {
+      qh_fprintf(qh, qh->ferr, 6170, "qhull precision error (qh_partitionvisible): all new facets deleted as\n        degenerate facets. Can not continue.\n");
+      qh_errexit(qh, qh_ERRprec, NULL, NULL);
+    }
+    if (visible->outsideset) {
+      size= qh_setsize(qh, visible->outsideset);
+      *numoutside += size;
+      qh->num_outside -= size;
+      FOREACHpoint_(visible->outsideset)
+        qh_partitionpoint(qh, point, newfacet);
+    }
+    if (visible->coplanarset && (qh->KEEPcoplanar + qh->KEEPinside + qh->KEEPnearinside)) {
+      size= qh_setsize(qh, visible->coplanarset);
+      coplanar += size;
+      FOREACHpoint_(visible->coplanarset) {
+        if (allpoints) /* not used */
+          qh_partitionpoint(qh, point, newfacet);
+        else
+          qh_partitioncoplanar(qh, point, newfacet, NULL);
+      }
+    }
+  }
+  FOREACHvertex_(qh->del_vertices) {
+    if (vertex->point) {
+      if (allpoints) /* not used */
+        qh_partitionpoint(qh, vertex->point, qh->newfacet_list);
+      else
+        qh_partitioncoplanar(qh, vertex->point, qh->newfacet_list, NULL);
+    }
+  }
+  trace1((qh, qh->ferr, 1043,"qh_partitionvisible: partitioned %d points from outsidesets and %d points from coplanarsets\n", *numoutside, coplanar));
+} /* partitionvisible */
+
+
+
+/*-<a                             href="qh-qhull_r.htm#TOC"
+  >-------------------------------</a><a name="precision">-</a>
+
+  qh_precision(qh, reason )
+    restart on precision errors if not merging and if 'QJn'
+*/
+void qh_precision(qhT *qh, const char *reason) {
+
+  if (qh->ALLOWrestart && !qh->PREmerge && !qh->MERGEexact) {
+    if (qh->JOGGLEmax < REALmax/2) {
+      trace0((qh, qh->ferr, 26, "qh_precision: qhull restart because of %s\n", reason));
+      /* May be called repeatedly if qh->ALLOWrestart */
+      longjmp(qh->restartexit, qh_ERRprec);
+    }
+  }
+} /* qh_precision */
+
+/*-<a                             href="qh-qhull_r.htm#TOC"
+  >-------------------------------</a><a name="printsummary">-</a>
+
+  qh_printsummary(qh, fp )
+    prints summary to fp
+
+  notes:
+    not in io_r.c so that user_eg.c can prevent io_r.c from loading
+    qh_printsummary and qh_countfacets must match counts
+
+  design:
+    determine number of points, vertices, and coplanar points
+    print summary
+*/
+void qh_printsummary(qhT *qh, FILE *fp) {
+  realT ratio, outerplane, innerplane;
+  float cpu;
+  int size, id, nummerged, numvertices, numcoplanars= 0, nonsimplicial=0;
+  int goodused;
+  facetT *facet;
+  const char *s;
+  int numdel= zzval_(Zdelvertextot);
+  int numtricoplanars= 0;
+
+  size= qh->num_points + qh_setsize(qh, qh->other_points);
+  numvertices= qh->num_vertices - qh_setsize(qh, qh->del_vertices);
+  id= qh_pointid(qh, qh->GOODpointp);
+  FORALLfacets {
+    if (facet->coplanarset)
+      numcoplanars += qh_setsize(qh, facet->coplanarset);
+    if (facet->good) {
+      if (facet->simplicial) {
+        if (facet->keepcentrum && facet->tricoplanar)
+          numtricoplanars++;
+      }else if (qh_setsize(qh, facet->vertices) != qh->hull_dim)
+        nonsimplicial++;
+    }
+  }
+  if (id >=0 && qh->STOPcone-1 != id && -qh->STOPpoint-1 != id)
+    size--;
+  if (qh->STOPcone || qh->STOPpoint)
+      qh_fprintf(qh, fp, 9288, "\nAt a premature exit due to 'TVn', 'TCn', 'TRn', or precision error with 'QJn'.");
+  if (qh->UPPERdelaunay)
+    goodused= qh->GOODvertex + qh->GOODpoint + qh->SPLITthresholds;
+  else if (qh->DELAUNAY)
+    goodused= qh->GOODvertex + qh->GOODpoint + qh->GOODthreshold;
+  else
+    goodused= qh->num_good;
+  nummerged= zzval_(Ztotmerge) - zzval_(Zcyclehorizon) + zzval_(Zcyclefacettot);
+  if (qh->VORONOI) {
+    if (qh->UPPERdelaunay)
+      qh_fprintf(qh, fp, 9289, "\n\
+Furthest-site Voronoi vertices by the convex hull of %d points in %d-d:\n\n", size, qh->hull_dim);
+    else
+      qh_fprintf(qh, fp, 9290, "\n\
+Voronoi diagram by the convex hull of %d points in %d-d:\n\n", size, qh->hull_dim);
+    qh_fprintf(qh, fp, 9291, "  Number of Voronoi regions%s: %d\n",
+              qh->ATinfinity ? " and at-infinity" : "", numvertices);
+    if (numdel)
+      qh_fprintf(qh, fp, 9292, "  Total number of deleted points due to merging: %d\n", numdel);
+    if (numcoplanars - numdel > 0)
+      qh_fprintf(qh, fp, 9293, "  Number of nearly incident points: %d\n", numcoplanars - numdel);
+    else if (size - numvertices - numdel > 0)
+      qh_fprintf(qh, fp, 9294, "  Total number of nearly incident points: %d\n", size - numvertices - numdel);
+    qh_fprintf(qh, fp, 9295, "  Number of%s Voronoi vertices: %d\n",
+              goodused ? " 'good'" : "", qh->num_good);
+    if (nonsimplicial)
+      qh_fprintf(qh, fp, 9296, "  Number of%s non-simplicial Voronoi vertices: %d\n",
+              goodused ? " 'good'" : "", nonsimplicial);
+  }else if (qh->DELAUNAY) {
+    if (qh->UPPERdelaunay)
+      qh_fprintf(qh, fp, 9297, "\n\
+Furthest-site Delaunay triangulation by the convex hull of %d points in %d-d:\n\n", size, qh->hull_dim);
+    else
+      qh_fprintf(qh, fp, 9298, "\n\
+Delaunay triangulation by the convex hull of %d points in %d-d:\n\n", size, qh->hull_dim);
+    qh_fprintf(qh, fp, 9299, "  Number of input sites%s: %d\n",
+              qh->ATinfinity ? " and at-infinity" : "", numvertices);
+    if (numdel)
+      qh_fprintf(qh, fp, 9300, "  Total number of deleted points due to merging: %d\n", numdel);
+    if (numcoplanars - numdel > 0)
+      qh_fprintf(qh, fp, 9301, "  Number of nearly incident points: %d\n", numcoplanars - numdel);
+    else if (size - numvertices - numdel > 0)
+      qh_fprintf(qh, fp, 9302, "  Total number of nearly incident points: %d\n", size - numvertices - numdel);
+    qh_fprintf(qh, fp, 9303, "  Number of%s Delaunay regions: %d\n",
+              goodused ? " 'good'" : "", qh->num_good);
+    if (nonsimplicial)
+      qh_fprintf(qh, fp, 9304, "  Number of%s non-simplicial Delaunay regions: %d\n",
+              goodused ? " 'good'" : "", nonsimplicial);
+  }else if (qh->HALFspace) {
+    qh_fprintf(qh, fp, 9305, "\n\
+Halfspace intersection by the convex hull of %d points in %d-d:\n\n", size, qh->hull_dim);
+    qh_fprintf(qh, fp, 9306, "  Number of halfspaces: %d\n", size);
+    qh_fprintf(qh, fp, 9307, "  Number of non-redundant halfspaces: %d\n", numvertices);
+    if (numcoplanars) {
+      if (qh->KEEPinside && qh->KEEPcoplanar)
+        s= "similar and redundant";
+      else if (qh->KEEPinside)
+        s= "redundant";
+      else
+        s= "similar";
+      qh_fprintf(qh, fp, 9308, "  Number of %s halfspaces: %d\n", s, numcoplanars);
+    }
+    qh_fprintf(qh, fp, 9309, "  Number of intersection points: %d\n", qh->num_facets - qh->num_visible);
+    if (goodused)
+      qh_fprintf(qh, fp, 9310, "  Number of 'good' intersection points: %d\n", qh->num_good);
+    if (nonsimplicial)
+      qh_fprintf(qh, fp, 9311, "  Number of%s non-simplicial intersection points: %d\n",
+              goodused ? " 'good'" : "", nonsimplicial);
+  }else {
+    qh_fprintf(qh, fp, 9312, "\n\
+Convex hull of %d points in %d-d:\n\n", size, qh->hull_dim);
+    qh_fprintf(qh, fp, 9313, "  Number of vertices: %d\n", numvertices);
+    if (numcoplanars) {
+      if (qh->KEEPinside && qh->KEEPcoplanar)
+        s= "coplanar and interior";
+      else if (qh->KEEPinside)
+        s= "interior";
+      else
+        s= "coplanar";
+      qh_fprintf(qh, fp, 9314, "  Number of %s points: %d\n", s, numcoplanars);
+    }
+    qh_fprintf(qh, fp, 9315, "  Number of facets: %d\n", qh->num_facets - qh->num_visible);
+    if (goodused)
+      qh_fprintf(qh, fp, 9316, "  Number of 'good' facets: %d\n", qh->num_good);
+    if (nonsimplicial)
+      qh_fprintf(qh, fp, 9317, "  Number of%s non-simplicial facets: %d\n",
+              goodused ? " 'good'" : "", nonsimplicial);
+  }
+  if (numtricoplanars)
+      qh_fprintf(qh, fp, 9318, "  Number of triangulated facets: %d\n", numtricoplanars);
+  qh_fprintf(qh, fp, 9319, "\nStatistics for: %s | %s",
+                      qh->rbox_command, qh->qhull_command);
+  if (qh->ROTATErandom != INT_MIN)
+    qh_fprintf(qh, fp, 9320, " QR%d\n\n", qh->ROTATErandom);
+  else
+    qh_fprintf(qh, fp, 9321, "\n\n");
+  qh_fprintf(qh, fp, 9322, "  Number of points processed: %d\n", zzval_(Zprocessed));
+  qh_fprintf(qh, fp, 9323, "  Number of hyperplanes created: %d\n", zzval_(Zsetplane));
+  if (qh->DELAUNAY)
+    qh_fprintf(qh, fp, 9324, "  Number of facets in hull: %d\n", qh->num_facets - qh->num_visible);
+  qh_fprintf(qh, fp, 9325, "  Number of distance tests for qhull: %d\n", zzval_(Zpartition)+
+      zzval_(Zpartitionall)+zzval_(Znumvisibility)+zzval_(Zpartcoplanar));
+#if 0  /* NOTE: must print before printstatistics() */
+  {realT stddev, ave;
+  qh_fprintf(qh, fp, 9326, "  average new facet balance: %2.2g\n",
+          wval_(Wnewbalance)/zval_(Zprocessed));
+  stddev= qh_stddev(zval_(Zprocessed), wval_(Wnewbalance),
+                                 wval_(Wnewbalance2), &ave);
+  qh_fprintf(qh, fp, 9327, "  new facet standard deviation: %2.2g\n", stddev);
+  qh_fprintf(qh, fp, 9328, "  average partition balance: %2.2g\n",
+          wval_(Wpbalance)/zval_(Zpbalance));
+  stddev= qh_stddev(zval_(Zpbalance), wval_(Wpbalance),
+                                 wval_(Wpbalance2), &ave);
+  qh_fprintf(qh, fp, 9329, "  partition standard deviation: %2.2g\n", stddev);
+  }
+#endif
+  if (nummerged) {
+    qh_fprintf(qh, fp, 9330,"  Number of distance tests for merging: %d\n",zzval_(Zbestdist)+
+          zzval_(Zcentrumtests)+zzval_(Zdistconvex)+zzval_(Zdistcheck)+
+          zzval_(Zdistzero));
+    qh_fprintf(qh, fp, 9331,"  Number of distance tests for checking: %d\n",zzval_(Zcheckpart));
+    qh_fprintf(qh, fp, 9332,"  Number of merged facets: %d\n", nummerged);
+  }
+  if (!qh->RANDOMoutside && qh->QHULLfinished) {
+    cpu= (float)qh->hulltime;
+    cpu /= (float)qh_SECticks;
+    wval_(Wcpu)= cpu;
+    qh_fprintf(qh, fp, 9333, "  CPU seconds to compute hull (after input): %2.4g\n", cpu);
+  }
+  if (qh->RERUN) {
+    if (!qh->PREmerge && !qh->MERGEexact)
+      qh_fprintf(qh, fp, 9334, "  Percentage of runs with precision errors: %4.1f\n",
+           zzval_(Zretry)*100.0/qh->build_cnt);  /* careful of order */
+  }else if (qh->JOGGLEmax < REALmax/2) {
+    if (zzval_(Zretry))
+      qh_fprintf(qh, fp, 9335, "  After %d retries, input joggled by: %2.2g\n",
+         zzval_(Zretry), qh->JOGGLEmax);
+    else
+      qh_fprintf(qh, fp, 9336, "  Input joggled by: %2.2g\n", qh->JOGGLEmax);
+  }
+  if (qh->totarea != 0.0)
+    qh_fprintf(qh, fp, 9337, "  %s facet area:   %2.8g\n",
+            zzval_(Ztotmerge) ? "Approximate" : "Total", qh->totarea);
+  if (qh->totvol != 0.0)
+    qh_fprintf(qh, fp, 9338, "  %s volume:       %2.8g\n",
+            zzval_(Ztotmerge) ? "Approximate" : "Total", qh->totvol);
+  if (qh->MERGING) {
+    qh_outerinner(qh, NULL, &outerplane, &innerplane);
+    if (outerplane > 2 * qh->DISTround) {
+      qh_fprintf(qh, fp, 9339, "  Maximum distance of %spoint above facet: %2.2g",
+            (qh->QHULLfinished ? "" : "merged "), outerplane);
+      ratio= outerplane/(qh->ONEmerge + qh->DISTround);
+      /* don't report ratio if MINoutside is large */
+      if (ratio > 0.05 && 2* qh->ONEmerge > qh->MINoutside && qh->JOGGLEmax > REALmax/2)
+        qh_fprintf(qh, fp, 9340, " (%.1fx)\n", ratio);
+      else
+        qh_fprintf(qh, fp, 9341, "\n");
+    }
+    if (innerplane < -2 * qh->DISTround) {
+      qh_fprintf(qh, fp, 9342, "  Maximum distance of %svertex below facet: %2.2g",
+            (qh->QHULLfinished ? "" : "merged "), innerplane);
+      ratio= -innerplane/(qh->ONEmerge+qh->DISTround);
+      if (ratio > 0.05 && qh->JOGGLEmax > REALmax/2)
+        qh_fprintf(qh, fp, 9343, " (%.1fx)\n", ratio);
+      else
+        qh_fprintf(qh, fp, 9344, "\n");
+    }
+  }
+  qh_fprintf(qh, fp, 9345, "\n");
+} /* printsummary */
+
+
diff --git a/C/mem_r.c b/C/mem_r.c
new file mode 100644
--- /dev/null
+++ b/C/mem_r.c
@@ -0,0 +1,562 @@
+/*<html><pre>  -<a                             href="qh-mem_r.htm"
+  >-------------------------------</a><a name="TOP">-</a>
+
+  mem_r.c
+    memory management routines for qhull
+
+  See libqhull/mem_r.c for a standalone program.
+
+  To initialize memory:
+
+    qh_meminit(qh, stderr);
+    qh_meminitbuffers(qh, qh->IStracing, qh_MEMalign, 7, qh_MEMbufsize,qh_MEMinitbuf);
+    qh_memsize(qh, (int)sizeof(facetT));
+    qh_memsize(qh, (int)sizeof(facetT));
+    ...
+    qh_memsetup(qh);
+
+  To free up all memory buffers:
+    qh_memfreeshort(qh, &curlong, &totlong);
+
+  if qh_NOmem,
+    malloc/free is used instead of mem.c
+
+  notes:
+    uses Quickfit algorithm (freelists for commonly allocated sizes)
+    assumes small sizes for freelists (it discards the tail of memory buffers)
+
+  see:
+    qh-mem_r.htm and mem_r.h
+    global_r.c (qh_initbuffers) for an example of using mem_r.c
+
+  Copyright (c) 1993-2015 The Geometry Center.
+  $Id: //main/2015/qhull/src/libqhull_r/mem_r.c#5 $$Change: 2065 $
+  $DateTime: 2016/01/18 13:51:04 $$Author: bbarber $
+*/
+
+#include "libqhull_r.h"  /* includes user_r.h and mem_r.h */
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#ifndef qh_NOmem
+
+/*============= internal functions ==============*/
+
+static int qh_intcompare(const void *i, const void *j);
+
+/*========== functions in alphabetical order ======== */
+
+/*-<a                             href="qh-mem_r.htm#TOC"
+  >-------------------------------</a><a name="intcompare">-</a>
+
+  qh_intcompare( i, j )
+    used by qsort and bsearch to compare two integers
+*/
+static int qh_intcompare(const void *i, const void *j) {
+  return(*((const int *)i) - *((const int *)j));
+} /* intcompare */
+
+
+/*-<a                             href="qh-mem_r.htm#TOC"
+  >--------------------------------</a><a name="memalloc">-</a>
+
+  qh_memalloc( qh, insize )
+    returns object of insize bytes
+    qhmem is the global memory structure
+
+  returns:
+    pointer to allocated memory
+    errors if insufficient memory
+
+  notes:
+    use explicit type conversion to avoid type warnings on some compilers
+    actual object may be larger than insize
+    use qh_memalloc_() for inline code for quick allocations
+    logs allocations if 'T5'
+    caller is responsible for freeing the memory.
+    short memory is freed on shutdown by qh_memfreeshort unless qh_NOmem
+
+  design:
+    if size < qh->qhmem.LASTsize
+      if qh->qhmem.freelists[size] non-empty
+        return first object on freelist
+      else
+        round up request to size of qh->qhmem.freelists[size]
+        allocate new allocation buffer if necessary
+        allocate object from allocation buffer
+    else
+      allocate object with qh_malloc() in user.c
+*/
+void *qh_memalloc(qhT *qh, int insize) {
+  void **freelistp, *newbuffer;
+  int idx, size, n;
+  int outsize, bufsize;
+  void *object;
+
+  if (insize<0) {
+      qh_fprintf(qh, qh->qhmem.ferr, 6235, "qhull error (qh_memalloc): negative request size (%d).  Did int overflow due to high-D?\n", insize); /* WARN64 */
+      qh_errexit(qh, qhmem_ERRmem, NULL, NULL);
+  }
+  if (insize>=0 && insize <= qh->qhmem.LASTsize) {
+    idx= qh->qhmem.indextable[insize];
+    outsize= qh->qhmem.sizetable[idx];
+    qh->qhmem.totshort += outsize;
+    freelistp= qh->qhmem.freelists+idx;
+    if ((object= *freelistp)) {
+      qh->qhmem.cntquick++;
+      qh->qhmem.totfree -= outsize;
+      *freelistp= *((void **)*freelistp);  /* replace freelist with next object */
+#ifdef qh_TRACEshort
+      n= qh->qhmem.cntshort+qh->qhmem.cntquick+qh->qhmem.freeshort;
+      if (qh->qhmem.IStracing >= 5)
+          qh_fprintf(qh, qh->qhmem.ferr, 8141, "qh_mem %p n %8d alloc quick: %d bytes (tot %d cnt %d)\n", object, n, outsize, qh->qhmem.totshort, qh->qhmem.cntshort+qh->qhmem.cntquick-qh->qhmem.freeshort);
+#endif
+      return(object);
+    }else {
+      qh->qhmem.cntshort++;
+      if (outsize > qh->qhmem.freesize) {
+        qh->qhmem.totdropped += qh->qhmem.freesize;
+        if (!qh->qhmem.curbuffer)
+          bufsize= qh->qhmem.BUFinit;
+        else
+          bufsize= qh->qhmem.BUFsize;
+        if (!(newbuffer= qh_malloc((size_t)bufsize))) {
+          qh_fprintf(qh, qh->qhmem.ferr, 6080, "qhull error (qh_memalloc): insufficient memory to allocate short memory buffer (%d bytes)\n", bufsize);
+          qh_errexit(qh, qhmem_ERRmem, NULL, NULL);
+        }
+        *((void **)newbuffer)= qh->qhmem.curbuffer;  /* prepend newbuffer to curbuffer
+                                                    list.  newbuffer!=0 by QH6080 */
+        qh->qhmem.curbuffer= newbuffer;
+        size= (sizeof(void **) + qh->qhmem.ALIGNmask) & ~qh->qhmem.ALIGNmask;
+        qh->qhmem.freemem= (void *)((char *)newbuffer+size);
+        qh->qhmem.freesize= bufsize - size;
+        qh->qhmem.totbuffer += bufsize - size; /* easier to check */
+        /* Periodically test totbuffer.  It matches at beginning and exit of every call */
+        n = qh->qhmem.totshort + qh->qhmem.totfree + qh->qhmem.totdropped + qh->qhmem.freesize - outsize;
+        if (qh->qhmem.totbuffer != n) {
+            qh_fprintf(qh, qh->qhmem.ferr, 6212, "qh_memalloc internal error: short totbuffer %d != totshort+totfree... %d\n", qh->qhmem.totbuffer, n);
+            qh_errexit(qh, qhmem_ERRmem, NULL, NULL);
+        }
+      }
+      object= qh->qhmem.freemem;
+      qh->qhmem.freemem= (void *)((char *)qh->qhmem.freemem + outsize);
+      qh->qhmem.freesize -= outsize;
+      qh->qhmem.totunused += outsize - insize;
+#ifdef qh_TRACEshort
+      n= qh->qhmem.cntshort+qh->qhmem.cntquick+qh->qhmem.freeshort;
+      if (qh->qhmem.IStracing >= 5)
+          qh_fprintf(qh, qh->qhmem.ferr, 8140, "qh_mem %p n %8d alloc short: %d bytes (tot %d cnt %d)\n", object, n, outsize, qh->qhmem.totshort, qh->qhmem.cntshort+qh->qhmem.cntquick-qh->qhmem.freeshort);
+#endif
+      return object;
+    }
+  }else {                     /* long allocation */
+    if (!qh->qhmem.indextable) {
+      qh_fprintf(qh, qh->qhmem.ferr, 6081, "qhull internal error (qh_memalloc): qhmem has not been initialized.\n");
+      qh_errexit(qh, qhmem_ERRqhull, NULL, NULL);
+    }
+    outsize= insize;
+    qh->qhmem.cntlong++;
+    qh->qhmem.totlong += outsize;
+    if (qh->qhmem.maxlong < qh->qhmem.totlong)
+      qh->qhmem.maxlong= qh->qhmem.totlong;
+    if (!(object= qh_malloc((size_t)outsize))) {
+      qh_fprintf(qh, qh->qhmem.ferr, 6082, "qhull error (qh_memalloc): insufficient memory to allocate %d bytes\n", outsize);
+      qh_errexit(qh, qhmem_ERRmem, NULL, NULL);
+    }
+    if (qh->qhmem.IStracing >= 5)
+      qh_fprintf(qh, qh->qhmem.ferr, 8057, "qh_mem %p n %8d alloc long: %d bytes (tot %d cnt %d)\n", object, qh->qhmem.cntlong+qh->qhmem.freelong, outsize, qh->qhmem.totlong, qh->qhmem.cntlong-qh->qhmem.freelong);
+  }
+  return(object);
+} /* memalloc */
+
+
+/*-<a                             href="qh-mem_r.htm#TOC"
+  >--------------------------------</a><a name="memcheck">-</a>
+
+  qh_memcheck(qh)
+*/
+void qh_memcheck(qhT *qh) {
+  int i, count, totfree= 0;
+  void *object;
+
+  if (!qh) {
+    qh_fprintf_stderr(6243, "qh_memcheck(qh) error: qh is 0.  It does not point to a qhT");
+    qh_exit(qhmem_ERRqhull);  /* can not use qh_errexit() */
+  }
+  if (qh->qhmem.ferr == 0 || qh->qhmem.IStracing < 0 || qh->qhmem.IStracing > 10 || (((qh->qhmem.ALIGNmask+1) & qh->qhmem.ALIGNmask) != 0)) {
+    qh_fprintf_stderr(6244, "qh_memcheck error: either qh->qhmem is overwritten or qh->qhmem is not initialized.  Call qh_mem_new() or qh_new_qhull() before calling qh_mem routines.  ferr 0x%x IsTracing %d ALIGNmask 0x%x", qh->qhmem.ferr, qh->qhmem.IStracing, qh->qhmem.ALIGNmask);
+    qh_exit(qhmem_ERRqhull);  /* can not use qh_errexit() */
+    return;
+  }
+  if (qh->qhmem.IStracing != 0)
+    qh_fprintf(qh, qh->qhmem.ferr, 8143, "qh_memcheck: check size of freelists on qh->qhmem\nqh_memcheck: A segmentation fault indicates an overwrite of qh->qhmem\n");
+  for (i=0; i < qh->qhmem.TABLEsize; i++) {
+    count=0;
+    for (object= qh->qhmem.freelists[i]; object; object= *((void **)object))
+      count++;
+    totfree += qh->qhmem.sizetable[i] * count;
+  }
+  if (totfree != qh->qhmem.totfree) {
+    qh_fprintf(qh, qh->qhmem.ferr, 6211, "Qhull internal error (qh_memcheck): totfree %d not equal to freelist total %d\n", qh->qhmem.totfree, totfree);
+    qh_errexit(qh, qhmem_ERRqhull, NULL, NULL);
+  }
+  if (qh->qhmem.IStracing != 0)
+    qh_fprintf(qh, qh->qhmem.ferr, 8144, "qh_memcheck: total size of freelists totfree is the same as qh->qhmem.totfree\n", totfree);
+} /* memcheck */
+
+/*-<a                             href="qh-mem_r.htm#TOC"
+  >--------------------------------</a><a name="memfree">-</a>
+
+  qh_memfree(qh, object, insize )
+    free up an object of size bytes
+    size is insize from qh_memalloc
+
+  notes:
+    object may be NULL
+    type checking warns if using (void **)object
+    use qh_memfree_() for quick free's of small objects
+
+  design:
+    if size <= qh->qhmem.LASTsize
+      append object to corresponding freelist
+    else
+      call qh_free(object)
+*/
+void qh_memfree(qhT *qh, void *object, int insize) {
+  void **freelistp;
+  int idx, outsize;
+
+  if (!object)
+    return;
+  if (insize <= qh->qhmem.LASTsize) {
+    qh->qhmem.freeshort++;
+    idx= qh->qhmem.indextable[insize];
+    outsize= qh->qhmem.sizetable[idx];
+    qh->qhmem.totfree += outsize;
+    qh->qhmem.totshort -= outsize;
+    freelistp= qh->qhmem.freelists + idx;
+    *((void **)object)= *freelistp;
+    *freelistp= object;
+#ifdef qh_TRACEshort
+    idx= qh->qhmem.cntshort+qh->qhmem.cntquick+qh->qhmem.freeshort;
+    if (qh->qhmem.IStracing >= 5)
+        qh_fprintf(qh, qh->qhmem.ferr, 8142, "qh_mem %p n %8d free short: %d bytes (tot %d cnt %d)\n", object, idx, outsize, qh->qhmem.totshort, qh->qhmem.cntshort+qh->qhmem.cntquick-qh->qhmem.freeshort);
+#endif
+  }else {
+    qh->qhmem.freelong++;
+    qh->qhmem.totlong -= insize;
+    if (qh->qhmem.IStracing >= 5)
+      qh_fprintf(qh, qh->qhmem.ferr, 8058, "qh_mem %p n %8d free long: %d bytes (tot %d cnt %d)\n", object, qh->qhmem.cntlong+qh->qhmem.freelong, insize, qh->qhmem.totlong, qh->qhmem.cntlong-qh->qhmem.freelong);
+    qh_free(object);
+  }
+} /* memfree */
+
+
+/*-<a                             href="qh-mem_r.htm#TOC"
+  >-------------------------------</a><a name="memfreeshort">-</a>
+
+  qh_memfreeshort(qh, curlong, totlong )
+    frees up all short and qhmem memory allocations
+
+  returns:
+    number and size of current long allocations
+
+  notes:
+    if qh_NOmem (qh_malloc() for all allocations),
+       short objects (e.g., facetT) are not recovered.
+       use qh_freeqhull(qh, qh_ALL) instead.
+
+  see:
+    qh_freeqhull(qh, allMem)
+    qh_memtotal(qh, curlong, totlong, curshort, totshort, maxlong, totbuffer);
+*/
+void qh_memfreeshort(qhT *qh, int *curlong, int *totlong) {
+  void *buffer, *nextbuffer;
+  FILE *ferr;
+
+  *curlong= qh->qhmem.cntlong - qh->qhmem.freelong;
+  *totlong= qh->qhmem.totlong;
+  for (buffer= qh->qhmem.curbuffer; buffer; buffer= nextbuffer) {
+    nextbuffer= *((void **) buffer);
+    qh_free(buffer);
+  }
+  qh->qhmem.curbuffer= NULL;
+  if (qh->qhmem.LASTsize) {
+    qh_free(qh->qhmem.indextable);
+    qh_free(qh->qhmem.freelists);
+    qh_free(qh->qhmem.sizetable);
+  }
+  ferr= qh->qhmem.ferr;
+  memset((char *)&qh->qhmem, 0, sizeof(qh->qhmem));  /* every field is 0, FALSE, NULL */
+  qh->qhmem.ferr= ferr;
+} /* memfreeshort */
+
+
+/*-<a                             href="qh-mem_r.htm#TOC"
+  >--------------------------------</a><a name="meminit">-</a>
+
+  qh_meminit(qh, ferr )
+    initialize qhmem and test sizeof( void*)
+    Does not throw errors.  qh_exit on failure
+*/
+void qh_meminit(qhT *qh, FILE *ferr) {
+
+  memset((char *)&qh->qhmem, 0, sizeof(qh->qhmem));  /* every field is 0, FALSE, NULL */
+  if (ferr)
+      qh->qhmem.ferr= ferr;
+  else
+      qh->qhmem.ferr= stderr;
+  if (sizeof(void*) < sizeof(int)) {
+    qh_fprintf(qh, qh->qhmem.ferr, 6083, "qhull internal error (qh_meminit): sizeof(void*) %d < sizeof(int) %d.  qset.c will not work\n", (int)sizeof(void*), (int)sizeof(int));
+    qh_exit(qhmem_ERRqhull);  /* can not use qh_errexit() */
+  }
+  if (sizeof(void*) > sizeof(ptr_intT)) {
+      qh_fprintf(qh, qh->qhmem.ferr, 6084, "qhull internal error (qh_meminit): sizeof(void*) %d > sizeof(ptr_intT) %d. Change ptr_intT in mem.h to 'long long'\n", (int)sizeof(void*), (int)sizeof(ptr_intT));
+      qh_exit(qhmem_ERRqhull);  /* can not use qh_errexit() */
+  }
+  qh_memcheck(qh);
+} /* meminit */
+
+/*-<a                             href="qh-mem_r.htm#TOC"
+  >-------------------------------</a><a name="meminitbuffers">-</a>
+
+  qh_meminitbuffers(qh, tracelevel, alignment, numsizes, bufsize, bufinit )
+    initialize qhmem
+    if tracelevel >= 5, trace memory allocations
+    alignment= desired address alignment for memory allocations
+    numsizes= number of freelists
+    bufsize=  size of additional memory buffers for short allocations
+    bufinit=  size of initial memory buffer for short allocations
+*/
+void qh_meminitbuffers(qhT *qh, int tracelevel, int alignment, int numsizes, int bufsize, int bufinit) {
+
+  qh->qhmem.IStracing= tracelevel;
+  qh->qhmem.NUMsizes= numsizes;
+  qh->qhmem.BUFsize= bufsize;
+  qh->qhmem.BUFinit= bufinit;
+  qh->qhmem.ALIGNmask= alignment-1;
+  if (qh->qhmem.ALIGNmask & ~qh->qhmem.ALIGNmask) {
+    qh_fprintf(qh, qh->qhmem.ferr, 6085, "qhull internal error (qh_meminit): memory alignment %d is not a power of 2\n", alignment);
+    qh_errexit(qh, qhmem_ERRqhull, NULL, NULL);
+  }
+  qh->qhmem.sizetable= (int *) calloc((size_t)numsizes, sizeof(int));
+  qh->qhmem.freelists= (void **) calloc((size_t)numsizes, sizeof(void *));
+  if (!qh->qhmem.sizetable || !qh->qhmem.freelists) {
+    qh_fprintf(qh, qh->qhmem.ferr, 6086, "qhull error (qh_meminit): insufficient memory\n");
+    qh_errexit(qh, qhmem_ERRmem, NULL, NULL);
+  }
+  if (qh->qhmem.IStracing >= 1)
+    qh_fprintf(qh, qh->qhmem.ferr, 8059, "qh_meminitbuffers: memory initialized with alignment %d\n", alignment);
+} /* meminitbuffers */
+
+/*-<a                             href="qh-mem_r.htm#TOC"
+  >-------------------------------</a><a name="memsetup">-</a>
+
+  qh_memsetup(qh)
+    set up memory after running memsize()
+*/
+void qh_memsetup(qhT *qh) {
+  int k,i;
+
+  qsort(qh->qhmem.sizetable, (size_t)qh->qhmem.TABLEsize, sizeof(int), qh_intcompare);
+  qh->qhmem.LASTsize= qh->qhmem.sizetable[qh->qhmem.TABLEsize-1];
+  if(qh->qhmem.LASTsize >= qh->qhmem.BUFsize || qh->qhmem.LASTsize >= qh->qhmem.BUFinit) {
+    qh_fprintf(qh, qh->qhmem.ferr, 6087, "qhull error (qh_memsetup): largest mem size %d is >= buffer size %d or initial buffer size %d\n",
+            qh->qhmem.LASTsize, qh->qhmem.BUFsize, qh->qhmem.BUFinit);
+    qh_errexit(qh, qhmem_ERRmem, NULL, NULL);
+  }
+  if (!(qh->qhmem.indextable= (int *)qh_malloc((qh->qhmem.LASTsize+1) * sizeof(int)))) {
+    qh_fprintf(qh, qh->qhmem.ferr, 6088, "qhull error (qh_memsetup): insufficient memory\n");
+    qh_errexit(qh, qhmem_ERRmem, NULL, NULL);
+  }
+  for (k=qh->qhmem.LASTsize+1; k--; )
+    qh->qhmem.indextable[k]= k;
+  i= 0;
+  for (k=0; k <= qh->qhmem.LASTsize; k++) {
+    if (qh->qhmem.indextable[k] <= qh->qhmem.sizetable[i])
+      qh->qhmem.indextable[k]= i;
+    else
+      qh->qhmem.indextable[k]= ++i;
+  }
+} /* memsetup */
+
+/*-<a                             href="qh-mem_r.htm#TOC"
+  >-------------------------------</a><a name="memsize">-</a>
+
+  qh_memsize(qh, size )
+    define a free list for this size
+*/
+void qh_memsize(qhT *qh, int size) {
+  int k;
+
+  if(qh->qhmem.LASTsize) {
+    qh_fprintf(qh, qh->qhmem.ferr, 6089, "qhull error (qh_memsize): called after qhmem_setup\n");
+    qh_errexit(qh, qhmem_ERRqhull, NULL, NULL);
+  }
+  size= (size + qh->qhmem.ALIGNmask) & ~qh->qhmem.ALIGNmask;
+  for (k=qh->qhmem.TABLEsize; k--; ) {
+    if (qh->qhmem.sizetable[k] == size)
+      return;
+  }
+  if (qh->qhmem.TABLEsize < qh->qhmem.NUMsizes)
+    qh->qhmem.sizetable[qh->qhmem.TABLEsize++]= size;
+  else
+    qh_fprintf(qh, qh->qhmem.ferr, 7060, "qhull warning (memsize): free list table has room for only %d sizes\n", qh->qhmem.NUMsizes);
+} /* memsize */
+
+
+/*-<a                             href="qh-mem_r.htm#TOC"
+  >-------------------------------</a><a name="memstatistics">-</a>
+
+  qh_memstatistics(qh, fp )
+    print out memory statistics
+
+    Verifies that qh->qhmem.totfree == sum of freelists
+*/
+void qh_memstatistics(qhT *qh, FILE *fp) {
+  int i;
+  int count;
+  void *object;
+
+  qh_memcheck(qh);
+  qh_fprintf(qh, fp, 9278, "\nmemory statistics:\n\
+%7d quick allocations\n\
+%7d short allocations\n\
+%7d long allocations\n\
+%7d short frees\n\
+%7d long frees\n\
+%7d bytes of short memory in use\n\
+%7d bytes of short memory in freelists\n\
+%7d bytes of dropped short memory\n\
+%7d bytes of unused short memory (estimated)\n\
+%7d bytes of long memory allocated (max, except for input)\n\
+%7d bytes of long memory in use (in %d pieces)\n\
+%7d bytes of short memory buffers (minus links)\n\
+%7d bytes per short memory buffer (initially %d bytes)\n",
+           qh->qhmem.cntquick, qh->qhmem.cntshort, qh->qhmem.cntlong,
+           qh->qhmem.freeshort, qh->qhmem.freelong,
+           qh->qhmem.totshort, qh->qhmem.totfree,
+           qh->qhmem.totdropped + qh->qhmem.freesize, qh->qhmem.totunused,
+           qh->qhmem.maxlong, qh->qhmem.totlong, qh->qhmem.cntlong - qh->qhmem.freelong,
+           qh->qhmem.totbuffer, qh->qhmem.BUFsize, qh->qhmem.BUFinit);
+  if (qh->qhmem.cntlarger) {
+    qh_fprintf(qh, fp, 9279, "%7d calls to qh_setlarger\n%7.2g     average copy size\n",
+           qh->qhmem.cntlarger, ((float)qh->qhmem.totlarger)/(float)qh->qhmem.cntlarger);
+    qh_fprintf(qh, fp, 9280, "  freelists(bytes->count):");
+  }
+  for (i=0; i < qh->qhmem.TABLEsize; i++) {
+    count=0;
+    for (object= qh->qhmem.freelists[i]; object; object= *((void **)object))
+      count++;
+    qh_fprintf(qh, fp, 9281, " %d->%d", qh->qhmem.sizetable[i], count);
+  }
+  qh_fprintf(qh, fp, 9282, "\n\n");
+} /* memstatistics */
+
+
+/*-<a                             href="qh-mem_r.htm#TOC"
+  >-------------------------------</a><a name="NOmem">-</a>
+
+  qh_NOmem
+    turn off quick-fit memory allocation
+
+  notes:
+    uses qh_malloc() and qh_free() instead
+*/
+#else /* qh_NOmem */
+
+void *qh_memalloc(qhT *qh, int insize) {
+  void *object;
+
+  if (!(object= qh_malloc((size_t)insize))) {
+    qh_fprintf(qh, qh->qhmem.ferr, 6090, "qhull error (qh_memalloc): insufficient memory\n");
+    qh_errexit(qh, qhmem_ERRmem, NULL, NULL);
+  }
+  qh->qhmem.cntlong++;
+  qh->qhmem.totlong += insize;
+  if (qh->qhmem.maxlong < qh->qhmem.totlong)
+      qh->qhmem.maxlong= qh->qhmem.totlong;
+  if (qh->qhmem.IStracing >= 5)
+    qh_fprintf(qh, qh->qhmem.ferr, 8060, "qh_mem %p n %8d alloc long: %d bytes (tot %d cnt %d)\n", object, qh->qhmem.cntlong+qh->qhmem.freelong, insize, qh->qhmem.totlong, qh->qhmem.cntlong-qh->qhmem.freelong);
+  return object;
+}
+
+void qh_memfree(qhT *qh, void *object, int insize) {
+
+  if (!object)
+    return;
+  qh_free(object);
+  qh->qhmem.freelong++;
+  qh->qhmem.totlong -= insize;
+  if (qh->qhmem.IStracing >= 5)
+    qh_fprintf(qh, qh->qhmem.ferr, 8061, "qh_mem %p n %8d free long: %d bytes (tot %d cnt %d)\n", object, qh->qhmem.cntlong+qh->qhmem.freelong, insize, qh->qhmem.totlong, qh->qhmem.cntlong-qh->qhmem.freelong);
+}
+
+void qh_memfreeshort(qhT *qh, int *curlong, int *totlong) {
+  *totlong= qh->qhmem.totlong;
+  *curlong= qh->qhmem.cntlong - qh->qhmem.freelong;
+  memset((char *)&qh->qhmem, 0, sizeof(qh->qhmem));  /* every field is 0, FALSE, NULL */
+}
+
+void qh_meminit(qhT *qh, FILE *ferr) {
+
+  memset((char *)&qh->qhmem, 0, sizeof(qh->qhmem));  /* every field is 0, FALSE, NULL */
+  if (ferr)
+      qh->qhmem.ferr= ferr;
+  else
+      qh->qhmem.ferr= stderr;
+  if (sizeof(void*) < sizeof(int)) {
+    qh_fprintf(qh, qh->qhmem.ferr, 6091, "qhull internal error (qh_meminit): sizeof(void*) %d < sizeof(int) %d.  qset.c will not work\n", (int)sizeof(void*), (int)sizeof(int));
+    qh_errexit(qh, qhmem_ERRqhull, NULL, NULL);
+  }
+}
+
+void qh_meminitbuffers(qhT *qh, int tracelevel, int alignment, int numsizes, int bufsize, int bufinit) {
+
+  qh->qhmem.IStracing= tracelevel;
+}
+
+void qh_memsetup(qhT *qh) {
+
+}
+
+void qh_memsize(qhT *qh, int size) {
+
+}
+
+void qh_memstatistics(qhT *qh, FILE *fp) {
+
+  qh_fprintf(qh, fp, 9409, "\nmemory statistics:\n\
+%7d long allocations\n\
+%7d long frees\n\
+%7d bytes of long memory allocated (max, except for input)\n\
+%7d bytes of long memory in use (in %d pieces)\n",
+           qh->qhmem.cntlong,
+           qh->qhmem.freelong,
+           qh->qhmem.maxlong, qh->qhmem.totlong, qh->qhmem.cntlong - qh->qhmem.freelong);
+}
+
+#endif /* qh_NOmem */
+
+/*-<a                             href="qh-mem_r.htm#TOC"
+>-------------------------------</a><a name="memtotlong">-</a>
+
+  qh_memtotal(qh, totlong, curlong, totshort, curshort, maxlong, totbuffer )
+    Return the total, allocated long and short memory
+
+  returns:
+    Returns the total current bytes of long and short allocations
+    Returns the current count of long and short allocations
+    Returns the maximum long memory and total short buffer (minus one link per buffer)
+    Does not error (for deprecated UsingLibQhull.cpp (libqhullpcpp))
+*/
+void qh_memtotal(qhT *qh, int *totlong, int *curlong, int *totshort, int *curshort, int *maxlong, int *totbuffer) {
+    *totlong= qh->qhmem.totlong;
+    *curlong= qh->qhmem.cntlong - qh->qhmem.freelong;
+    *totshort= qh->qhmem.totshort;
+    *curshort= qh->qhmem.cntshort + qh->qhmem.cntquick - qh->qhmem.freeshort;
+    *maxlong= qh->qhmem.maxlong;
+    *totbuffer= qh->qhmem.totbuffer;
+} /* memtotlong */
diff --git a/C/merge_r.c b/C/merge_r.c
new file mode 100644
--- /dev/null
+++ b/C/merge_r.c
@@ -0,0 +1,3627 @@
+/*<html><pre>  -<a                             href="qh-merge_r.htm#TOC"
+  >-------------------------------</a><a name="TOP">-</a>
+
+   merge_r.c
+   merges non-convex facets
+
+   see qh-merge_r.htm and merge_r.h
+
+   other modules call qh_premerge() and qh_postmerge()
+
+   the user may call qh_postmerge() to perform additional merges.
+
+   To remove deleted facets and vertices (qhull() in libqhull_r.c):
+     qh_partitionvisible(qh, !qh_ALL, &numoutside);  // visible_list, newfacet_list
+     qh_deletevisible();         // qh.visible_list
+     qh_resetlists(qh, False, qh_RESETvisible);       // qh.visible_list newvertex_list newfacet_list
+
+   assumes qh.CENTERtype= centrum
+
+   merges occur in qh_mergefacet and in qh_mergecycle
+   vertex->neighbors not set until the first merge occurs
+
+   Copyright (c) 1993-2015 C.B. Barber.
+   $Id: //main/2015/qhull/src/libqhull_r/merge_r.c#5 $$Change: 2064 $
+   $DateTime: 2016/01/18 12:36:08 $$Author: bbarber $
+*/
+
+#include "qhull_ra.h"
+
+#ifndef qh_NOmerge
+
+/*===== functions(alphabetical after premerge and postmerge) ======*/
+
+/*-<a                             href="qh-merge_r.htm#TOC"
+  >-------------------------------</a><a name="premerge">-</a>
+
+  qh_premerge(qh, apex, maxcentrum )
+    pre-merge nonconvex facets in qh.newfacet_list for apex
+    maxcentrum defines coplanar and concave (qh_test_appendmerge)
+
+  returns:
+    deleted facets added to qh.visible_list with facet->visible set
+
+  notes:
+    uses globals, qh.MERGEexact, qh.PREmerge
+
+  design:
+    mark duplicate ridges in qh.newfacet_list
+    merge facet cycles in qh.newfacet_list
+    merge duplicate ridges and concave facets in qh.newfacet_list
+    check merged facet cycles for degenerate and redundant facets
+    merge degenerate and redundant facets
+    collect coplanar and concave facets
+    merge concave, coplanar, degenerate, and redundant facets
+*/
+void qh_premerge(qhT *qh, vertexT *apex, realT maxcentrum, realT maxangle) {
+  boolT othermerge= False;
+  facetT *newfacet;
+
+  if (qh->ZEROcentrum && qh_checkzero(qh, !qh_ALL))
+    return;
+  trace2((qh, qh->ferr, 2008, "qh_premerge: premerge centrum %2.2g angle %2.2g for apex v%d facetlist f%d\n",
+            maxcentrum, maxangle, apex->id, getid_(qh->newfacet_list)));
+  if (qh->IStracing >= 4 && qh->num_facets < 50)
+    qh_printlists(qh);
+  qh->centrum_radius= maxcentrum;
+  qh->cos_max= maxangle;
+  qh->degen_mergeset= qh_settemp(qh, qh->TEMPsize);
+  qh->facet_mergeset= qh_settemp(qh, qh->TEMPsize);
+  if (qh->hull_dim >=3) {
+    qh_mark_dupridges(qh, qh->newfacet_list); /* facet_mergeset */
+    qh_mergecycle_all(qh, qh->newfacet_list, &othermerge);
+    qh_forcedmerges(qh, &othermerge /* qh->facet_mergeset */);
+    FORALLnew_facets {  /* test samecycle merges */
+      if (!newfacet->simplicial && !newfacet->mergeridge)
+        qh_degen_redundant_neighbors(qh, newfacet, NULL);
+    }
+    if (qh_merge_degenredundant(qh))
+      othermerge= True;
+  }else /* qh->hull_dim == 2 */
+    qh_mergecycle_all(qh, qh->newfacet_list, &othermerge);
+  qh_flippedmerges(qh, qh->newfacet_list, &othermerge);
+  if (!qh->MERGEexact || zzval_(Ztotmerge)) {
+    zinc_(Zpremergetot);
+    qh->POSTmerging= False;
+    qh_getmergeset_initial(qh, qh->newfacet_list);
+    qh_all_merges(qh, othermerge, False);
+  }
+  qh_settempfree(qh, &qh->facet_mergeset);
+  qh_settempfree(qh, &qh->degen_mergeset);
+} /* premerge */
+
+/*-<a                             href="qh-merge_r.htm#TOC"
+  >-------------------------------</a><a name="postmerge">-</a>
+
+  qh_postmerge(qh, reason, maxcentrum, maxangle, vneighbors )
+    post-merge nonconvex facets as defined by maxcentrum and maxangle
+    'reason' is for reporting progress
+    if vneighbors,
+      calls qh_test_vneighbors at end of qh_all_merge
+    if firstmerge,
+      calls qh_reducevertices before qh_getmergeset
+
+  returns:
+    if first call (qh.visible_list != qh.facet_list),
+      builds qh.facet_newlist, qh.newvertex_list
+    deleted facets added to qh.visible_list with facet->visible
+    qh.visible_list == qh.facet_list
+
+  notes:
+
+
+  design:
+    if first call
+      set qh.visible_list and qh.newfacet_list to qh.facet_list
+      add all facets to qh.newfacet_list
+      mark non-simplicial facets, facet->newmerge
+      set qh.newvertext_list to qh.vertex_list
+      add all vertices to qh.newvertex_list
+      if a pre-merge occured
+        set vertex->delridge {will retest the ridge}
+        if qh.MERGEexact
+          call qh_reducevertices()
+      if no pre-merging
+        merge flipped facets
+    determine non-convex facets
+    merge all non-convex facets
+*/
+void qh_postmerge(qhT *qh, const char *reason, realT maxcentrum, realT maxangle,
+                      boolT vneighbors) {
+  facetT *newfacet;
+  boolT othermerges= False;
+  vertexT *vertex;
+
+  if (qh->REPORTfreq || qh->IStracing) {
+    qh_buildtracing(qh, NULL, NULL);
+    qh_printsummary(qh, qh->ferr);
+    if (qh->PRINTstatistics)
+      qh_printallstatistics(qh, qh->ferr, "reason");
+    qh_fprintf(qh, qh->ferr, 8062, "\n%s with 'C%.2g' and 'A%.2g'\n",
+        reason, maxcentrum, maxangle);
+  }
+  trace2((qh, qh->ferr, 2009, "qh_postmerge: postmerge.  test vneighbors? %d\n",
+            vneighbors));
+  qh->centrum_radius= maxcentrum;
+  qh->cos_max= maxangle;
+  qh->POSTmerging= True;
+  qh->degen_mergeset= qh_settemp(qh, qh->TEMPsize);
+  qh->facet_mergeset= qh_settemp(qh, qh->TEMPsize);
+  if (qh->visible_list != qh->facet_list) {  /* first call */
+    qh->NEWfacets= True;
+    qh->visible_list= qh->newfacet_list= qh->facet_list;
+    FORALLnew_facets {
+      newfacet->newfacet= True;
+       if (!newfacet->simplicial)
+        newfacet->newmerge= True;
+     zinc_(Zpostfacets);
+    }
+    qh->newvertex_list= qh->vertex_list;
+    FORALLvertices
+      vertex->newlist= True;
+    if (qh->VERTEXneighbors) { /* a merge has occurred */
+      FORALLvertices
+        vertex->delridge= True; /* test for redundant, needed? */
+      if (qh->MERGEexact) {
+        if (qh->hull_dim <= qh_DIMreduceBuild)
+          qh_reducevertices(qh); /* was skipped during pre-merging */
+      }
+    }
+    if (!qh->PREmerge && !qh->MERGEexact)
+      qh_flippedmerges(qh, qh->newfacet_list, &othermerges);
+  }
+  qh_getmergeset_initial(qh, qh->newfacet_list);
+  qh_all_merges(qh, False, vneighbors);
+  qh_settempfree(qh, &qh->facet_mergeset);
+  qh_settempfree(qh, &qh->degen_mergeset);
+} /* post_merge */
+
+/*-<a                             href="qh-merge_r.htm#TOC"
+  >-------------------------------</a><a name="all_merges">-</a>
+
+  qh_all_merges(qh, othermerge, vneighbors )
+    merge all non-convex facets
+
+    set othermerge if already merged facets (for qh_reducevertices)
+    if vneighbors
+      tests vertex neighbors for convexity at end
+    qh.facet_mergeset lists the non-convex ridges in qh_newfacet_list
+    qh.degen_mergeset is defined
+    if qh.MERGEexact && !qh.POSTmerging,
+      does not merge coplanar facets
+
+  returns:
+    deleted facets added to qh.visible_list with facet->visible
+    deleted vertices added qh.delvertex_list with vertex->delvertex
+
+  notes:
+    unless !qh.MERGEindependent,
+      merges facets in independent sets
+    uses qh.newfacet_list as argument since merges call qh_removefacet()
+
+  design:
+    while merges occur
+      for each merge in qh.facet_mergeset
+        unless one of the facets was already merged in this pass
+          merge the facets
+        test merged facets for additional merges
+        add merges to qh.facet_mergeset
+      if vertices record neighboring facets
+        rename redundant vertices
+          update qh.facet_mergeset
+    if vneighbors ??
+      tests vertex neighbors for convexity at end
+*/
+void qh_all_merges(qhT *qh, boolT othermerge, boolT vneighbors) {
+  facetT *facet1, *facet2;
+  mergeT *merge;
+  boolT wasmerge= True, isreduce;
+  void **freelistp;  /* used if !qh_NOmem by qh_memfree_() */
+  vertexT *vertex;
+  mergeType mergetype;
+  int numcoplanar=0, numconcave=0, numdegenredun= 0, numnewmerges= 0;
+
+  trace2((qh, qh->ferr, 2010, "qh_all_merges: starting to merge facets beginning from f%d\n",
+            getid_(qh->newfacet_list)));
+  while (True) {
+    wasmerge= False;
+    while (qh_setsize(qh, qh->facet_mergeset)) {
+      while ((merge= (mergeT*)qh_setdellast(qh->facet_mergeset))) {
+        facet1= merge->facet1;
+        facet2= merge->facet2;
+        mergetype= merge->type;
+        qh_memfree_(qh, merge, (int)sizeof(mergeT), freelistp);
+        if (facet1->visible || facet2->visible) /*deleted facet*/
+          continue;
+        if ((facet1->newfacet && !facet1->tested)
+                || (facet2->newfacet && !facet2->tested)) {
+          if (qh->MERGEindependent && mergetype <= MRGanglecoplanar)
+            continue;      /* perform independent sets of merges */
+        }
+        qh_merge_nonconvex(qh, facet1, facet2, mergetype);
+        numdegenredun += qh_merge_degenredundant(qh);
+        numnewmerges++;
+        wasmerge= True;
+        if (mergetype == MRGconcave)
+          numconcave++;
+        else /* MRGcoplanar or MRGanglecoplanar */
+          numcoplanar++;
+      } /* while setdellast */
+      if (qh->POSTmerging && qh->hull_dim <= qh_DIMreduceBuild
+      && numnewmerges > qh_MAXnewmerges) {
+        numnewmerges= 0;
+        qh_reducevertices(qh);  /* otherwise large post merges too slow */
+      }
+      qh_getmergeset(qh, qh->newfacet_list); /* facet_mergeset */
+    } /* while mergeset */
+    if (qh->VERTEXneighbors) {
+      isreduce= False;
+      if (qh->hull_dim >=4 && qh->POSTmerging) {
+        FORALLvertices
+          vertex->delridge= True;
+        isreduce= True;
+      }
+      if ((wasmerge || othermerge) && (!qh->MERGEexact || qh->POSTmerging)
+          && qh->hull_dim <= qh_DIMreduceBuild) {
+        othermerge= False;
+        isreduce= True;
+      }
+      if (isreduce) {
+        if (qh_reducevertices(qh)) {
+          qh_getmergeset(qh, qh->newfacet_list); /* facet_mergeset */
+          continue;
+        }
+      }
+    }
+    if (vneighbors && qh_test_vneighbors(qh /* qh->newfacet_list */))
+      continue;
+    break;
+  } /* while (True) */
+  if (qh->CHECKfrequently && !qh->MERGEexact) {
+    qh->old_randomdist= qh->RANDOMdist;
+    qh->RANDOMdist= False;
+    qh_checkconvex(qh, qh->newfacet_list, qh_ALGORITHMfault);
+    /* qh_checkconnect(qh); [this is slow and it changes the facet order] */
+    qh->RANDOMdist= qh->old_randomdist;
+  }
+  trace1((qh, qh->ferr, 1009, "qh_all_merges: merged %d coplanar facets %d concave facets and %d degen or redundant facets.\n",
+    numcoplanar, numconcave, numdegenredun));
+  if (qh->IStracing >= 4 && qh->num_facets < 50)
+    qh_printlists(qh);
+} /* all_merges */
+
+
+/*-<a                             href="qh-merge_r.htm#TOC"
+  >-------------------------------</a><a name="appendmergeset">-</a>
+
+  qh_appendmergeset(qh, facet, neighbor, mergetype, angle )
+    appends an entry to qh.facet_mergeset or qh.degen_mergeset
+
+    angle ignored if NULL or !qh.ANGLEmerge
+
+  returns:
+    merge appended to facet_mergeset or degen_mergeset
+      sets ->degenerate or ->redundant if degen_mergeset
+
+  see:
+    qh_test_appendmerge()
+
+  design:
+    allocate merge entry
+    if regular merge
+      append to qh.facet_mergeset
+    else if degenerate merge and qh.facet_mergeset is all degenerate
+      append to qh.degen_mergeset
+    else if degenerate merge
+      prepend to qh.degen_mergeset
+    else if redundant merge
+      append to qh.degen_mergeset
+*/
+void qh_appendmergeset(qhT *qh, facetT *facet, facetT *neighbor, mergeType mergetype, realT *angle) {
+  mergeT *merge, *lastmerge;
+  void **freelistp; /* used if !qh_NOmem by qh_memalloc_() */
+
+  if (facet->redundant)
+    return;
+  if (facet->degenerate && mergetype == MRGdegen)
+    return;
+  qh_memalloc_(qh, (int)sizeof(mergeT), freelistp, merge, mergeT);
+  merge->facet1= facet;
+  merge->facet2= neighbor;
+  merge->type= mergetype;
+  if (angle && qh->ANGLEmerge)
+    merge->angle= *angle;
+  if (mergetype < MRGdegen)
+    qh_setappend(qh, &(qh->facet_mergeset), merge);
+  else if (mergetype == MRGdegen) {
+    facet->degenerate= True;
+    if (!(lastmerge= (mergeT*)qh_setlast(qh->degen_mergeset))
+    || lastmerge->type == MRGdegen)
+      qh_setappend(qh, &(qh->degen_mergeset), merge);
+    else
+      qh_setaddnth(qh, &(qh->degen_mergeset), 0, merge);
+  }else if (mergetype == MRGredundant) {
+    facet->redundant= True;
+    qh_setappend(qh, &(qh->degen_mergeset), merge);
+  }else /* mergetype == MRGmirror */ {
+    if (facet->redundant || neighbor->redundant) {
+      qh_fprintf(qh, qh->ferr, 6092, "qhull error (qh_appendmergeset): facet f%d or f%d is already a mirrored facet\n",
+           facet->id, neighbor->id);
+      qh_errexit2(qh, qh_ERRqhull, facet, neighbor);
+    }
+    if (!qh_setequal(facet->vertices, neighbor->vertices)) {
+      qh_fprintf(qh, qh->ferr, 6093, "qhull error (qh_appendmergeset): mirrored facets f%d and f%d do not have the same vertices\n",
+           facet->id, neighbor->id);
+      qh_errexit2(qh, qh_ERRqhull, facet, neighbor);
+    }
+    facet->redundant= True;
+    neighbor->redundant= True;
+    qh_setappend(qh, &(qh->degen_mergeset), merge);
+  }
+} /* appendmergeset */
+
+
+/*-<a                             href="qh-merge_r.htm#TOC"
+  >-------------------------------</a><a name="basevertices">-</a>
+
+  qh_basevertices(qh, samecycle )
+    return temporary set of base vertices for samecycle
+    samecycle is first facet in the cycle
+    assumes apex is SETfirst_( samecycle->vertices )
+
+  returns:
+    vertices(settemp)
+    all ->seen are cleared
+
+  notes:
+    uses qh_vertex_visit;
+
+  design:
+    for each facet in samecycle
+      for each unseen vertex in facet->vertices
+        append to result
+*/
+setT *qh_basevertices(qhT *qh, facetT *samecycle) {
+  facetT *same;
+  vertexT *apex, *vertex, **vertexp;
+  setT *vertices= qh_settemp(qh, qh->TEMPsize);
+
+  apex= SETfirstt_(samecycle->vertices, vertexT);
+  apex->visitid= ++qh->vertex_visit;
+  FORALLsame_cycle_(samecycle) {
+    if (same->mergeridge)
+      continue;
+    FOREACHvertex_(same->vertices) {
+      if (vertex->visitid != qh->vertex_visit) {
+        qh_setappend(qh, &vertices, vertex);
+        vertex->visitid= qh->vertex_visit;
+        vertex->seen= False;
+      }
+    }
+  }
+  trace4((qh, qh->ferr, 4019, "qh_basevertices: found %d vertices\n",
+         qh_setsize(qh, vertices)));
+  return vertices;
+} /* basevertices */
+
+/*-<a                             href="qh-merge_r.htm#TOC"
+  >-------------------------------</a><a name="checkconnect">-</a>
+
+  qh_checkconnect(qh)
+    check that new facets are connected
+    new facets are on qh.newfacet_list
+
+  notes:
+    this is slow and it changes the order of the facets
+    uses qh.visit_id
+
+  design:
+    move first new facet to end of qh.facet_list
+    for all newly appended facets
+      append unvisited neighbors to end of qh.facet_list
+    for all new facets
+      report error if unvisited
+*/
+void qh_checkconnect(qhT *qh /* qh->newfacet_list */) {
+  facetT *facet, *newfacet, *errfacet= NULL, *neighbor, **neighborp;
+
+  facet= qh->newfacet_list;
+  qh_removefacet(qh, facet);
+  qh_appendfacet(qh, facet);
+  facet->visitid= ++qh->visit_id;
+  FORALLfacet_(facet) {
+    FOREACHneighbor_(facet) {
+      if (neighbor->visitid != qh->visit_id) {
+        qh_removefacet(qh, neighbor);
+        qh_appendfacet(qh, neighbor);
+        neighbor->visitid= qh->visit_id;
+      }
+    }
+  }
+  FORALLnew_facets {
+    if (newfacet->visitid == qh->visit_id)
+      break;
+    qh_fprintf(qh, qh->ferr, 6094, "qhull error: f%d is not attached to the new facets\n",
+         newfacet->id);
+    errfacet= newfacet;
+  }
+  if (errfacet)
+    qh_errexit(qh, qh_ERRqhull, errfacet, NULL);
+} /* checkconnect */
+
+/*-<a                             href="qh-merge_r.htm#TOC"
+  >-------------------------------</a><a name="checkzero">-</a>
+
+  qh_checkzero(qh, testall )
+    check that facets are clearly convex for qh.DISTround with qh.MERGEexact
+
+    if testall,
+      test all facets for qh.MERGEexact post-merging
+    else
+      test qh.newfacet_list
+
+    if qh.MERGEexact,
+      allows coplanar ridges
+      skips convexity test while qh.ZEROall_ok
+
+  returns:
+    True if all facets !flipped, !dupridge, normal
+         if all horizon facets are simplicial
+         if all vertices are clearly below neighbor
+         if all opposite vertices of horizon are below
+    clears qh.ZEROall_ok if any problems or coplanar facets
+
+  notes:
+    uses qh.vertex_visit
+    horizon facets may define multiple new facets
+
+  design:
+    for all facets in qh.newfacet_list or qh.facet_list
+      check for flagged faults (flipped, etc.)
+    for all facets in qh.newfacet_list or qh.facet_list
+      for each neighbor of facet
+        skip horizon facets for qh.newfacet_list
+        test the opposite vertex
+      if qh.newfacet_list
+        test the other vertices in the facet's horizon facet
+*/
+boolT qh_checkzero(qhT *qh, boolT testall) {
+  facetT *facet, *neighbor, **neighborp;
+  facetT *horizon, *facetlist;
+  int neighbor_i;
+  vertexT *vertex, **vertexp;
+  realT dist;
+
+  if (testall)
+    facetlist= qh->facet_list;
+  else {
+    facetlist= qh->newfacet_list;
+    FORALLfacet_(facetlist) {
+      horizon= SETfirstt_(facet->neighbors, facetT);
+      if (!horizon->simplicial)
+        goto LABELproblem;
+      if (facet->flipped || facet->dupridge || !facet->normal)
+        goto LABELproblem;
+    }
+    if (qh->MERGEexact && qh->ZEROall_ok) {
+      trace2((qh, qh->ferr, 2011, "qh_checkzero: skip convexity check until first pre-merge\n"));
+      return True;
+    }
+  }
+  FORALLfacet_(facetlist) {
+    qh->vertex_visit++;
+    neighbor_i= 0;
+    horizon= NULL;
+    FOREACHneighbor_(facet) {
+      if (!neighbor_i && !testall) {
+        horizon= neighbor;
+        neighbor_i++;
+        continue; /* horizon facet tested in qh_findhorizon */
+      }
+      vertex= SETelemt_(facet->vertices, neighbor_i++, vertexT);
+      vertex->visitid= qh->vertex_visit;
+      zzinc_(Zdistzero);
+      qh_distplane(qh, vertex->point, neighbor, &dist);
+      if (dist >= -qh->DISTround) {
+        qh->ZEROall_ok= False;
+        if (!qh->MERGEexact || testall || dist > qh->DISTround)
+          goto LABELnonconvex;
+      }
+    }
+    if (!testall && horizon) {
+      FOREACHvertex_(horizon->vertices) {
+        if (vertex->visitid != qh->vertex_visit) {
+          zzinc_(Zdistzero);
+          qh_distplane(qh, vertex->point, facet, &dist);
+          if (dist >= -qh->DISTround) {
+            qh->ZEROall_ok= False;
+            if (!qh->MERGEexact || dist > qh->DISTround)
+              goto LABELnonconvex;
+          }
+          break;
+        }
+      }
+    }
+  }
+  trace2((qh, qh->ferr, 2012, "qh_checkzero: testall %d, facets are %s\n", testall,
+        (qh->MERGEexact && !testall) ?
+           "not concave, flipped, or duplicate ridged" : "clearly convex"));
+  return True;
+
+ LABELproblem:
+  qh->ZEROall_ok= False;
+  trace2((qh, qh->ferr, 2013, "qh_checkzero: facet f%d needs pre-merging\n",
+       facet->id));
+  return False;
+
+ LABELnonconvex:
+  trace2((qh, qh->ferr, 2014, "qh_checkzero: facet f%d and f%d are not clearly convex.  v%d dist %.2g\n",
+         facet->id, neighbor->id, vertex->id, dist));
+  return False;
+} /* checkzero */
+
+/*-<a                             href="qh-merge_r.htm#TOC"
+  >-------------------------------</a><a name="compareangle">-</a>
+
+  qh_compareangle(angle1, angle2 )
+    used by qsort() to order merges by angle
+*/
+int qh_compareangle(const void *p1, const void *p2) {
+  const mergeT *a= *((mergeT *const*)p1), *b= *((mergeT *const*)p2);
+
+  return((a->angle > b->angle) ? 1 : -1);
+} /* compareangle */
+
+/*-<a                             href="qh-merge_r.htm#TOC"
+  >-------------------------------</a><a name="comparemerge">-</a>
+
+  qh_comparemerge(merge1, merge2 )
+    used by qsort() to order merges
+*/
+int qh_comparemerge(const void *p1, const void *p2) {
+  const mergeT *a= *((mergeT *const*)p1), *b= *((mergeT *const*)p2);
+
+  return(a->type - b->type);
+} /* comparemerge */
+
+/*-<a                             href="qh-merge_r.htm#TOC"
+  >-------------------------------</a><a name="comparevisit">-</a>
+
+  qh_comparevisit(vertex1, vertex2 )
+    used by qsort() to order vertices by their visitid
+*/
+int qh_comparevisit(const void *p1, const void *p2) {
+  const vertexT *a= *((vertexT *const*)p1), *b= *((vertexT *const*)p2);
+
+  return(a->visitid - b->visitid);
+} /* comparevisit */
+
+/*-<a                             href="qh-merge_r.htm#TOC"
+  >-------------------------------</a><a name="copynonconvex">-</a>
+
+  qh_copynonconvex(qh, atridge )
+    set non-convex flag on other ridges (if any) between same neighbors
+
+  notes:
+    may be faster if use smaller ridge set
+
+  design:
+    for each ridge of atridge's top facet
+      if ridge shares the same neighbor
+        set nonconvex flag
+*/
+void qh_copynonconvex(qhT *qh, ridgeT *atridge) {
+  facetT *facet, *otherfacet;
+  ridgeT *ridge, **ridgep;
+
+  facet= atridge->top;
+  otherfacet= atridge->bottom;
+  FOREACHridge_(facet->ridges) {
+    if (otherfacet == otherfacet_(ridge, facet) && ridge != atridge) {
+      ridge->nonconvex= True;
+      trace4((qh, qh->ferr, 4020, "qh_copynonconvex: moved nonconvex flag from r%d to r%d\n",
+              atridge->id, ridge->id));
+      break;
+    }
+  }
+} /* copynonconvex */
+
+/*-<a                             href="qh-merge_r.htm#TOC"
+  >-------------------------------</a><a name="degen_redundant_facet">-</a>
+
+  qh_degen_redundant_facet(qh, facet )
+    check facet for degen. or redundancy
+
+  notes:
+    bumps vertex_visit
+    called if a facet was redundant but no longer is (qh_merge_degenredundant)
+    qh_appendmergeset() only appends first reference to facet (i.e., redundant)
+
+  see:
+    qh_degen_redundant_neighbors()
+
+  design:
+    test for redundant neighbor
+    test for degenerate facet
+*/
+void qh_degen_redundant_facet(qhT *qh, facetT *facet) {
+  vertexT *vertex, **vertexp;
+  facetT *neighbor, **neighborp;
+
+  trace4((qh, qh->ferr, 4021, "qh_degen_redundant_facet: test facet f%d for degen/redundant\n",
+          facet->id));
+  FOREACHneighbor_(facet) {
+    qh->vertex_visit++;
+    FOREACHvertex_(neighbor->vertices)
+      vertex->visitid= qh->vertex_visit;
+    FOREACHvertex_(facet->vertices) {
+      if (vertex->visitid != qh->vertex_visit)
+        break;
+    }
+    if (!vertex) {
+      qh_appendmergeset(qh, facet, neighbor, MRGredundant, NULL);
+      trace2((qh, qh->ferr, 2015, "qh_degen_redundant_facet: f%d is contained in f%d.  merge\n", facet->id, neighbor->id));
+      return;
+    }
+  }
+  if (qh_setsize(qh, facet->neighbors) < qh->hull_dim) {
+    qh_appendmergeset(qh, facet, facet, MRGdegen, NULL);
+    trace2((qh, qh->ferr, 2016, "qh_degen_redundant_neighbors: f%d is degenerate.\n", facet->id));
+  }
+} /* degen_redundant_facet */
+
+
+/*-<a                             href="qh-merge_r.htm#TOC"
+  >-------------------------------</a><a name="degen_redundant_neighbors">-</a>
+
+  qh_degen_redundant_neighbors(qh, facet, delfacet,  )
+    append degenerate and redundant neighbors to facet_mergeset
+    if delfacet,
+      only checks neighbors of both delfacet and facet
+    also checks current facet for degeneracy
+
+  notes:
+    bumps vertex_visit
+    called for each qh_mergefacet() and qh_mergecycle()
+    merge and statistics occur in merge_nonconvex
+    qh_appendmergeset() only appends first reference to facet (i.e., redundant)
+      it appends redundant facets after degenerate ones
+
+    a degenerate facet has fewer than hull_dim neighbors
+    a redundant facet's vertices is a subset of its neighbor's vertices
+    tests for redundant merges first (appendmergeset is nop for others)
+    in a merge, only needs to test neighbors of merged facet
+
+  see:
+    qh_merge_degenredundant() and qh_degen_redundant_facet()
+
+  design:
+    test for degenerate facet
+    test for redundant neighbor
+    test for degenerate neighbor
+*/
+void qh_degen_redundant_neighbors(qhT *qh, facetT *facet, facetT *delfacet) {
+  vertexT *vertex, **vertexp;
+  facetT *neighbor, **neighborp;
+  int size;
+
+  trace4((qh, qh->ferr, 4022, "qh_degen_redundant_neighbors: test neighbors of f%d with delfacet f%d\n",
+          facet->id, getid_(delfacet)));
+  if ((size= qh_setsize(qh, facet->neighbors)) < qh->hull_dim) {
+    qh_appendmergeset(qh, facet, facet, MRGdegen, NULL);
+    trace2((qh, qh->ferr, 2017, "qh_degen_redundant_neighbors: f%d is degenerate with %d neighbors.\n", facet->id, size));
+  }
+  if (!delfacet)
+    delfacet= facet;
+  qh->vertex_visit++;
+  FOREACHvertex_(facet->vertices)
+    vertex->visitid= qh->vertex_visit;
+  FOREACHneighbor_(delfacet) {
+    /* uses early out instead of checking vertex count */
+    if (neighbor == facet)
+      continue;
+    FOREACHvertex_(neighbor->vertices) {
+      if (vertex->visitid != qh->vertex_visit)
+        break;
+    }
+    if (!vertex) {
+      qh_appendmergeset(qh, neighbor, facet, MRGredundant, NULL);
+      trace2((qh, qh->ferr, 2018, "qh_degen_redundant_neighbors: f%d is contained in f%d.  merge\n", neighbor->id, facet->id));
+    }
+  }
+  FOREACHneighbor_(delfacet) {   /* redundant merges occur first */
+    if (neighbor == facet)
+      continue;
+    if ((size= qh_setsize(qh, neighbor->neighbors)) < qh->hull_dim) {
+      qh_appendmergeset(qh, neighbor, neighbor, MRGdegen, NULL);
+      trace2((qh, qh->ferr, 2019, "qh_degen_redundant_neighbors: f%d is degenerate with %d neighbors.  Neighbor of f%d.\n", neighbor->id, size, facet->id));
+    }
+  }
+} /* degen_redundant_neighbors */
+
+
+/*-<a                             href="qh-merge_r.htm#TOC"
+  >-------------------------------</a><a name="find_newvertex">-</a>
+
+  qh_find_newvertex(qh, oldvertex, vertices, ridges )
+    locate new vertex for renaming old vertex
+    vertices is a set of possible new vertices
+      vertices sorted by number of deleted ridges
+
+  returns:
+    newvertex or NULL
+      each ridge includes both vertex and oldvertex
+    vertices sorted by number of deleted ridges
+
+  notes:
+    modifies vertex->visitid
+    new vertex is in one of the ridges
+    renaming will not cause a duplicate ridge
+    renaming will minimize the number of deleted ridges
+    newvertex may not be adjacent in the dual (though unlikely)
+
+  design:
+    for each vertex in vertices
+      set vertex->visitid to number of references in ridges
+    remove unvisited vertices
+    set qh.vertex_visit above all possible values
+    sort vertices by number of references in ridges
+    add each ridge to qh.hash_table
+    for each vertex in vertices
+      look for a vertex that would not cause a duplicate ridge after a rename
+*/
+vertexT *qh_find_newvertex(qhT *qh, vertexT *oldvertex, setT *vertices, setT *ridges) {
+  vertexT *vertex, **vertexp;
+  setT *newridges;
+  ridgeT *ridge, **ridgep;
+  int size, hashsize;
+  int hash;
+
+#ifndef qh_NOtrace
+  if (qh->IStracing >= 4) {
+    qh_fprintf(qh, qh->ferr, 8063, "qh_find_newvertex: find new vertex for v%d from ",
+             oldvertex->id);
+    FOREACHvertex_(vertices)
+      qh_fprintf(qh, qh->ferr, 8064, "v%d ", vertex->id);
+    FOREACHridge_(ridges)
+      qh_fprintf(qh, qh->ferr, 8065, "r%d ", ridge->id);
+    qh_fprintf(qh, qh->ferr, 8066, "\n");
+  }
+#endif
+  FOREACHvertex_(vertices)
+    vertex->visitid= 0;
+  FOREACHridge_(ridges) {
+    FOREACHvertex_(ridge->vertices)
+      vertex->visitid++;
+  }
+  FOREACHvertex_(vertices) {
+    if (!vertex->visitid) {
+      qh_setdelnth(qh, vertices, SETindex_(vertices,vertex));
+      vertexp--; /* repeat since deleted this vertex */
+    }
+  }
+  qh->vertex_visit += (unsigned int)qh_setsize(qh, ridges);
+  if (!qh_setsize(qh, vertices)) {
+    trace4((qh, qh->ferr, 4023, "qh_find_newvertex: vertices not in ridges for v%d\n",
+            oldvertex->id));
+    return NULL;
+  }
+  qsort(SETaddr_(vertices, vertexT), (size_t)qh_setsize(qh, vertices),
+                sizeof(vertexT *), qh_comparevisit);
+  /* can now use qh->vertex_visit */
+  if (qh->PRINTstatistics) {
+    size= qh_setsize(qh, vertices);
+    zinc_(Zintersect);
+    zadd_(Zintersecttot, size);
+    zmax_(Zintersectmax, size);
+  }
+  hashsize= qh_newhashtable(qh, qh_setsize(qh, ridges));
+  FOREACHridge_(ridges)
+    qh_hashridge(qh, qh->hash_table, hashsize, ridge, oldvertex);
+  FOREACHvertex_(vertices) {
+    newridges= qh_vertexridges(qh, vertex);
+    FOREACHridge_(newridges) {
+      if (qh_hashridge_find(qh, qh->hash_table, hashsize, ridge, vertex, oldvertex, &hash)) {
+        zinc_(Zdupridge);
+        break;
+      }
+    }
+    qh_settempfree(qh, &newridges);
+    if (!ridge)
+      break;  /* found a rename */
+  }
+  if (vertex) {
+    /* counted in qh_renamevertex */
+    trace2((qh, qh->ferr, 2020, "qh_find_newvertex: found v%d for old v%d from %d vertices and %d ridges.\n",
+      vertex->id, oldvertex->id, qh_setsize(qh, vertices), qh_setsize(qh, ridges)));
+  }else {
+    zinc_(Zfindfail);
+    trace0((qh, qh->ferr, 14, "qh_find_newvertex: no vertex for renaming v%d(all duplicated ridges) during p%d\n",
+      oldvertex->id, qh->furthest_id));
+  }
+  qh_setfree(qh, &qh->hash_table);
+  return vertex;
+} /* find_newvertex */
+
+/*-<a                             href="qh-merge_r.htm#TOC"
+  >-------------------------------</a><a name="findbest_test">-</a>
+
+  qh_findbest_test(qh, testcentrum, facet, neighbor, bestfacet, dist, mindist, maxdist )
+    test neighbor of facet for qh_findbestneighbor()
+    if testcentrum,
+      tests centrum (assumes it is defined)
+    else
+      tests vertices
+
+  returns:
+    if a better facet (i.e., vertices/centrum of facet closer to neighbor)
+      updates bestfacet, dist, mindist, and maxdist
+*/
+void qh_findbest_test(qhT *qh, boolT testcentrum, facetT *facet, facetT *neighbor,
+      facetT **bestfacet, realT *distp, realT *mindistp, realT *maxdistp) {
+  realT dist, mindist, maxdist;
+
+  if (testcentrum) {
+    zzinc_(Zbestdist);
+    qh_distplane(qh, facet->center, neighbor, &dist);
+    dist *= qh->hull_dim; /* estimate furthest vertex */
+    if (dist < 0) {
+      maxdist= 0;
+      mindist= dist;
+      dist= -dist;
+    }else {
+      mindist= 0;
+      maxdist= dist;
+    }
+  }else
+    dist= qh_getdistance(qh, facet, neighbor, &mindist, &maxdist);
+  if (dist < *distp) {
+    *bestfacet= neighbor;
+    *mindistp= mindist;
+    *maxdistp= maxdist;
+    *distp= dist;
+  }
+} /* findbest_test */
+
+/*-<a                             href="qh-merge_r.htm#TOC"
+  >-------------------------------</a><a name="findbestneighbor">-</a>
+
+  qh_findbestneighbor(qh, facet, dist, mindist, maxdist )
+    finds best neighbor (least dist) of a facet for merging
+
+  returns:
+    returns min and max distances and their max absolute value
+
+  notes:
+    error if qh_ASvoronoi
+    avoids merging old into new
+    assumes ridge->nonconvex only set on one ridge between a pair of facets
+    could use an early out predicate but not worth it
+
+  design:
+    if a large facet
+      will test centrum
+    else
+      will test vertices
+    if a large facet
+      test nonconvex neighbors for best merge
+    else
+      test all neighbors for the best merge
+    if testing centrum
+      get distance information
+*/
+facetT *qh_findbestneighbor(qhT *qh, facetT *facet, realT *distp, realT *mindistp, realT *maxdistp) {
+  facetT *neighbor, **neighborp, *bestfacet= NULL;
+  ridgeT *ridge, **ridgep;
+  boolT nonconvex= True, testcentrum= False;
+  int size= qh_setsize(qh, facet->vertices);
+
+  if(qh->CENTERtype==qh_ASvoronoi){
+    qh_fprintf(qh, qh->ferr, 6272, "qhull error: cannot call qh_findbestneighor for f%d while qh.CENTERtype is qh_ASvoronoi\n", facet->id);
+    qh_errexit(qh, qh_ERRqhull, facet, NULL);
+  }
+  *distp= REALmax;
+  if (size > qh_BESTcentrum2 * qh->hull_dim + qh_BESTcentrum) {
+    testcentrum= True;
+    zinc_(Zbestcentrum);
+    if (!facet->center)
+       facet->center= qh_getcentrum(qh, facet);
+  }
+  if (size > qh->hull_dim + qh_BESTnonconvex) {
+    FOREACHridge_(facet->ridges) {
+      if (ridge->nonconvex) {
+        neighbor= otherfacet_(ridge, facet);
+        qh_findbest_test(qh, testcentrum, facet, neighbor,
+                          &bestfacet, distp, mindistp, maxdistp);
+      }
+    }
+  }
+  if (!bestfacet) {
+    nonconvex= False;
+    FOREACHneighbor_(facet)
+      qh_findbest_test(qh, testcentrum, facet, neighbor,
+                        &bestfacet, distp, mindistp, maxdistp);
+  }
+  if (!bestfacet) {
+    qh_fprintf(qh, qh->ferr, 6095, "qhull internal error (qh_findbestneighbor): no neighbors for f%d\n", facet->id);
+    qh_errexit(qh, qh_ERRqhull, facet, NULL);
+  }
+  if (testcentrum)
+    qh_getdistance(qh, facet, bestfacet, mindistp, maxdistp);
+  trace3((qh, qh->ferr, 3002, "qh_findbestneighbor: f%d is best neighbor for f%d testcentrum? %d nonconvex? %d dist %2.2g min %2.2g max %2.2g\n",
+     bestfacet->id, facet->id, testcentrum, nonconvex, *distp, *mindistp, *maxdistp));
+  return(bestfacet);
+} /* findbestneighbor */
+
+
+/*-<a                             href="qh-merge_r.htm#TOC"
+  >-------------------------------</a><a name="flippedmerges">-</a>
+
+  qh_flippedmerges(qh, facetlist, wasmerge )
+    merge flipped facets into best neighbor
+    assumes qh.facet_mergeset at top of temporary stack
+
+  returns:
+    no flipped facets on facetlist
+    sets wasmerge if merge occurred
+    degen/redundant merges passed through
+
+  notes:
+    othermerges not needed since qh.facet_mergeset is empty before & after
+      keep it in case of change
+
+  design:
+    append flipped facets to qh.facetmergeset
+    for each flipped merge
+      find best neighbor
+      merge facet into neighbor
+      merge degenerate and redundant facets
+    remove flipped merges from qh.facet_mergeset
+*/
+void qh_flippedmerges(qhT *qh, facetT *facetlist, boolT *wasmerge) {
+  facetT *facet, *neighbor, *facet1;
+  realT dist, mindist, maxdist;
+  mergeT *merge, **mergep;
+  setT *othermerges;
+  int nummerge=0;
+
+  trace4((qh, qh->ferr, 4024, "qh_flippedmerges: begin\n"));
+  FORALLfacet_(facetlist) {
+    if (facet->flipped && !facet->visible)
+      qh_appendmergeset(qh, facet, facet, MRGflip, NULL);
+  }
+  othermerges= qh_settemppop(qh); /* was facet_mergeset */
+  qh->facet_mergeset= qh_settemp(qh, qh->TEMPsize);
+  qh_settemppush(qh, othermerges);
+  FOREACHmerge_(othermerges) {
+    facet1= merge->facet1;
+    if (merge->type != MRGflip || facet1->visible)
+      continue;
+    if (qh->TRACEmerge-1 == zzval_(Ztotmerge))
+      qh->qhmem.IStracing= qh->IStracing= qh->TRACElevel;
+    neighbor= qh_findbestneighbor(qh, facet1, &dist, &mindist, &maxdist);
+    trace0((qh, qh->ferr, 15, "qh_flippedmerges: merge flipped f%d into f%d dist %2.2g during p%d\n",
+      facet1->id, neighbor->id, dist, qh->furthest_id));
+    qh_mergefacet(qh, facet1, neighbor, &mindist, &maxdist, !qh_MERGEapex);
+    nummerge++;
+    if (qh->PRINTstatistics) {
+      zinc_(Zflipped);
+      wadd_(Wflippedtot, dist);
+      wmax_(Wflippedmax, dist);
+    }
+    qh_merge_degenredundant(qh);
+  }
+  FOREACHmerge_(othermerges) {
+    if (merge->facet1->visible || merge->facet2->visible)
+      qh_memfree(qh, merge, (int)sizeof(mergeT));
+    else
+      qh_setappend(qh, &qh->facet_mergeset, merge);
+  }
+  qh_settempfree(qh, &othermerges);
+  if (nummerge)
+    *wasmerge= True;
+  trace1((qh, qh->ferr, 1010, "qh_flippedmerges: merged %d flipped facets into a good neighbor\n", nummerge));
+} /* flippedmerges */
+
+
+/*-<a                             href="qh-merge_r.htm#TOC"
+  >-------------------------------</a><a name="forcedmerges">-</a>
+
+  qh_forcedmerges(qh, wasmerge )
+    merge duplicated ridges
+
+  returns:
+    removes all duplicate ridges on facet_mergeset
+    wasmerge set if merge
+    qh.facet_mergeset may include non-forced merges(none for now)
+    qh.degen_mergeset includes degen/redun merges
+
+  notes:
+    duplicate ridges occur when the horizon is pinched,
+        i.e. a subridge occurs in more than two horizon ridges.
+     could rename vertices that pinch the horizon
+    assumes qh_merge_degenredundant() has not be called
+    othermerges isn't needed since facet_mergeset is empty afterwards
+      keep it in case of change
+
+  design:
+    for each duplicate ridge
+      find current facets by chasing f.replace links
+      check for wide merge due to duplicate ridge
+      determine best direction for facet
+      merge one facet into the other
+      remove duplicate ridges from qh.facet_mergeset
+*/
+void qh_forcedmerges(qhT *qh, boolT *wasmerge) {
+  facetT *facet1, *facet2;
+  mergeT *merge, **mergep;
+  realT dist1, dist2, mindist1, mindist2, maxdist1, maxdist2;
+  setT *othermerges;
+  int nummerge=0, numflip=0;
+
+  if (qh->TRACEmerge-1 == zzval_(Ztotmerge))
+    qh->qhmem.IStracing= qh->IStracing= qh->TRACElevel;
+  trace4((qh, qh->ferr, 4025, "qh_forcedmerges: begin\n"));
+  othermerges= qh_settemppop(qh); /* was facet_mergeset */
+  qh->facet_mergeset= qh_settemp(qh, qh->TEMPsize);
+  qh_settemppush(qh, othermerges);
+  FOREACHmerge_(othermerges) {
+    if (merge->type != MRGridge)
+        continue;
+    if (qh->TRACEmerge-1 == zzval_(Ztotmerge))
+        qh->qhmem.IStracing= qh->IStracing= qh->TRACElevel;
+    facet1= merge->facet1;
+    facet2= merge->facet2;
+    while (facet1->visible)      /* must exist, no qh_merge_degenredunant */
+      facet1= facet1->f.replace; /* previously merged facet */
+    while (facet2->visible)
+      facet2= facet2->f.replace; /* previously merged facet */
+    if (facet1 == facet2)
+      continue;
+    if (!qh_setin(facet2->neighbors, facet1)) {
+      qh_fprintf(qh, qh->ferr, 6096, "qhull internal error (qh_forcedmerges): f%d and f%d had a duplicate ridge but as f%d and f%d they are no longer neighbors\n",
+               merge->facet1->id, merge->facet2->id, facet1->id, facet2->id);
+      qh_errexit2(qh, qh_ERRqhull, facet1, facet2);
+    }
+    dist1= qh_getdistance(qh, facet1, facet2, &mindist1, &maxdist1);
+    dist2= qh_getdistance(qh, facet2, facet1, &mindist2, &maxdist2);
+    qh_check_dupridge(qh, facet1, dist1, facet2, dist2);
+    if (dist1 < dist2)
+      qh_mergefacet(qh, facet1, facet2, &mindist1, &maxdist1, !qh_MERGEapex);
+    else {
+      qh_mergefacet(qh, facet2, facet1, &mindist2, &maxdist2, !qh_MERGEapex);
+      dist1= dist2;
+      facet1= facet2;
+    }
+    if (facet1->flipped) {
+      zinc_(Zmergeflipdup);
+      numflip++;
+    }else
+      nummerge++;
+    if (qh->PRINTstatistics) {
+      zinc_(Zduplicate);
+      wadd_(Wduplicatetot, dist1);
+      wmax_(Wduplicatemax, dist1);
+    }
+  }
+  FOREACHmerge_(othermerges) {
+    if (merge->type == MRGridge)
+      qh_memfree(qh, merge, (int)sizeof(mergeT));
+    else
+      qh_setappend(qh, &qh->facet_mergeset, merge);
+  }
+  qh_settempfree(qh, &othermerges);
+  if (nummerge)
+    *wasmerge= True;
+  trace1((qh, qh->ferr, 1011, "qh_forcedmerges: merged %d facets and %d flipped facets across duplicated ridges\n",
+                nummerge, numflip));
+} /* forcedmerges */
+
+
+/*-<a                             href="qh-merge_r.htm#TOC"
+  >-------------------------------</a><a name="getmergeset">-</a>
+
+  qh_getmergeset(qh, facetlist )
+    determines nonconvex facets on facetlist
+    tests !tested ridges and nonconvex ridges of !tested facets
+
+  returns:
+    returns sorted qh.facet_mergeset of facet-neighbor pairs to be merged
+    all ridges tested
+
+  notes:
+    assumes no nonconvex ridges with both facets tested
+    uses facet->tested/ridge->tested to prevent duplicate tests
+    can not limit tests to modified ridges since the centrum changed
+    uses qh.visit_id
+
+  see:
+    qh_getmergeset_initial()
+
+  design:
+    for each facet on facetlist
+      for each ridge of facet
+        if untested ridge
+          test ridge for convexity
+          if non-convex
+            append ridge to qh.facet_mergeset
+    sort qh.facet_mergeset by angle
+*/
+void qh_getmergeset(qhT *qh, facetT *facetlist) {
+  facetT *facet, *neighbor, **neighborp;
+  ridgeT *ridge, **ridgep;
+  int nummerges;
+
+  nummerges= qh_setsize(qh, qh->facet_mergeset);
+  trace4((qh, qh->ferr, 4026, "qh_getmergeset: started.\n"));
+  qh->visit_id++;
+  FORALLfacet_(facetlist) {
+    if (facet->tested)
+      continue;
+    facet->visitid= qh->visit_id;
+    facet->tested= True;  /* must be non-simplicial due to merge */
+    FOREACHneighbor_(facet)
+      neighbor->seen= False;
+    FOREACHridge_(facet->ridges) {
+      if (ridge->tested && !ridge->nonconvex)
+        continue;
+      /* if tested & nonconvex, need to append merge */
+      neighbor= otherfacet_(ridge, facet);
+      if (neighbor->seen) {
+        ridge->tested= True;
+        ridge->nonconvex= False;
+      }else if (neighbor->visitid != qh->visit_id) {
+        ridge->tested= True;
+        ridge->nonconvex= False;
+        neighbor->seen= True;      /* only one ridge is marked nonconvex */
+        if (qh_test_appendmerge(qh, facet, neighbor))
+          ridge->nonconvex= True;
+      }
+    }
+  }
+  nummerges= qh_setsize(qh, qh->facet_mergeset);
+  if (qh->ANGLEmerge)
+    qsort(SETaddr_(qh->facet_mergeset, mergeT), (size_t)nummerges, sizeof(mergeT *), qh_compareangle);
+  else
+    qsort(SETaddr_(qh->facet_mergeset, mergeT), (size_t)nummerges, sizeof(mergeT *), qh_comparemerge);
+  if (qh->POSTmerging) {
+    zadd_(Zmergesettot2, nummerges);
+  }else {
+    zadd_(Zmergesettot, nummerges);
+    zmax_(Zmergesetmax, nummerges);
+  }
+  trace2((qh, qh->ferr, 2021, "qh_getmergeset: %d merges found\n", nummerges));
+} /* getmergeset */
+
+
+/*-<a                             href="qh-merge_r.htm#TOC"
+  >-------------------------------</a><a name="getmergeset_initial">-</a>
+
+  qh_getmergeset_initial(qh, facetlist )
+    determine initial qh.facet_mergeset for facets
+    tests all facet/neighbor pairs on facetlist
+
+  returns:
+    sorted qh.facet_mergeset with nonconvex ridges
+    sets facet->tested, ridge->tested, and ridge->nonconvex
+
+  notes:
+    uses visit_id, assumes ridge->nonconvex is False
+
+  see:
+    qh_getmergeset()
+
+  design:
+    for each facet on facetlist
+      for each untested neighbor of facet
+        test facet and neighbor for convexity
+        if non-convex
+          append merge to qh.facet_mergeset
+          mark one of the ridges as nonconvex
+    sort qh.facet_mergeset by angle
+*/
+void qh_getmergeset_initial(qhT *qh, facetT *facetlist) {
+  facetT *facet, *neighbor, **neighborp;
+  ridgeT *ridge, **ridgep;
+  int nummerges;
+
+  qh->visit_id++;
+  FORALLfacet_(facetlist) {
+    facet->visitid= qh->visit_id;
+    facet->tested= True;
+    FOREACHneighbor_(facet) {
+      if (neighbor->visitid != qh->visit_id) {
+        if (qh_test_appendmerge(qh, facet, neighbor)) {
+          FOREACHridge_(neighbor->ridges) {
+            if (facet == otherfacet_(ridge, neighbor)) {
+              ridge->nonconvex= True;
+              break;    /* only one ridge is marked nonconvex */
+            }
+          }
+        }
+      }
+    }
+    FOREACHridge_(facet->ridges)
+      ridge->tested= True;
+  }
+  nummerges= qh_setsize(qh, qh->facet_mergeset);
+  if (qh->ANGLEmerge)
+    qsort(SETaddr_(qh->facet_mergeset, mergeT), (size_t)nummerges, sizeof(mergeT *), qh_compareangle);
+  else
+    qsort(SETaddr_(qh->facet_mergeset, mergeT), (size_t)nummerges, sizeof(mergeT *), qh_comparemerge);
+  if (qh->POSTmerging) {
+    zadd_(Zmergeinittot2, nummerges);
+  }else {
+    zadd_(Zmergeinittot, nummerges);
+    zmax_(Zmergeinitmax, nummerges);
+  }
+  trace2((qh, qh->ferr, 2022, "qh_getmergeset_initial: %d merges found\n", nummerges));
+} /* getmergeset_initial */
+
+
+/*-<a                             href="qh-merge_r.htm#TOC"
+  >-------------------------------</a><a name="hashridge">-</a>
+
+  qh_hashridge(qh, hashtable, hashsize, ridge, oldvertex )
+    add ridge to hashtable without oldvertex
+
+  notes:
+    assumes hashtable is large enough
+
+  design:
+    determine hash value for ridge without oldvertex
+    find next empty slot for ridge
+*/
+void qh_hashridge(qhT *qh, setT *hashtable, int hashsize, ridgeT *ridge, vertexT *oldvertex) {
+  int hash;
+  ridgeT *ridgeA;
+
+  hash= qh_gethash(qh, hashsize, ridge->vertices, qh->hull_dim-1, 0, oldvertex);
+  while (True) {
+    if (!(ridgeA= SETelemt_(hashtable, hash, ridgeT))) {
+      SETelem_(hashtable, hash)= ridge;
+      break;
+    }else if (ridgeA == ridge)
+      break;
+    if (++hash == hashsize)
+      hash= 0;
+  }
+} /* hashridge */
+
+
+/*-<a                             href="qh-merge_r.htm#TOC"
+  >-------------------------------</a><a name="hashridge_find">-</a>
+
+  qh_hashridge_find(qh, hashtable, hashsize, ridge, vertex, oldvertex, hashslot )
+    returns matching ridge without oldvertex in hashtable
+      for ridge without vertex
+    if oldvertex is NULL
+      matches with any one skip
+
+  returns:
+    matching ridge or NULL
+    if no match,
+      if ridge already in   table
+        hashslot= -1
+      else
+        hashslot= next NULL index
+
+  notes:
+    assumes hashtable is large enough
+    can't match ridge to itself
+
+  design:
+    get hash value for ridge without vertex
+    for each hashslot
+      return match if ridge matches ridgeA without oldvertex
+*/
+ridgeT *qh_hashridge_find(qhT *qh, setT *hashtable, int hashsize, ridgeT *ridge,
+              vertexT *vertex, vertexT *oldvertex, int *hashslot) {
+  int hash;
+  ridgeT *ridgeA;
+
+  *hashslot= 0;
+  zinc_(Zhashridge);
+  hash= qh_gethash(qh, hashsize, ridge->vertices, qh->hull_dim-1, 0, vertex);
+  while ((ridgeA= SETelemt_(hashtable, hash, ridgeT))) {
+    if (ridgeA == ridge)
+      *hashslot= -1;
+    else {
+      zinc_(Zhashridgetest);
+      if (qh_setequal_except(ridge->vertices, vertex, ridgeA->vertices, oldvertex))
+        return ridgeA;
+    }
+    if (++hash == hashsize)
+      hash= 0;
+  }
+  if (!*hashslot)
+    *hashslot= hash;
+  return NULL;
+} /* hashridge_find */
+
+
+/*-<a                             href="qh-merge_r.htm#TOC"
+  >-------------------------------</a><a name="makeridges">-</a>
+
+  qh_makeridges(qh, facet )
+    creates explicit ridges between simplicial facets
+
+  returns:
+    facet with ridges and without qh_MERGEridge
+    ->simplicial is False
+
+  notes:
+    allows qh_MERGEridge flag
+    uses existing ridges
+    duplicate neighbors ok if ridges already exist (qh_mergecycle_ridges)
+
+  see:
+    qh_mergecycle_ridges()
+
+  design:
+    look for qh_MERGEridge neighbors
+    mark neighbors that already have ridges
+    for each unprocessed neighbor of facet
+      create a ridge for neighbor and facet
+    if any qh_MERGEridge neighbors
+      delete qh_MERGEridge flags (already handled by qh_mark_dupridges)
+*/
+void qh_makeridges(qhT *qh, facetT *facet) {
+  facetT *neighbor, **neighborp;
+  ridgeT *ridge, **ridgep;
+  int neighbor_i, neighbor_n;
+  boolT toporient, mergeridge= False;
+
+  if (!facet->simplicial)
+    return;
+  trace4((qh, qh->ferr, 4027, "qh_makeridges: make ridges for f%d\n", facet->id));
+  facet->simplicial= False;
+  FOREACHneighbor_(facet) {
+    if (neighbor == qh_MERGEridge)
+      mergeridge= True;
+    else
+      neighbor->seen= False;
+  }
+  FOREACHridge_(facet->ridges)
+    otherfacet_(ridge, facet)->seen= True;
+  FOREACHneighbor_i_(qh, facet) {
+    if (neighbor == qh_MERGEridge)
+      continue;  /* fixed by qh_mark_dupridges */
+    else if (!neighbor->seen) {  /* no current ridges */
+      ridge= qh_newridge(qh);
+      ridge->vertices= qh_setnew_delnthsorted(qh, facet->vertices, qh->hull_dim,
+                                                          neighbor_i, 0);
+      toporient= facet->toporient ^ (neighbor_i & 0x1);
+      if (toporient) {
+        ridge->top= facet;
+        ridge->bottom= neighbor;
+      }else {
+        ridge->top= neighbor;
+        ridge->bottom= facet;
+      }
+#if 0 /* this also works */
+      flip= (facet->toporient ^ neighbor->toporient)^(skip1 & 0x1) ^ (skip2 & 0x1);
+      if (facet->toporient ^ (skip1 & 0x1) ^ flip) {
+        ridge->top= neighbor;
+        ridge->bottom= facet;
+      }else {
+        ridge->top= facet;
+        ridge->bottom= neighbor;
+      }
+#endif
+      qh_setappend(qh, &(facet->ridges), ridge);
+      qh_setappend(qh, &(neighbor->ridges), ridge);
+    }
+  }
+  if (mergeridge) {
+    while (qh_setdel(facet->neighbors, qh_MERGEridge))
+      ; /* delete each one */
+  }
+} /* makeridges */
+
+
+/*-<a                             href="qh-merge_r.htm#TOC"
+  >-------------------------------</a><a name="mark_dupridges">-</a>
+
+  qh_mark_dupridges(qh, facetlist )
+    add duplicated ridges to qh.facet_mergeset
+    facet->dupridge is true
+
+  returns:
+    duplicate ridges on qh.facet_mergeset
+    ->mergeridge/->mergeridge2 set
+    duplicate ridges marked by qh_MERGEridge and both sides facet->dupridge
+    no MERGEridges in neighbor sets
+
+  notes:
+    duplicate ridges occur when the horizon is pinched,
+        i.e. a subridge occurs in more than two horizon ridges.
+    could rename vertices that pinch the horizon (thus removing subridge)
+    uses qh.visit_id
+
+  design:
+    for all facets on facetlist
+      if facet contains a duplicate ridge
+        for each neighbor of facet
+          if neighbor marked qh_MERGEridge (one side of the merge)
+            set facet->mergeridge
+          else
+            if neighbor contains a duplicate ridge
+            and the back link is qh_MERGEridge
+              append duplicate ridge to qh.facet_mergeset
+   for each duplicate ridge
+     make ridge sets in preparation for merging
+     remove qh_MERGEridge from neighbor set
+   for each duplicate ridge
+     restore the missing neighbor from the neighbor set that was qh_MERGEridge
+     add the missing ridge for this neighbor
+*/
+void qh_mark_dupridges(qhT *qh, facetT *facetlist) {
+  facetT *facet, *neighbor, **neighborp;
+  int nummerge=0;
+  mergeT *merge, **mergep;
+
+
+  trace4((qh, qh->ferr, 4028, "qh_mark_dupridges: identify duplicate ridges\n"));
+  FORALLfacet_(facetlist) {
+    if (facet->dupridge) {
+      FOREACHneighbor_(facet) {
+        if (neighbor == qh_MERGEridge) {
+          facet->mergeridge= True;
+          continue;
+        }
+        if (neighbor->dupridge
+        && !qh_setin(neighbor->neighbors, facet)) { /* qh_MERGEridge */
+          qh_appendmergeset(qh, facet, neighbor, MRGridge, NULL);
+          facet->mergeridge2= True;
+          facet->mergeridge= True;
+          nummerge++;
+        }
+      }
+    }
+  }
+  if (!nummerge)
+    return;
+  FORALLfacet_(facetlist) {            /* gets rid of qh_MERGEridge */
+    if (facet->mergeridge && !facet->mergeridge2)
+      qh_makeridges(qh, facet);
+  }
+  FOREACHmerge_(qh->facet_mergeset) {   /* restore the missing neighbors */
+    if (merge->type == MRGridge) {
+      qh_setappend(qh, &merge->facet2->neighbors, merge->facet1);
+      qh_makeridges(qh, merge->facet1);   /* and the missing ridges */
+    }
+  }
+  trace1((qh, qh->ferr, 1012, "qh_mark_dupridges: found %d duplicated ridges\n",
+                nummerge));
+} /* mark_dupridges */
+
+/*-<a                             href="qh-merge_r.htm#TOC"
+  >-------------------------------</a><a name="maydropneighbor">-</a>
+
+  qh_maydropneighbor(qh, facet )
+    drop neighbor relationship if no ridge between facet and neighbor
+
+  returns:
+    neighbor sets updated
+    appends degenerate facets to qh.facet_mergeset
+
+  notes:
+    won't cause redundant facets since vertex inclusion is the same
+    may drop vertex and neighbor if no ridge
+    uses qh.visit_id
+
+  design:
+    visit all neighbors with ridges
+    for each unvisited neighbor of facet
+      delete neighbor and facet from the neighbor sets
+      if neighbor becomes degenerate
+        append neighbor to qh.degen_mergeset
+    if facet is degenerate
+      append facet to qh.degen_mergeset
+*/
+void qh_maydropneighbor(qhT *qh, facetT *facet) {
+  ridgeT *ridge, **ridgep;
+  realT angledegen= qh_ANGLEdegen;
+  facetT *neighbor, **neighborp;
+
+  qh->visit_id++;
+  trace4((qh, qh->ferr, 4029, "qh_maydropneighbor: test f%d for no ridges to a neighbor\n",
+          facet->id));
+  FOREACHridge_(facet->ridges) {
+    ridge->top->visitid= qh->visit_id;
+    ridge->bottom->visitid= qh->visit_id;
+  }
+  FOREACHneighbor_(facet) {
+    if (neighbor->visitid != qh->visit_id) {
+      trace0((qh, qh->ferr, 17, "qh_maydropneighbor: facets f%d and f%d are no longer neighbors during p%d\n",
+            facet->id, neighbor->id, qh->furthest_id));
+      zinc_(Zdropneighbor);
+      qh_setdel(facet->neighbors, neighbor);
+      neighborp--;  /* repeat, deleted a neighbor */
+      qh_setdel(neighbor->neighbors, facet);
+      if (qh_setsize(qh, neighbor->neighbors) < qh->hull_dim) {
+        zinc_(Zdropdegen);
+        qh_appendmergeset(qh, neighbor, neighbor, MRGdegen, &angledegen);
+        trace2((qh, qh->ferr, 2023, "qh_maydropneighbors: f%d is degenerate.\n", neighbor->id));
+      }
+    }
+  }
+  if (qh_setsize(qh, facet->neighbors) < qh->hull_dim) {
+    zinc_(Zdropdegen);
+    qh_appendmergeset(qh, facet, facet, MRGdegen, &angledegen);
+    trace2((qh, qh->ferr, 2024, "qh_maydropneighbors: f%d is degenerate.\n", facet->id));
+  }
+} /* maydropneighbor */
+
+
+/*-<a                             href="qh-merge_r.htm#TOC"
+  >-------------------------------</a><a name="merge_degenredundant">-</a>
+
+  qh_merge_degenredundant(qh)
+    merge all degenerate and redundant facets
+    qh.degen_mergeset contains merges from qh_degen_redundant_neighbors()
+
+  returns:
+    number of merges performed
+    resets facet->degenerate/redundant
+    if deleted (visible) facet has no neighbors
+      sets ->f.replace to NULL
+
+  notes:
+    redundant merges happen before degenerate ones
+    merging and renaming vertices can result in degen/redundant facets
+
+  design:
+    for each merge on qh.degen_mergeset
+      if redundant merge
+        if non-redundant facet merged into redundant facet
+          recheck facet for redundancy
+        else
+          merge redundant facet into other facet
+*/
+int qh_merge_degenredundant(qhT *qh) {
+  int size;
+  mergeT *merge;
+  facetT *bestneighbor, *facet1, *facet2;
+  realT dist, mindist, maxdist;
+  vertexT *vertex, **vertexp;
+  int nummerges= 0;
+  mergeType mergetype;
+
+  while ((merge= (mergeT*)qh_setdellast(qh->degen_mergeset))) {
+    facet1= merge->facet1;
+    facet2= merge->facet2;
+    mergetype= merge->type;
+    qh_memfree(qh, merge, (int)sizeof(mergeT));
+    if (facet1->visible)
+      continue;
+    facet1->degenerate= False;
+    facet1->redundant= False;
+    if (qh->TRACEmerge-1 == zzval_(Ztotmerge))
+      qh->qhmem.IStracing= qh->IStracing= qh->TRACElevel;
+    if (mergetype == MRGredundant) {
+      zinc_(Zneighbor);
+      while (facet2->visible) {
+        if (!facet2->f.replace) {
+          qh_fprintf(qh, qh->ferr, 6097, "qhull internal error (qh_merge_degenredunant): f%d redundant but f%d has no replacement\n",
+               facet1->id, facet2->id);
+          qh_errexit2(qh, qh_ERRqhull, facet1, facet2);
+        }
+        facet2= facet2->f.replace;
+      }
+      if (facet1 == facet2) {
+        qh_degen_redundant_facet(qh, facet1); /* in case of others */
+        continue;
+      }
+      trace2((qh, qh->ferr, 2025, "qh_merge_degenredundant: facet f%d is contained in f%d, will merge\n",
+            facet1->id, facet2->id));
+      qh_mergefacet(qh, facet1, facet2, NULL, NULL, !qh_MERGEapex);
+      /* merge distance is already accounted for */
+      nummerges++;
+    }else {  /* mergetype == MRGdegen, other merges may have fixed */
+      if (!(size= qh_setsize(qh, facet1->neighbors))) {
+        zinc_(Zdelfacetdup);
+        trace2((qh, qh->ferr, 2026, "qh_merge_degenredundant: facet f%d has no neighbors.  Deleted\n", facet1->id));
+        qh_willdelete(qh, facet1, NULL);
+        FOREACHvertex_(facet1->vertices) {
+          qh_setdel(vertex->neighbors, facet1);
+          if (!SETfirst_(vertex->neighbors)) {
+            zinc_(Zdegenvertex);
+            trace2((qh, qh->ferr, 2027, "qh_merge_degenredundant: deleted v%d because f%d has no neighbors\n",
+                 vertex->id, facet1->id));
+            vertex->deleted= True;
+            qh_setappend(qh, &qh->del_vertices, vertex);
+          }
+        }
+        nummerges++;
+      }else if (size < qh->hull_dim) {
+        bestneighbor= qh_findbestneighbor(qh, facet1, &dist, &mindist, &maxdist);
+        trace2((qh, qh->ferr, 2028, "qh_merge_degenredundant: facet f%d has %d neighbors, merge into f%d dist %2.2g\n",
+              facet1->id, size, bestneighbor->id, dist));
+        qh_mergefacet(qh, facet1, bestneighbor, &mindist, &maxdist, !qh_MERGEapex);
+        nummerges++;
+        if (qh->PRINTstatistics) {
+          zinc_(Zdegen);
+          wadd_(Wdegentot, dist);
+          wmax_(Wdegenmax, dist);
+        }
+      } /* else, another merge fixed the degeneracy and redundancy tested */
+    }
+  }
+  return nummerges;
+} /* merge_degenredundant */
+
+/*-<a                             href="qh-merge_r.htm#TOC"
+  >-------------------------------</a><a name="merge_nonconvex">-</a>
+
+  qh_merge_nonconvex(qh, facet1, facet2, mergetype )
+    remove non-convex ridge between facet1 into facet2
+    mergetype gives why the facet's are non-convex
+
+  returns:
+    merges one of the facets into the best neighbor
+
+  design:
+    if one of the facets is a new facet
+      prefer merging new facet into old facet
+    find best neighbors for both facets
+    merge the nearest facet into its best neighbor
+    update the statistics
+*/
+void qh_merge_nonconvex(qhT *qh, facetT *facet1, facetT *facet2, mergeType mergetype) {
+  facetT *bestfacet, *bestneighbor, *neighbor;
+  realT dist, dist2, mindist, mindist2, maxdist, maxdist2;
+
+  if (qh->TRACEmerge-1 == zzval_(Ztotmerge))
+    qh->qhmem.IStracing= qh->IStracing= qh->TRACElevel;
+  trace3((qh, qh->ferr, 3003, "qh_merge_nonconvex: merge #%d for f%d and f%d type %d\n",
+      zzval_(Ztotmerge) + 1, facet1->id, facet2->id, mergetype));
+  /* concave or coplanar */
+  if (!facet1->newfacet) {
+    bestfacet= facet2;   /* avoid merging old facet if new is ok */
+    facet2= facet1;
+    facet1= bestfacet;
+  }else
+    bestfacet= facet1;
+  bestneighbor= qh_findbestneighbor(qh, bestfacet, &dist, &mindist, &maxdist);
+  neighbor= qh_findbestneighbor(qh, facet2, &dist2, &mindist2, &maxdist2);
+  if (dist < dist2) {
+    qh_mergefacet(qh, bestfacet, bestneighbor, &mindist, &maxdist, !qh_MERGEapex);
+  }else if (qh->AVOIDold && !facet2->newfacet
+  && ((mindist >= -qh->MAXcoplanar && maxdist <= qh->max_outside)
+       || dist * 1.5 < dist2)) {
+    zinc_(Zavoidold);
+    wadd_(Wavoidoldtot, dist);
+    wmax_(Wavoidoldmax, dist);
+    trace2((qh, qh->ferr, 2029, "qh_merge_nonconvex: avoid merging old facet f%d dist %2.2g.  Use f%d dist %2.2g instead\n",
+           facet2->id, dist2, facet1->id, dist2));
+    qh_mergefacet(qh, bestfacet, bestneighbor, &mindist, &maxdist, !qh_MERGEapex);
+  }else {
+    qh_mergefacet(qh, facet2, neighbor, &mindist2, &maxdist2, !qh_MERGEapex);
+    dist= dist2;
+  }
+  if (qh->PRINTstatistics) {
+    if (mergetype == MRGanglecoplanar) {
+      zinc_(Zacoplanar);
+      wadd_(Wacoplanartot, dist);
+      wmax_(Wacoplanarmax, dist);
+    }else if (mergetype == MRGconcave) {
+      zinc_(Zconcave);
+      wadd_(Wconcavetot, dist);
+      wmax_(Wconcavemax, dist);
+    }else { /* MRGcoplanar */
+      zinc_(Zcoplanar);
+      wadd_(Wcoplanartot, dist);
+      wmax_(Wcoplanarmax, dist);
+    }
+  }
+} /* merge_nonconvex */
+
+/*-<a                             href="qh-merge_r.htm#TOC"
+  >-------------------------------</a><a name="mergecycle">-</a>
+
+  qh_mergecycle(qh, samecycle, newfacet )
+    merge a cycle of facets starting at samecycle into a newfacet
+    newfacet is a horizon facet with ->normal
+    samecycle facets are simplicial from an apex
+
+  returns:
+    initializes vertex neighbors on first merge
+    samecycle deleted (placed on qh.visible_list)
+    newfacet at end of qh.facet_list
+    deleted vertices on qh.del_vertices
+
+  see:
+    qh_mergefacet()
+    called by qh_mergecycle_all() for multiple, same cycle facets
+
+  design:
+    make vertex neighbors if necessary
+    make ridges for newfacet
+    merge neighbor sets of samecycle into newfacet
+    merge ridges of samecycle into newfacet
+    merge vertex neighbors of samecycle into newfacet
+    make apex of samecycle the apex of newfacet
+    if newfacet wasn't a new facet
+      add its vertices to qh.newvertex_list
+    delete samecycle facets a make newfacet a newfacet
+*/
+void qh_mergecycle(qhT *qh, facetT *samecycle, facetT *newfacet) {
+  int traceonce= False, tracerestore= 0;
+  vertexT *apex;
+#ifndef qh_NOtrace
+  facetT *same;
+#endif
+
+  if (newfacet->tricoplanar) {
+    if (!qh->TRInormals) {
+      qh_fprintf(qh, qh->ferr, 6224, "Qhull internal error (qh_mergecycle): does not work for tricoplanar facets.  Use option 'Q11'\n");
+      qh_errexit(qh, qh_ERRqhull, newfacet, NULL);
+    }
+    newfacet->tricoplanar= False;
+    newfacet->keepcentrum= False;
+  }
+  if (!qh->VERTEXneighbors)
+    qh_vertexneighbors(qh);
+  zzinc_(Ztotmerge);
+  if (qh->REPORTfreq2 && qh->POSTmerging) {
+    if (zzval_(Ztotmerge) > qh->mergereport + qh->REPORTfreq2)
+      qh_tracemerging(qh);
+  }
+#ifndef qh_NOtrace
+  if (qh->TRACEmerge == zzval_(Ztotmerge))
+    qh->qhmem.IStracing= qh->IStracing= qh->TRACElevel;
+  trace2((qh, qh->ferr, 2030, "qh_mergecycle: merge #%d for facets from cycle f%d into coplanar horizon f%d\n",
+        zzval_(Ztotmerge), samecycle->id, newfacet->id));
+  if (newfacet == qh->tracefacet) {
+    tracerestore= qh->IStracing;
+    qh->IStracing= 4;
+    qh_fprintf(qh, qh->ferr, 8068, "qh_mergecycle: ========= trace merge %d of samecycle %d into trace f%d, furthest is p%d\n",
+               zzval_(Ztotmerge), samecycle->id, newfacet->id,  qh->furthest_id);
+    traceonce= True;
+  }
+  if (qh->IStracing >=4) {
+    qh_fprintf(qh, qh->ferr, 8069, "  same cycle:");
+    FORALLsame_cycle_(samecycle)
+      qh_fprintf(qh, qh->ferr, 8070, " f%d", same->id);
+    qh_fprintf(qh, qh->ferr, 8071, "\n");
+  }
+  if (qh->IStracing >=4)
+    qh_errprint(qh, "MERGING CYCLE", samecycle, newfacet, NULL, NULL);
+#endif /* !qh_NOtrace */
+  apex= SETfirstt_(samecycle->vertices, vertexT);
+  qh_makeridges(qh, newfacet);
+  qh_mergecycle_neighbors(qh, samecycle, newfacet);
+  qh_mergecycle_ridges(qh, samecycle, newfacet);
+  qh_mergecycle_vneighbors(qh, samecycle, newfacet);
+  if (SETfirstt_(newfacet->vertices, vertexT) != apex)
+    qh_setaddnth(qh, &newfacet->vertices, 0, apex);  /* apex has last id */
+  if (!newfacet->newfacet)
+    qh_newvertices(qh, newfacet->vertices);
+  qh_mergecycle_facets(qh, samecycle, newfacet);
+  qh_tracemerge(qh, samecycle, newfacet);
+  /* check for degen_redundant_neighbors after qh_forcedmerges() */
+  if (traceonce) {
+    qh_fprintf(qh, qh->ferr, 8072, "qh_mergecycle: end of trace facet\n");
+    qh->IStracing= tracerestore;
+  }
+} /* mergecycle */
+
+/*-<a                             href="qh-merge_r.htm#TOC"
+  >-------------------------------</a><a name="mergecycle_all">-</a>
+
+  qh_mergecycle_all(qh, facetlist, wasmerge )
+    merge all samecycles of coplanar facets into horizon
+    don't merge facets with ->mergeridge (these already have ->normal)
+    all facets are simplicial from apex
+    all facet->cycledone == False
+
+  returns:
+    all newfacets merged into coplanar horizon facets
+    deleted vertices on  qh.del_vertices
+    sets wasmerge if any merge
+
+  see:
+    calls qh_mergecycle for multiple, same cycle facets
+
+  design:
+    for each facet on facetlist
+      skip facets with duplicate ridges and normals
+      check that facet is in a samecycle (->mergehorizon)
+      if facet only member of samecycle
+        sets vertex->delridge for all vertices except apex
+        merge facet into horizon
+      else
+        mark all facets in samecycle
+        remove facets with duplicate ridges from samecycle
+        merge samecycle into horizon (deletes facets from facetlist)
+*/
+void qh_mergecycle_all(qhT *qh, facetT *facetlist, boolT *wasmerge) {
+  facetT *facet, *same, *prev, *horizon;
+  facetT *samecycle= NULL, *nextfacet, *nextsame;
+  vertexT *apex, *vertex, **vertexp;
+  int cycles=0, total=0, facets, nummerge;
+
+  trace2((qh, qh->ferr, 2031, "qh_mergecycle_all: begin\n"));
+  for (facet= facetlist; facet && (nextfacet= facet->next); facet= nextfacet) {
+    if (facet->normal)
+      continue;
+    if (!facet->mergehorizon) {
+      qh_fprintf(qh, qh->ferr, 6225, "Qhull internal error (qh_mergecycle_all): f%d without normal\n", facet->id);
+      qh_errexit(qh, qh_ERRqhull, facet, NULL);
+    }
+    horizon= SETfirstt_(facet->neighbors, facetT);
+    if (facet->f.samecycle == facet) {
+      zinc_(Zonehorizon);
+      /* merge distance done in qh_findhorizon */
+      apex= SETfirstt_(facet->vertices, vertexT);
+      FOREACHvertex_(facet->vertices) {
+        if (vertex != apex)
+          vertex->delridge= True;
+      }
+      horizon->f.newcycle= NULL;
+      qh_mergefacet(qh, facet, horizon, NULL, NULL, qh_MERGEapex);
+    }else {
+      samecycle= facet;
+      facets= 0;
+      prev= facet;
+      for (same= facet->f.samecycle; same;  /* FORALLsame_cycle_(facet) */
+           same= (same == facet ? NULL :nextsame)) { /* ends at facet */
+        nextsame= same->f.samecycle;
+        if (same->cycledone || same->visible)
+          qh_infiniteloop(qh, same);
+        same->cycledone= True;
+        if (same->normal) {
+          prev->f.samecycle= same->f.samecycle; /* unlink ->mergeridge */
+          same->f.samecycle= NULL;
+        }else {
+          prev= same;
+          facets++;
+        }
+      }
+      while (nextfacet && nextfacet->cycledone)  /* will delete samecycle */
+        nextfacet= nextfacet->next;
+      horizon->f.newcycle= NULL;
+      qh_mergecycle(qh, samecycle, horizon);
+      nummerge= horizon->nummerge + facets;
+      if (nummerge > qh_MAXnummerge)
+        horizon->nummerge= qh_MAXnummerge;
+      else
+        horizon->nummerge= (short unsigned int)nummerge;
+      zzinc_(Zcyclehorizon);
+      total += facets;
+      zzadd_(Zcyclefacettot, facets);
+      zmax_(Zcyclefacetmax, facets);
+    }
+    cycles++;
+  }
+  if (cycles)
+    *wasmerge= True;
+  trace1((qh, qh->ferr, 1013, "qh_mergecycle_all: merged %d same cycles or facets into coplanar horizons\n", cycles));
+} /* mergecycle_all */
+
+/*-<a                             href="qh-merge_r.htm#TOC"
+  >-------------------------------</a><a name="mergecycle_facets">-</a>
+
+  qh_mergecycle_facets(qh, samecycle, newfacet )
+    finish merge of samecycle into newfacet
+
+  returns:
+    samecycle prepended to visible_list for later deletion and partitioning
+      each facet->f.replace == newfacet
+
+    newfacet moved to end of qh.facet_list
+      makes newfacet a newfacet (get's facet1->id if it was old)
+      sets newfacet->newmerge
+      clears newfacet->center (unless merging into a large facet)
+      clears newfacet->tested and ridge->tested for facet1
+
+    adds neighboring facets to facet_mergeset if redundant or degenerate
+
+  design:
+    make newfacet a new facet and set its flags
+    move samecycle facets to qh.visible_list for later deletion
+    unless newfacet is large
+      remove its centrum
+*/
+void qh_mergecycle_facets(qhT *qh, facetT *samecycle, facetT *newfacet) {
+  facetT *same, *next;
+
+  trace4((qh, qh->ferr, 4030, "qh_mergecycle_facets: make newfacet new and samecycle deleted\n"));
+  qh_removefacet(qh, newfacet);  /* append as a newfacet to end of qh->facet_list */
+  qh_appendfacet(qh, newfacet);
+  newfacet->newfacet= True;
+  newfacet->simplicial= False;
+  newfacet->newmerge= True;
+
+  for (same= samecycle->f.samecycle; same; same= (same == samecycle ?  NULL : next)) {
+    next= same->f.samecycle;  /* reused by willdelete */
+    qh_willdelete(qh, same, newfacet);
+  }
+  if (newfacet->center
+      && qh_setsize(qh, newfacet->vertices) <= qh->hull_dim + qh_MAXnewcentrum) {
+    qh_memfree(qh, newfacet->center, qh->normal_size);
+    newfacet->center= NULL;
+  }
+  trace3((qh, qh->ferr, 3004, "qh_mergecycle_facets: merged facets from cycle f%d into f%d\n",
+             samecycle->id, newfacet->id));
+} /* mergecycle_facets */
+
+/*-<a                             href="qh-merge_r.htm#TOC"
+  >-------------------------------</a><a name="mergecycle_neighbors">-</a>
+
+  qh_mergecycle_neighbors(qh, samecycle, newfacet )
+    add neighbors for samecycle facets to newfacet
+
+  returns:
+    newfacet with updated neighbors and vice-versa
+    newfacet has ridges
+    all neighbors of newfacet marked with qh.visit_id
+    samecycle facets marked with qh.visit_id-1
+    ridges updated for simplicial neighbors of samecycle with a ridge
+
+  notes:
+    assumes newfacet not in samecycle
+    usually, samecycle facets are new, simplicial facets without internal ridges
+      not so if horizon facet is coplanar to two different samecycles
+
+  see:
+    qh_mergeneighbors()
+
+  design:
+    check samecycle
+    delete neighbors from newfacet that are also in samecycle
+    for each neighbor of a facet in samecycle
+      if neighbor is simplicial
+        if first visit
+          move the neighbor relation to newfacet
+          update facet links for its ridges
+        else
+          make ridges for neighbor
+          remove samecycle reference
+      else
+        update neighbor sets
+*/
+void qh_mergecycle_neighbors(qhT *qh, facetT *samecycle, facetT *newfacet) {
+  facetT *same, *neighbor, **neighborp;
+  int delneighbors= 0, newneighbors= 0;
+  unsigned int samevisitid;
+  ridgeT *ridge, **ridgep;
+
+  samevisitid= ++qh->visit_id;
+  FORALLsame_cycle_(samecycle) {
+    if (same->visitid == samevisitid || same->visible)
+      qh_infiniteloop(qh, samecycle);
+    same->visitid= samevisitid;
+  }
+  newfacet->visitid= ++qh->visit_id;
+  trace4((qh, qh->ferr, 4031, "qh_mergecycle_neighbors: delete shared neighbors from newfacet\n"));
+  FOREACHneighbor_(newfacet) {
+    if (neighbor->visitid == samevisitid) {
+      SETref_(neighbor)= NULL;  /* samecycle neighbors deleted */
+      delneighbors++;
+    }else
+      neighbor->visitid= qh->visit_id;
+  }
+  qh_setcompact(qh, newfacet->neighbors);
+
+  trace4((qh, qh->ferr, 4032, "qh_mergecycle_neighbors: update neighbors\n"));
+  FORALLsame_cycle_(samecycle) {
+    FOREACHneighbor_(same) {
+      if (neighbor->visitid == samevisitid)
+        continue;
+      if (neighbor->simplicial) {
+        if (neighbor->visitid != qh->visit_id) {
+          qh_setappend(qh, &newfacet->neighbors, neighbor);
+          qh_setreplace(qh, neighbor->neighbors, same, newfacet);
+          newneighbors++;
+          neighbor->visitid= qh->visit_id;
+          FOREACHridge_(neighbor->ridges) { /* update ridge in case of qh_makeridges */
+            if (ridge->top == same) {
+              ridge->top= newfacet;
+              break;
+            }else if (ridge->bottom == same) {
+              ridge->bottom= newfacet;
+              break;
+            }
+          }
+        }else {
+          qh_makeridges(qh, neighbor);
+          qh_setdel(neighbor->neighbors, same);
+          /* same can't be horizon facet for neighbor */
+        }
+      }else { /* non-simplicial neighbor */
+        qh_setdel(neighbor->neighbors, same);
+        if (neighbor->visitid != qh->visit_id) {
+          qh_setappend(qh, &neighbor->neighbors, newfacet);
+          qh_setappend(qh, &newfacet->neighbors, neighbor);
+          neighbor->visitid= qh->visit_id;
+          newneighbors++;
+        }
+      }
+    }
+  }
+  trace2((qh, qh->ferr, 2032, "qh_mergecycle_neighbors: deleted %d neighbors and added %d\n",
+             delneighbors, newneighbors));
+} /* mergecycle_neighbors */
+
+/*-<a                             href="qh-merge_r.htm#TOC"
+  >-------------------------------</a><a name="mergecycle_ridges">-</a>
+
+  qh_mergecycle_ridges(qh, samecycle, newfacet )
+    add ridges/neighbors for facets in samecycle to newfacet
+    all new/old neighbors of newfacet marked with qh.visit_id
+    facets in samecycle marked with qh.visit_id-1
+    newfacet marked with qh.visit_id
+
+  returns:
+    newfacet has merged ridges
+
+  notes:
+    ridge already updated for simplicial neighbors of samecycle with a ridge
+
+  see:
+    qh_mergeridges()
+    qh_makeridges()
+
+  design:
+    remove ridges between newfacet and samecycle
+    for each facet in samecycle
+      for each ridge in facet
+        update facet pointers in ridge
+        skip ridges processed in qh_mergecycle_neighors
+        free ridges between newfacet and samecycle
+        free ridges between facets of samecycle (on 2nd visit)
+        append remaining ridges to newfacet
+      if simpilicial facet
+        for each neighbor of facet
+          if simplicial facet
+          and not samecycle facet or newfacet
+            make ridge between neighbor and newfacet
+*/
+void qh_mergecycle_ridges(qhT *qh, facetT *samecycle, facetT *newfacet) {
+  facetT *same, *neighbor= NULL;
+  int numold=0, numnew=0;
+  int neighbor_i, neighbor_n;
+  unsigned int samevisitid;
+  ridgeT *ridge, **ridgep;
+  boolT toporient;
+  void **freelistp; /* used if !qh_NOmem by qh_memfree_() */
+
+  trace4((qh, qh->ferr, 4033, "qh_mergecycle_ridges: delete shared ridges from newfacet\n"));
+  samevisitid= qh->visit_id -1;
+  FOREACHridge_(newfacet->ridges) {
+    neighbor= otherfacet_(ridge, newfacet);
+    if (neighbor->visitid == samevisitid)
+      SETref_(ridge)= NULL; /* ridge free'd below */
+  }
+  qh_setcompact(qh, newfacet->ridges);
+
+  trace4((qh, qh->ferr, 4034, "qh_mergecycle_ridges: add ridges to newfacet\n"));
+  FORALLsame_cycle_(samecycle) {
+    FOREACHridge_(same->ridges) {
+      if (ridge->top == same) {
+        ridge->top= newfacet;
+        neighbor= ridge->bottom;
+      }else if (ridge->bottom == same) {
+        ridge->bottom= newfacet;
+        neighbor= ridge->top;
+      }else if (ridge->top == newfacet || ridge->bottom == newfacet) {
+        qh_setappend(qh, &newfacet->ridges, ridge);
+        numold++;  /* already set by qh_mergecycle_neighbors */
+        continue;
+      }else {
+        qh_fprintf(qh, qh->ferr, 6098, "qhull internal error (qh_mergecycle_ridges): bad ridge r%d\n", ridge->id);
+        qh_errexit(qh, qh_ERRqhull, NULL, ridge);
+      }
+      if (neighbor == newfacet) {
+        qh_setfree(qh, &(ridge->vertices));
+        qh_memfree_(qh, ridge, (int)sizeof(ridgeT), freelistp);
+        numold++;
+      }else if (neighbor->visitid == samevisitid) {
+        qh_setdel(neighbor->ridges, ridge);
+        qh_setfree(qh, &(ridge->vertices));
+        qh_memfree_(qh, ridge, (int)sizeof(ridgeT), freelistp);
+        numold++;
+      }else {
+        qh_setappend(qh, &newfacet->ridges, ridge);
+        numold++;
+      }
+    }
+    if (same->ridges)
+      qh_settruncate(qh, same->ridges, 0);
+    if (!same->simplicial)
+      continue;
+    FOREACHneighbor_i_(qh, same) {       /* note: !newfact->simplicial */
+      if (neighbor->visitid != samevisitid && neighbor->simplicial) {
+        ridge= qh_newridge(qh);
+        ridge->vertices= qh_setnew_delnthsorted(qh, same->vertices, qh->hull_dim,
+                                                          neighbor_i, 0);
+        toporient= same->toporient ^ (neighbor_i & 0x1);
+        if (toporient) {
+          ridge->top= newfacet;
+          ridge->bottom= neighbor;
+        }else {
+          ridge->top= neighbor;
+          ridge->bottom= newfacet;
+        }
+        qh_setappend(qh, &(newfacet->ridges), ridge);
+        qh_setappend(qh, &(neighbor->ridges), ridge);
+        numnew++;
+      }
+    }
+  }
+
+  trace2((qh, qh->ferr, 2033, "qh_mergecycle_ridges: found %d old ridges and %d new ones\n",
+             numold, numnew));
+} /* mergecycle_ridges */
+
+/*-<a                             href="qh-merge_r.htm#TOC"
+  >-------------------------------</a><a name="mergecycle_vneighbors">-</a>
+
+  qh_mergecycle_vneighbors(qh, samecycle, newfacet )
+    create vertex neighbors for newfacet from vertices of facets in samecycle
+    samecycle marked with visitid == qh.visit_id - 1
+
+  returns:
+    newfacet vertices with updated neighbors
+    marks newfacet with qh.visit_id-1
+    deletes vertices that are merged away
+    sets delridge on all vertices (faster here than in mergecycle_ridges)
+
+  see:
+    qh_mergevertex_neighbors()
+
+  design:
+    for each vertex of samecycle facet
+      set vertex->delridge
+      delete samecycle facets from vertex neighbors
+      append newfacet to vertex neighbors
+      if vertex only in newfacet
+        delete it from newfacet
+        add it to qh.del_vertices for later deletion
+*/
+void qh_mergecycle_vneighbors(qhT *qh, facetT *samecycle, facetT *newfacet) {
+  facetT *neighbor, **neighborp;
+  unsigned int mergeid;
+  vertexT *vertex, **vertexp, *apex;
+  setT *vertices;
+
+  trace4((qh, qh->ferr, 4035, "qh_mergecycle_vneighbors: update vertex neighbors for newfacet\n"));
+  mergeid= qh->visit_id - 1;
+  newfacet->visitid= mergeid;
+  vertices= qh_basevertices(qh, samecycle); /* temp */
+  apex= SETfirstt_(samecycle->vertices, vertexT);
+  qh_setappend(qh, &vertices, apex);
+  FOREACHvertex_(vertices) {
+    vertex->delridge= True;
+    FOREACHneighbor_(vertex) {
+      if (neighbor->visitid == mergeid)
+        SETref_(neighbor)= NULL;
+    }
+    qh_setcompact(qh, vertex->neighbors);
+    qh_setappend(qh, &vertex->neighbors, newfacet);
+    if (!SETsecond_(vertex->neighbors)) {
+      zinc_(Zcyclevertex);
+      trace2((qh, qh->ferr, 2034, "qh_mergecycle_vneighbors: deleted v%d when merging cycle f%d into f%d\n",
+        vertex->id, samecycle->id, newfacet->id));
+      qh_setdelsorted(newfacet->vertices, vertex);
+      vertex->deleted= True;
+      qh_setappend(qh, &qh->del_vertices, vertex);
+    }
+  }
+  qh_settempfree(qh, &vertices);
+  trace3((qh, qh->ferr, 3005, "qh_mergecycle_vneighbors: merged vertices from cycle f%d into f%d\n",
+             samecycle->id, newfacet->id));
+} /* mergecycle_vneighbors */
+
+/*-<a                             href="qh-merge_r.htm#TOC"
+  >-------------------------------</a><a name="mergefacet">-</a>
+
+  qh_mergefacet(qh, facet1, facet2, mindist, maxdist, mergeapex )
+    merges facet1 into facet2
+    mergeapex==qh_MERGEapex if merging new facet into coplanar horizon
+
+  returns:
+    qh.max_outside and qh.min_vertex updated
+    initializes vertex neighbors on first merge
+
+  returns:
+    facet2 contains facet1's vertices, neighbors, and ridges
+      facet2 moved to end of qh.facet_list
+      makes facet2 a newfacet
+      sets facet2->newmerge set
+      clears facet2->center (unless merging into a large facet)
+      clears facet2->tested and ridge->tested for facet1
+
+    facet1 prepended to visible_list for later deletion and partitioning
+      facet1->f.replace == facet2
+
+    adds neighboring facets to facet_mergeset if redundant or degenerate
+
+  notes:
+    mindist/maxdist may be NULL (only if both NULL)
+    traces merge if fmax_(maxdist,-mindist) > TRACEdist
+
+  see:
+    qh_mergecycle()
+
+  design:
+    trace merge and check for degenerate simplex
+    make ridges for both facets
+    update qh.max_outside, qh.max_vertex, qh.min_vertex
+    update facet2->maxoutside and keepcentrum
+    update facet2->nummerge
+    update tested flags for facet2
+    if facet1 is simplicial
+      merge facet1 into facet2
+    else
+      merge facet1's neighbors into facet2
+      merge facet1's ridges into facet2
+      merge facet1's vertices into facet2
+      merge facet1's vertex neighbors into facet2
+      add facet2's vertices to qh.new_vertexlist
+      unless qh_MERGEapex
+        test facet2 for degenerate or redundant neighbors
+      move facet1 to qh.visible_list for later deletion
+      move facet2 to end of qh.newfacet_list
+*/
+void qh_mergefacet(qhT *qh, facetT *facet1, facetT *facet2, realT *mindist, realT *maxdist, boolT mergeapex) {
+  boolT traceonce= False;
+  vertexT *vertex, **vertexp;
+  int tracerestore=0, nummerge;
+
+  if (facet1->tricoplanar || facet2->tricoplanar) {
+    if (!qh->TRInormals) {
+      qh_fprintf(qh, qh->ferr, 6226, "Qhull internal error (qh_mergefacet): does not work for tricoplanar facets.  Use option 'Q11'\n");
+      qh_errexit2(qh, qh_ERRqhull, facet1, facet2);
+    }
+    if (facet2->tricoplanar) {
+      facet2->tricoplanar= False;
+      facet2->keepcentrum= False;
+    }
+  }
+  zzinc_(Ztotmerge);
+  if (qh->REPORTfreq2 && qh->POSTmerging) {
+    if (zzval_(Ztotmerge) > qh->mergereport + qh->REPORTfreq2)
+      qh_tracemerging(qh);
+  }
+#ifndef qh_NOtrace
+  if (qh->build_cnt >= qh->RERUN) {
+    if (mindist && (-*mindist > qh->TRACEdist || *maxdist > qh->TRACEdist)) {
+      tracerestore= 0;
+      qh->IStracing= qh->TRACElevel;
+      traceonce= True;
+      qh_fprintf(qh, qh->ferr, 8075, "qh_mergefacet: ========= trace wide merge #%d(%2.2g) for f%d into f%d, last point was p%d\n", zzval_(Ztotmerge),
+             fmax_(-*mindist, *maxdist), facet1->id, facet2->id, qh->furthest_id);
+    }else if (facet1 == qh->tracefacet || facet2 == qh->tracefacet) {
+      tracerestore= qh->IStracing;
+      qh->IStracing= 4;
+      traceonce= True;
+      qh_fprintf(qh, qh->ferr, 8076, "qh_mergefacet: ========= trace merge #%d involving f%d, furthest is p%d\n",
+                 zzval_(Ztotmerge), qh->tracefacet_id,  qh->furthest_id);
+    }
+  }
+  if (qh->IStracing >= 2) {
+    realT mergemin= -2;
+    realT mergemax= -2;
+
+    if (mindist) {
+      mergemin= *mindist;
+      mergemax= *maxdist;
+    }
+    qh_fprintf(qh, qh->ferr, 8077, "qh_mergefacet: #%d merge f%d into f%d, mindist= %2.2g, maxdist= %2.2g\n",
+    zzval_(Ztotmerge), facet1->id, facet2->id, mergemin, mergemax);
+  }
+#endif /* !qh_NOtrace */
+  if (facet1 == facet2 || facet1->visible || facet2->visible) {
+    qh_fprintf(qh, qh->ferr, 6099, "qhull internal error (qh_mergefacet): either f%d and f%d are the same or one is a visible facet\n",
+             facet1->id, facet2->id);
+    qh_errexit2(qh, qh_ERRqhull, facet1, facet2);
+  }
+  if (qh->num_facets - qh->num_visible <= qh->hull_dim + 1) {
+    qh_fprintf(qh, qh->ferr, 6227, "\n\
+qhull precision error: Only %d facets remain.  Can not merge another\n\
+pair.  The input is too degenerate or the convexity constraints are\n\
+too strong.\n", qh->hull_dim+1);
+    if (qh->hull_dim >= 5 && !qh->MERGEexact)
+      qh_fprintf(qh, qh->ferr, 8079, "Option 'Qx' may avoid this problem.\n");
+    qh_errexit(qh, qh_ERRprec, NULL, NULL);
+  }
+  if (!qh->VERTEXneighbors)
+    qh_vertexneighbors(qh);
+  qh_makeridges(qh, facet1);
+  qh_makeridges(qh, facet2);
+  if (qh->IStracing >=4)
+    qh_errprint(qh, "MERGING", facet1, facet2, NULL, NULL);
+  if (mindist) {
+    maximize_(qh->max_outside, *maxdist);
+    maximize_(qh->max_vertex, *maxdist);
+#if qh_MAXoutside
+    maximize_(facet2->maxoutside, *maxdist);
+#endif
+    minimize_(qh->min_vertex, *mindist);
+    if (!facet2->keepcentrum
+    && (*maxdist > qh->WIDEfacet || *mindist < -qh->WIDEfacet)) {
+      facet2->keepcentrum= True;
+      zinc_(Zwidefacet);
+    }
+  }
+  nummerge= facet1->nummerge + facet2->nummerge + 1;
+  if (nummerge >= qh_MAXnummerge)
+    facet2->nummerge= qh_MAXnummerge;
+  else
+    facet2->nummerge= (short unsigned int)nummerge;
+  facet2->newmerge= True;
+  facet2->dupridge= False;
+  qh_updatetested(qh, facet1, facet2);
+  if (qh->hull_dim > 2 && qh_setsize(qh, facet1->vertices) == qh->hull_dim)
+    qh_mergesimplex(qh, facet1, facet2, mergeapex);
+  else {
+    qh->vertex_visit++;
+    FOREACHvertex_(facet2->vertices)
+      vertex->visitid= qh->vertex_visit;
+    if (qh->hull_dim == 2)
+      qh_mergefacet2d(qh, facet1, facet2);
+    else {
+      qh_mergeneighbors(qh, facet1, facet2);
+      qh_mergevertices(qh, facet1->vertices, &facet2->vertices);
+    }
+    qh_mergeridges(qh, facet1, facet2);
+    qh_mergevertex_neighbors(qh, facet1, facet2);
+    if (!facet2->newfacet)
+      qh_newvertices(qh, facet2->vertices);
+  }
+  if (!mergeapex)
+    qh_degen_redundant_neighbors(qh, facet2, facet1);
+  if (facet2->coplanar || !facet2->newfacet) {
+    zinc_(Zmergeintohorizon);
+  }else if (!facet1->newfacet && facet2->newfacet) {
+    zinc_(Zmergehorizon);
+  }else {
+    zinc_(Zmergenew);
+  }
+  qh_willdelete(qh, facet1, facet2);
+  qh_removefacet(qh, facet2);  /* append as a newfacet to end of qh->facet_list */
+  qh_appendfacet(qh, facet2);
+  facet2->newfacet= True;
+  facet2->tested= False;
+  qh_tracemerge(qh, facet1, facet2);
+  if (traceonce) {
+    qh_fprintf(qh, qh->ferr, 8080, "qh_mergefacet: end of wide tracing\n");
+    qh->IStracing= tracerestore;
+  }
+} /* mergefacet */
+
+
+/*-<a                             href="qh-merge_r.htm#TOC"
+  >-------------------------------</a><a name="mergefacet2d">-</a>
+
+  qh_mergefacet2d(qh, facet1, facet2 )
+    in 2d, merges neighbors and vertices of facet1 into facet2
+
+  returns:
+    build ridges for neighbors if necessary
+    facet2 looks like a simplicial facet except for centrum, ridges
+      neighbors are opposite the corresponding vertex
+      maintains orientation of facet2
+
+  notes:
+    qh_mergefacet() retains non-simplicial structures
+      they are not needed in 2d, but later routines may use them
+    preserves qh.vertex_visit for qh_mergevertex_neighbors()
+
+  design:
+    get vertices and neighbors
+    determine new vertices and neighbors
+    set new vertices and neighbors and adjust orientation
+    make ridges for new neighbor if needed
+*/
+void qh_mergefacet2d(qhT *qh, facetT *facet1, facetT *facet2) {
+  vertexT *vertex1A, *vertex1B, *vertex2A, *vertex2B, *vertexA, *vertexB;
+  facetT *neighbor1A, *neighbor1B, *neighbor2A, *neighbor2B, *neighborA, *neighborB;
+
+  vertex1A= SETfirstt_(facet1->vertices, vertexT);
+  vertex1B= SETsecondt_(facet1->vertices, vertexT);
+  vertex2A= SETfirstt_(facet2->vertices, vertexT);
+  vertex2B= SETsecondt_(facet2->vertices, vertexT);
+  neighbor1A= SETfirstt_(facet1->neighbors, facetT);
+  neighbor1B= SETsecondt_(facet1->neighbors, facetT);
+  neighbor2A= SETfirstt_(facet2->neighbors, facetT);
+  neighbor2B= SETsecondt_(facet2->neighbors, facetT);
+  if (vertex1A == vertex2A) {
+    vertexA= vertex1B;
+    vertexB= vertex2B;
+    neighborA= neighbor2A;
+    neighborB= neighbor1A;
+  }else if (vertex1A == vertex2B) {
+    vertexA= vertex1B;
+    vertexB= vertex2A;
+    neighborA= neighbor2B;
+    neighborB= neighbor1A;
+  }else if (vertex1B == vertex2A) {
+    vertexA= vertex1A;
+    vertexB= vertex2B;
+    neighborA= neighbor2A;
+    neighborB= neighbor1B;
+  }else { /* 1B == 2B */
+    vertexA= vertex1A;
+    vertexB= vertex2A;
+    neighborA= neighbor2B;
+    neighborB= neighbor1B;
+  }
+  /* vertexB always from facet2, neighborB always from facet1 */
+  if (vertexA->id > vertexB->id) {
+    SETfirst_(facet2->vertices)= vertexA;
+    SETsecond_(facet2->vertices)= vertexB;
+    if (vertexB == vertex2A)
+      facet2->toporient= !facet2->toporient;
+    SETfirst_(facet2->neighbors)= neighborA;
+    SETsecond_(facet2->neighbors)= neighborB;
+  }else {
+    SETfirst_(facet2->vertices)= vertexB;
+    SETsecond_(facet2->vertices)= vertexA;
+    if (vertexB == vertex2B)
+      facet2->toporient= !facet2->toporient;
+    SETfirst_(facet2->neighbors)= neighborB;
+    SETsecond_(facet2->neighbors)= neighborA;
+  }
+  qh_makeridges(qh, neighborB);
+  qh_setreplace(qh, neighborB->neighbors, facet1, facet2);
+  trace4((qh, qh->ferr, 4036, "qh_mergefacet2d: merged v%d and neighbor f%d of f%d into f%d\n",
+       vertexA->id, neighborB->id, facet1->id, facet2->id));
+} /* mergefacet2d */
+
+
+/*-<a                             href="qh-merge_r.htm#TOC"
+  >-------------------------------</a><a name="mergeneighbors">-</a>
+
+  qh_mergeneighbors(qh, facet1, facet2 )
+    merges the neighbors of facet1 into facet2
+
+  see:
+    qh_mergecycle_neighbors()
+
+  design:
+    for each neighbor of facet1
+      if neighbor is also a neighbor of facet2
+        if neighbor is simpilicial
+          make ridges for later deletion as a degenerate facet
+        update its neighbor set
+      else
+        move the neighbor relation to facet2
+    remove the neighbor relation for facet1 and facet2
+*/
+void qh_mergeneighbors(qhT *qh, facetT *facet1, facetT *facet2) {
+  facetT *neighbor, **neighborp;
+
+  trace4((qh, qh->ferr, 4037, "qh_mergeneighbors: merge neighbors of f%d and f%d\n",
+          facet1->id, facet2->id));
+  qh->visit_id++;
+  FOREACHneighbor_(facet2) {
+    neighbor->visitid= qh->visit_id;
+  }
+  FOREACHneighbor_(facet1) {
+    if (neighbor->visitid == qh->visit_id) {
+      if (neighbor->simplicial)    /* is degen, needs ridges */
+        qh_makeridges(qh, neighbor);
+      if (SETfirstt_(neighbor->neighbors, facetT) != facet1) /*keep newfacet->horizon*/
+        qh_setdel(neighbor->neighbors, facet1);
+      else {
+        qh_setdel(neighbor->neighbors, facet2);
+        qh_setreplace(qh, neighbor->neighbors, facet1, facet2);
+      }
+    }else if (neighbor != facet2) {
+      qh_setappend(qh, &(facet2->neighbors), neighbor);
+      qh_setreplace(qh, neighbor->neighbors, facet1, facet2);
+    }
+  }
+  qh_setdel(facet1->neighbors, facet2);  /* here for makeridges */
+  qh_setdel(facet2->neighbors, facet1);
+} /* mergeneighbors */
+
+
+/*-<a                             href="qh-merge_r.htm#TOC"
+  >-------------------------------</a><a name="mergeridges">-</a>
+
+  qh_mergeridges(qh, facet1, facet2 )
+    merges the ridge set of facet1 into facet2
+
+  returns:
+    may delete all ridges for a vertex
+    sets vertex->delridge on deleted ridges
+
+  see:
+    qh_mergecycle_ridges()
+
+  design:
+    delete ridges between facet1 and facet2
+      mark (delridge) vertices on these ridges for later testing
+    for each remaining ridge
+      rename facet1 to facet2
+*/
+void qh_mergeridges(qhT *qh, facetT *facet1, facetT *facet2) {
+  ridgeT *ridge, **ridgep;
+  vertexT *vertex, **vertexp;
+
+  trace4((qh, qh->ferr, 4038, "qh_mergeridges: merge ridges of f%d and f%d\n",
+          facet1->id, facet2->id));
+  FOREACHridge_(facet2->ridges) {
+    if ((ridge->top == facet1) || (ridge->bottom == facet1)) {
+      FOREACHvertex_(ridge->vertices)
+        vertex->delridge= True;
+      qh_delridge(qh, ridge);  /* expensive in high-d, could rebuild */
+      ridgep--; /*repeat*/
+    }
+  }
+  FOREACHridge_(facet1->ridges) {
+    if (ridge->top == facet1)
+      ridge->top= facet2;
+    else
+      ridge->bottom= facet2;
+    qh_setappend(qh, &(facet2->ridges), ridge);
+  }
+} /* mergeridges */
+
+
+/*-<a                             href="qh-merge_r.htm#TOC"
+  >-------------------------------</a><a name="mergesimplex">-</a>
+
+  qh_mergesimplex(qh, facet1, facet2, mergeapex )
+    merge simplicial facet1 into facet2
+    mergeapex==qh_MERGEapex if merging samecycle into horizon facet
+      vertex id is latest (most recently created)
+    facet1 may be contained in facet2
+    ridges exist for both facets
+
+  returns:
+    facet2 with updated vertices, ridges, neighbors
+    updated neighbors for facet1's vertices
+    facet1 not deleted
+    sets vertex->delridge on deleted ridges
+
+  notes:
+    special case code since this is the most common merge
+    called from qh_mergefacet()
+
+  design:
+    if qh_MERGEapex
+      add vertices of facet2 to qh.new_vertexlist if necessary
+      add apex to facet2
+    else
+      for each ridge between facet1 and facet2
+        set vertex->delridge
+      determine the apex for facet1 (i.e., vertex to be merged)
+      unless apex already in facet2
+        insert apex into vertices for facet2
+      add vertices of facet2 to qh.new_vertexlist if necessary
+      add apex to qh.new_vertexlist if necessary
+      for each vertex of facet1
+        if apex
+          rename facet1 to facet2 in its vertex neighbors
+        else
+          delete facet1 from vertex neighors
+          if only in facet2
+            add vertex to qh.del_vertices for later deletion
+      for each ridge of facet1
+        delete ridges between facet1 and facet2
+        append other ridges to facet2 after renaming facet to facet2
+*/
+void qh_mergesimplex(qhT *qh, facetT *facet1, facetT *facet2, boolT mergeapex) {
+  vertexT *vertex, **vertexp, *apex;
+  ridgeT *ridge, **ridgep;
+  boolT issubset= False;
+  int vertex_i= -1, vertex_n;
+  facetT *neighbor, **neighborp, *otherfacet;
+
+  if (mergeapex) {
+    if (!facet2->newfacet)
+      qh_newvertices(qh, facet2->vertices);  /* apex is new */
+    apex= SETfirstt_(facet1->vertices, vertexT);
+    if (SETfirstt_(facet2->vertices, vertexT) != apex)
+      qh_setaddnth(qh, &facet2->vertices, 0, apex);  /* apex has last id */
+    else
+      issubset= True;
+  }else {
+    zinc_(Zmergesimplex);
+    FOREACHvertex_(facet1->vertices)
+      vertex->seen= False;
+    FOREACHridge_(facet1->ridges) {
+      if (otherfacet_(ridge, facet1) == facet2) {
+        FOREACHvertex_(ridge->vertices) {
+          vertex->seen= True;
+          vertex->delridge= True;
+        }
+        break;
+      }
+    }
+    FOREACHvertex_(facet1->vertices) {
+      if (!vertex->seen)
+        break;  /* must occur */
+    }
+    apex= vertex;
+    trace4((qh, qh->ferr, 4039, "qh_mergesimplex: merge apex v%d of f%d into facet f%d\n",
+          apex->id, facet1->id, facet2->id));
+    FOREACHvertex_i_(qh, facet2->vertices) {
+      if (vertex->id < apex->id) {
+        break;
+      }else if (vertex->id == apex->id) {
+        issubset= True;
+        break;
+      }
+    }
+    if (!issubset)
+      qh_setaddnth(qh, &facet2->vertices, vertex_i, apex);
+    if (!facet2->newfacet)
+      qh_newvertices(qh, facet2->vertices);
+    else if (!apex->newlist) {
+      qh_removevertex(qh, apex);
+      qh_appendvertex(qh, apex);
+    }
+  }
+  trace4((qh, qh->ferr, 4040, "qh_mergesimplex: update vertex neighbors of f%d\n",
+          facet1->id));
+  FOREACHvertex_(facet1->vertices) {
+    if (vertex == apex && !issubset)
+      qh_setreplace(qh, vertex->neighbors, facet1, facet2);
+    else {
+      qh_setdel(vertex->neighbors, facet1);
+      if (!SETsecond_(vertex->neighbors))
+        qh_mergevertex_del(qh, vertex, facet1, facet2);
+    }
+  }
+  trace4((qh, qh->ferr, 4041, "qh_mergesimplex: merge ridges and neighbors of f%d into f%d\n",
+          facet1->id, facet2->id));
+  qh->visit_id++;
+  FOREACHneighbor_(facet2)
+    neighbor->visitid= qh->visit_id;
+  FOREACHridge_(facet1->ridges) {
+    otherfacet= otherfacet_(ridge, facet1);
+    if (otherfacet == facet2) {
+      qh_setdel(facet2->ridges, ridge);
+      qh_setfree(qh, &(ridge->vertices));
+      qh_memfree(qh, ridge, (int)sizeof(ridgeT));
+      qh_setdel(facet2->neighbors, facet1);
+    }else {
+      qh_setappend(qh, &facet2->ridges, ridge);
+      if (otherfacet->visitid != qh->visit_id) {
+        qh_setappend(qh, &facet2->neighbors, otherfacet);
+        qh_setreplace(qh, otherfacet->neighbors, facet1, facet2);
+        otherfacet->visitid= qh->visit_id;
+      }else {
+        if (otherfacet->simplicial)    /* is degen, needs ridges */
+          qh_makeridges(qh, otherfacet);
+        if (SETfirstt_(otherfacet->neighbors, facetT) != facet1)
+          qh_setdel(otherfacet->neighbors, facet1);
+        else {   /*keep newfacet->neighbors->horizon*/
+          qh_setdel(otherfacet->neighbors, facet2);
+          qh_setreplace(qh, otherfacet->neighbors, facet1, facet2);
+        }
+      }
+      if (ridge->top == facet1) /* wait until after qh_makeridges */
+        ridge->top= facet2;
+      else
+        ridge->bottom= facet2;
+    }
+  }
+  SETfirst_(facet1->ridges)= NULL; /* it will be deleted */
+  trace3((qh, qh->ferr, 3006, "qh_mergesimplex: merged simplex f%d apex v%d into facet f%d\n",
+          facet1->id, getid_(apex), facet2->id));
+} /* mergesimplex */
+
+/*-<a                             href="qh-merge_r.htm#TOC"
+  >-------------------------------</a><a name="mergevertex_del">-</a>
+
+  qh_mergevertex_del(qh, vertex, facet1, facet2 )
+    delete a vertex because of merging facet1 into facet2
+
+  returns:
+    deletes vertex from facet2
+    adds vertex to qh.del_vertices for later deletion
+*/
+void qh_mergevertex_del(qhT *qh, vertexT *vertex, facetT *facet1, facetT *facet2) {
+
+  zinc_(Zmergevertex);
+  trace2((qh, qh->ferr, 2035, "qh_mergevertex_del: deleted v%d when merging f%d into f%d\n",
+          vertex->id, facet1->id, facet2->id));
+  qh_setdelsorted(facet2->vertices, vertex);
+  vertex->deleted= True;
+  qh_setappend(qh, &qh->del_vertices, vertex);
+} /* mergevertex_del */
+
+/*-<a                             href="qh-merge_r.htm#TOC"
+  >-------------------------------</a><a name="mergevertex_neighbors">-</a>
+
+  qh_mergevertex_neighbors(qh, facet1, facet2 )
+    merge the vertex neighbors of facet1 to facet2
+
+  returns:
+    if vertex is current qh.vertex_visit
+      deletes facet1 from vertex->neighbors
+    else
+      renames facet1 to facet2 in vertex->neighbors
+    deletes vertices if only one neighbor
+
+  notes:
+    assumes vertex neighbor sets are good
+*/
+void qh_mergevertex_neighbors(qhT *qh, facetT *facet1, facetT *facet2) {
+  vertexT *vertex, **vertexp;
+
+  trace4((qh, qh->ferr, 4042, "qh_mergevertex_neighbors: merge vertex neighbors of f%d and f%d\n",
+          facet1->id, facet2->id));
+  if (qh->tracevertex) {
+    qh_fprintf(qh, qh->ferr, 8081, "qh_mergevertex_neighbors: of f%d and f%d at furthest p%d f0= %p\n",
+             facet1->id, facet2->id, qh->furthest_id, qh->tracevertex->neighbors->e[0].p);
+    qh_errprint(qh, "TRACE", NULL, NULL, NULL, qh->tracevertex);
+  }
+  FOREACHvertex_(facet1->vertices) {
+    if (vertex->visitid != qh->vertex_visit)
+      qh_setreplace(qh, vertex->neighbors, facet1, facet2);
+    else {
+      qh_setdel(vertex->neighbors, facet1);
+      if (!SETsecond_(vertex->neighbors))
+        qh_mergevertex_del(qh, vertex, facet1, facet2);
+    }
+  }
+  if (qh->tracevertex)
+    qh_errprint(qh, "TRACE", NULL, NULL, NULL, qh->tracevertex);
+} /* mergevertex_neighbors */
+
+
+/*-<a                             href="qh-merge_r.htm#TOC"
+  >-------------------------------</a><a name="mergevertices">-</a>
+
+  qh_mergevertices(qh, vertices1, vertices2 )
+    merges the vertex set of facet1 into facet2
+
+  returns:
+    replaces vertices2 with merged set
+    preserves vertex_visit for qh_mergevertex_neighbors
+    updates qh.newvertex_list
+
+  design:
+    create a merged set of both vertices (in inverse id order)
+*/
+void qh_mergevertices(qhT *qh, setT *vertices1, setT **vertices2) {
+  int newsize= qh_setsize(qh, vertices1)+qh_setsize(qh, *vertices2) - qh->hull_dim + 1;
+  setT *mergedvertices;
+  vertexT *vertex, **vertexp, **vertex2= SETaddr_(*vertices2, vertexT);
+
+  mergedvertices= qh_settemp(qh, newsize);
+  FOREACHvertex_(vertices1) {
+    if (!*vertex2 || vertex->id > (*vertex2)->id)
+      qh_setappend(qh, &mergedvertices, vertex);
+    else {
+      while (*vertex2 && (*vertex2)->id > vertex->id)
+        qh_setappend(qh, &mergedvertices, *vertex2++);
+      if (!*vertex2 || (*vertex2)->id < vertex->id)
+        qh_setappend(qh, &mergedvertices, vertex);
+      else
+        qh_setappend(qh, &mergedvertices, *vertex2++);
+    }
+  }
+  while (*vertex2)
+    qh_setappend(qh, &mergedvertices, *vertex2++);
+  if (newsize < qh_setsize(qh, mergedvertices)) {
+    qh_fprintf(qh, qh->ferr, 6100, "qhull internal error (qh_mergevertices): facets did not share a ridge\n");
+    qh_errexit(qh, qh_ERRqhull, NULL, NULL);
+  }
+  qh_setfree(qh, vertices2);
+  *vertices2= mergedvertices;
+  qh_settemppop(qh);
+} /* mergevertices */
+
+
+/*-<a                             href="qh-merge_r.htm#TOC"
+  >-------------------------------</a><a name="neighbor_intersections">-</a>
+
+  qh_neighbor_intersections(qh, vertex )
+    return intersection of all vertices in vertex->neighbors except for vertex
+
+  returns:
+    returns temporary set of vertices
+    does not include vertex
+    NULL if a neighbor is simplicial
+    NULL if empty set
+
+  notes:
+    used for renaming vertices
+
+  design:
+    initialize the intersection set with vertices of the first two neighbors
+    delete vertex from the intersection
+    for each remaining neighbor
+      intersect its vertex set with the intersection set
+      return NULL if empty
+    return the intersection set
+*/
+setT *qh_neighbor_intersections(qhT *qh, vertexT *vertex) {
+  facetT *neighbor, **neighborp, *neighborA, *neighborB;
+  setT *intersect;
+  int neighbor_i, neighbor_n;
+
+  FOREACHneighbor_(vertex) {
+    if (neighbor->simplicial)
+      return NULL;
+  }
+  neighborA= SETfirstt_(vertex->neighbors, facetT);
+  neighborB= SETsecondt_(vertex->neighbors, facetT);
+  zinc_(Zintersectnum);
+  if (!neighborA)
+    return NULL;
+  if (!neighborB)
+    intersect= qh_setcopy(qh, neighborA->vertices, 0);
+  else
+    intersect= qh_vertexintersect_new(qh, neighborA->vertices, neighborB->vertices);
+  qh_settemppush(qh, intersect);
+  qh_setdelsorted(intersect, vertex);
+  FOREACHneighbor_i_(qh, vertex) {
+    if (neighbor_i >= 2) {
+      zinc_(Zintersectnum);
+      qh_vertexintersect(qh, &intersect, neighbor->vertices);
+      if (!SETfirst_(intersect)) {
+        zinc_(Zintersectfail);
+        qh_settempfree(qh, &intersect);
+        return NULL;
+      }
+    }
+  }
+  trace3((qh, qh->ferr, 3007, "qh_neighbor_intersections: %d vertices in neighbor intersection of v%d\n",
+          qh_setsize(qh, intersect), vertex->id));
+  return intersect;
+} /* neighbor_intersections */
+
+/*-<a                             href="qh-merge_r.htm#TOC"
+  >-------------------------------</a><a name="newvertices">-</a>
+
+  qh_newvertices(qh, vertices )
+    add vertices to end of qh.vertex_list (marks as new vertices)
+
+  returns:
+    vertices on qh.newvertex_list
+    vertex->newlist set
+*/
+void qh_newvertices(qhT *qh, setT *vertices) {
+  vertexT *vertex, **vertexp;
+
+  FOREACHvertex_(vertices) {
+    if (!vertex->newlist) {
+      qh_removevertex(qh, vertex);
+      qh_appendvertex(qh, vertex);
+    }
+  }
+} /* newvertices */
+
+/*-<a                             href="qh-merge_r.htm#TOC"
+  >-------------------------------</a><a name="reducevertices">-</a>
+
+  qh_reducevertices(qh)
+    reduce extra vertices, shared vertices, and redundant vertices
+    facet->newmerge is set if merged since last call
+    if !qh.MERGEvertices, only removes extra vertices
+
+  returns:
+    True if also merged degen_redundant facets
+    vertices are renamed if possible
+    clears facet->newmerge and vertex->delridge
+
+  notes:
+    ignored if 2-d
+
+  design:
+    merge any degenerate or redundant facets
+    for each newly merged facet
+      remove extra vertices
+    if qh.MERGEvertices
+      for each newly merged facet
+        for each vertex
+          if vertex was on a deleted ridge
+            rename vertex if it is shared
+      remove delridge flag from new vertices
+*/
+boolT qh_reducevertices(qhT *qh) {
+  int numshare=0, numrename= 0;
+  boolT degenredun= False;
+  facetT *newfacet;
+  vertexT *vertex, **vertexp;
+
+  if (qh->hull_dim == 2)
+    return False;
+  if (qh_merge_degenredundant(qh))
+    degenredun= True;
+ LABELrestart:
+  FORALLnew_facets {
+    if (newfacet->newmerge) {
+      if (!qh->MERGEvertices)
+        newfacet->newmerge= False;
+      qh_remove_extravertices(qh, newfacet);
+    }
+  }
+  if (!qh->MERGEvertices)
+    return False;
+  FORALLnew_facets {
+    if (newfacet->newmerge) {
+      newfacet->newmerge= False;
+      FOREACHvertex_(newfacet->vertices) {
+        if (vertex->delridge) {
+          if (qh_rename_sharedvertex(qh, vertex, newfacet)) {
+            numshare++;
+            vertexp--; /* repeat since deleted vertex */
+          }
+        }
+      }
+    }
+  }
+  FORALLvertex_(qh->newvertex_list) {
+    if (vertex->delridge && !vertex->deleted) {
+      vertex->delridge= False;
+      if (qh->hull_dim >= 4 && qh_redundant_vertex(qh, vertex)) {
+        numrename++;
+        if (qh_merge_degenredundant(qh)) {
+          degenredun= True;
+          goto LABELrestart;
+        }
+      }
+    }
+  }
+  trace1((qh, qh->ferr, 1014, "qh_reducevertices: renamed %d shared vertices and %d redundant vertices. Degen? %d\n",
+          numshare, numrename, degenredun));
+  return degenredun;
+} /* reducevertices */
+
+/*-<a                             href="qh-merge_r.htm#TOC"
+  >-------------------------------</a><a name="redundant_vertex">-</a>
+
+  qh_redundant_vertex(qh, vertex )
+    detect and rename a redundant vertex
+    vertices have full vertex->neighbors
+
+  returns:
+    returns true if find a redundant vertex
+      deletes vertex(vertex->deleted)
+
+  notes:
+    only needed if vertex->delridge and hull_dim >= 4
+    may add degenerate facets to qh.facet_mergeset
+    doesn't change vertex->neighbors or create redundant facets
+
+  design:
+    intersect vertices of all facet neighbors of vertex
+    determine ridges for these vertices
+    if find a new vertex for vertex amoung these ridges and vertices
+      rename vertex to the new vertex
+*/
+vertexT *qh_redundant_vertex(qhT *qh, vertexT *vertex) {
+  vertexT *newvertex= NULL;
+  setT *vertices, *ridges;
+
+  trace3((qh, qh->ferr, 3008, "qh_redundant_vertex: check if v%d can be renamed\n", vertex->id));
+  if ((vertices= qh_neighbor_intersections(qh, vertex))) {
+    ridges= qh_vertexridges(qh, vertex);
+    if ((newvertex= qh_find_newvertex(qh, vertex, vertices, ridges)))
+      qh_renamevertex(qh, vertex, newvertex, ridges, NULL, NULL);
+    qh_settempfree(qh, &ridges);
+    qh_settempfree(qh, &vertices);
+  }
+  return newvertex;
+} /* redundant_vertex */
+
+/*-<a                             href="qh-merge_r.htm#TOC"
+  >-------------------------------</a><a name="remove_extravertices">-</a>
+
+  qh_remove_extravertices(qh, facet )
+    remove extra vertices from non-simplicial facets
+
+  returns:
+    returns True if it finds them
+
+  design:
+    for each vertex in facet
+      if vertex not in a ridge (i.e., no longer used)
+        delete vertex from facet
+        delete facet from vertice's neighbors
+        unless vertex in another facet
+          add vertex to qh.del_vertices for later deletion
+*/
+boolT qh_remove_extravertices(qhT *qh, facetT *facet) {
+  ridgeT *ridge, **ridgep;
+  vertexT *vertex, **vertexp;
+  boolT foundrem= False;
+
+  trace4((qh, qh->ferr, 4043, "qh_remove_extravertices: test f%d for extra vertices\n",
+          facet->id));
+  FOREACHvertex_(facet->vertices)
+    vertex->seen= False;
+  FOREACHridge_(facet->ridges) {
+    FOREACHvertex_(ridge->vertices)
+      vertex->seen= True;
+  }
+  FOREACHvertex_(facet->vertices) {
+    if (!vertex->seen) {
+      foundrem= True;
+      zinc_(Zremvertex);
+      qh_setdelsorted(facet->vertices, vertex);
+      qh_setdel(vertex->neighbors, facet);
+      if (!qh_setsize(qh, vertex->neighbors)) {
+        vertex->deleted= True;
+        qh_setappend(qh, &qh->del_vertices, vertex);
+        zinc_(Zremvertexdel);
+        trace2((qh, qh->ferr, 2036, "qh_remove_extravertices: v%d deleted because it's lost all ridges\n", vertex->id));
+      }else
+        trace3((qh, qh->ferr, 3009, "qh_remove_extravertices: v%d removed from f%d because it's lost all ridges\n", vertex->id, facet->id));
+      vertexp--; /*repeat*/
+    }
+  }
+  return foundrem;
+} /* remove_extravertices */
+
+/*-<a                             href="qh-merge_r.htm#TOC"
+  >-------------------------------</a><a name="rename_sharedvertex">-</a>
+
+  qh_rename_sharedvertex(qh, vertex, facet )
+    detect and rename if shared vertex in facet
+    vertices have full ->neighbors
+
+  returns:
+    newvertex or NULL
+    the vertex may still exist in other facets (i.e., a neighbor was pinched)
+    does not change facet->neighbors
+    updates vertex->neighbors
+
+  notes:
+    a shared vertex for a facet is only in ridges to one neighbor
+    this may undo a pinched facet
+
+    it does not catch pinches involving multiple facets.  These appear
+      to be difficult to detect, since an exhaustive search is too expensive.
+
+  design:
+    if vertex only has two neighbors
+      determine the ridges that contain the vertex
+      determine the vertices shared by both neighbors
+      if can find a new vertex in this set
+        rename the vertex to the new vertex
+*/
+vertexT *qh_rename_sharedvertex(qhT *qh, vertexT *vertex, facetT *facet) {
+  facetT *neighbor, **neighborp, *neighborA= NULL;
+  setT *vertices, *ridges;
+  vertexT *newvertex;
+
+  if (qh_setsize(qh, vertex->neighbors) == 2) {
+    neighborA= SETfirstt_(vertex->neighbors, facetT);
+    if (neighborA == facet)
+      neighborA= SETsecondt_(vertex->neighbors, facetT);
+  }else if (qh->hull_dim == 3)
+    return NULL;
+  else {
+    qh->visit_id++;
+    FOREACHneighbor_(facet)
+      neighbor->visitid= qh->visit_id;
+    FOREACHneighbor_(vertex) {
+      if (neighbor->visitid == qh->visit_id) {
+        if (neighborA)
+          return NULL;
+        neighborA= neighbor;
+      }
+    }
+    if (!neighborA) {
+      qh_fprintf(qh, qh->ferr, 6101, "qhull internal error (qh_rename_sharedvertex): v%d's neighbors not in f%d\n",
+        vertex->id, facet->id);
+      qh_errprint(qh, "ERRONEOUS", facet, NULL, NULL, vertex);
+      qh_errexit(qh, qh_ERRqhull, NULL, NULL);
+    }
+  }
+  /* the vertex is shared by facet and neighborA */
+  ridges= qh_settemp(qh, qh->TEMPsize);
+  neighborA->visitid= ++qh->visit_id;
+  qh_vertexridges_facet(qh, vertex, facet, &ridges);
+  trace2((qh, qh->ferr, 2037, "qh_rename_sharedvertex: p%d(v%d) is shared by f%d(%d ridges) and f%d\n",
+    qh_pointid(qh, vertex->point), vertex->id, facet->id, qh_setsize(qh, ridges), neighborA->id));
+  zinc_(Zintersectnum);
+  vertices= qh_vertexintersect_new(qh, facet->vertices, neighborA->vertices);
+  qh_setdel(vertices, vertex);
+  qh_settemppush(qh, vertices);
+  if ((newvertex= qh_find_newvertex(qh, vertex, vertices, ridges)))
+    qh_renamevertex(qh, vertex, newvertex, ridges, facet, neighborA);
+  qh_settempfree(qh, &vertices);
+  qh_settempfree(qh, &ridges);
+  return newvertex;
+} /* rename_sharedvertex */
+
+/*-<a                             href="qh-merge_r.htm#TOC"
+  >-------------------------------</a><a name="renameridgevertex">-</a>
+
+  qh_renameridgevertex(qh, ridge, oldvertex, newvertex )
+    renames oldvertex as newvertex in ridge
+
+  returns:
+
+  design:
+    delete oldvertex from ridge
+    if newvertex already in ridge
+      copy ridge->noconvex to another ridge if possible
+      delete the ridge
+    else
+      insert newvertex into the ridge
+      adjust the ridge's orientation
+*/
+void qh_renameridgevertex(qhT *qh, ridgeT *ridge, vertexT *oldvertex, vertexT *newvertex) {
+  int nth= 0, oldnth;
+  facetT *temp;
+  vertexT *vertex, **vertexp;
+
+  oldnth= qh_setindex(ridge->vertices, oldvertex);
+  qh_setdelnthsorted(qh, ridge->vertices, oldnth);
+  FOREACHvertex_(ridge->vertices) {
+    if (vertex == newvertex) {
+      zinc_(Zdelridge);
+      if (ridge->nonconvex) /* only one ridge has nonconvex set */
+        qh_copynonconvex(qh, ridge);
+      trace2((qh, qh->ferr, 2038, "qh_renameridgevertex: ridge r%d deleted.  It contained both v%d and v%d\n",
+        ridge->id, oldvertex->id, newvertex->id));
+      qh_delridge(qh, ridge);
+      return;
+    }
+    if (vertex->id < newvertex->id)
+      break;
+    nth++;
+  }
+  qh_setaddnth(qh, &ridge->vertices, nth, newvertex);
+  if (abs(oldnth - nth)%2) {
+    trace3((qh, qh->ferr, 3010, "qh_renameridgevertex: swapped the top and bottom of ridge r%d\n",
+            ridge->id));
+    temp= ridge->top;
+    ridge->top= ridge->bottom;
+    ridge->bottom= temp;
+  }
+} /* renameridgevertex */
+
+
+/*-<a                             href="qh-merge_r.htm#TOC"
+  >-------------------------------</a><a name="renamevertex">-</a>
+
+  qh_renamevertex(qh, oldvertex, newvertex, ridges, oldfacet, neighborA )
+    renames oldvertex as newvertex in ridges
+    gives oldfacet/neighborA if oldvertex is shared between two facets
+
+  returns:
+    oldvertex may still exist afterwards
+
+
+  notes:
+    can not change neighbors of newvertex (since it's a subset)
+
+  design:
+    for each ridge in ridges
+      rename oldvertex to newvertex and delete degenerate ridges
+    if oldfacet not defined
+      for each neighbor of oldvertex
+        delete oldvertex from neighbor's vertices
+        remove extra vertices from neighbor
+      add oldvertex to qh.del_vertices
+    else if oldvertex only between oldfacet and neighborA
+      delete oldvertex from oldfacet and neighborA
+      add oldvertex to qh.del_vertices
+    else oldvertex is in oldfacet and neighborA and other facets (i.e., pinched)
+      delete oldvertex from oldfacet
+      delete oldfacet from oldvertice's neighbors
+      remove extra vertices (e.g., oldvertex) from neighborA
+*/
+void qh_renamevertex(qhT *qh, vertexT *oldvertex, vertexT *newvertex, setT *ridges, facetT *oldfacet, facetT *neighborA) {
+  facetT *neighbor, **neighborp;
+  ridgeT *ridge, **ridgep;
+  boolT istrace= False;
+
+  if (qh->IStracing >= 2 || oldvertex->id == qh->tracevertex_id ||
+        newvertex->id == qh->tracevertex_id)
+    istrace= True;
+  FOREACHridge_(ridges)
+    qh_renameridgevertex(qh, ridge, oldvertex, newvertex);
+  if (!oldfacet) {
+    zinc_(Zrenameall);
+    if (istrace)
+      qh_fprintf(qh, qh->ferr, 8082, "qh_renamevertex: renamed v%d to v%d in several facets\n",
+               oldvertex->id, newvertex->id);
+    FOREACHneighbor_(oldvertex) {
+      qh_maydropneighbor(qh, neighbor);
+      qh_setdelsorted(neighbor->vertices, oldvertex);
+      if (qh_remove_extravertices(qh, neighbor))
+        neighborp--; /* neighbor may be deleted */
+    }
+    if (!oldvertex->deleted) {
+      oldvertex->deleted= True;
+      qh_setappend(qh, &qh->del_vertices, oldvertex);
+    }
+  }else if (qh_setsize(qh, oldvertex->neighbors) == 2) {
+    zinc_(Zrenameshare);
+    if (istrace)
+      qh_fprintf(qh, qh->ferr, 8083, "qh_renamevertex: renamed v%d to v%d in oldfacet f%d\n",
+               oldvertex->id, newvertex->id, oldfacet->id);
+    FOREACHneighbor_(oldvertex)
+      qh_setdelsorted(neighbor->vertices, oldvertex);
+    oldvertex->deleted= True;
+    qh_setappend(qh, &qh->del_vertices, oldvertex);
+  }else {
+    zinc_(Zrenamepinch);
+    if (istrace || qh->IStracing)
+      qh_fprintf(qh, qh->ferr, 8084, "qh_renamevertex: renamed pinched v%d to v%d between f%d and f%d\n",
+               oldvertex->id, newvertex->id, oldfacet->id, neighborA->id);
+    qh_setdelsorted(oldfacet->vertices, oldvertex);
+    qh_setdel(oldvertex->neighbors, oldfacet);
+    qh_remove_extravertices(qh, neighborA);
+  }
+} /* renamevertex */
+
+
+/*-<a                             href="qh-merge_r.htm#TOC"
+  >-------------------------------</a><a name="test_appendmerge">-</a>
+
+  qh_test_appendmerge(qh, facet, neighbor )
+    tests facet/neighbor for convexity
+    appends to mergeset if non-convex
+    if pre-merging,
+      nop if qh.SKIPconvex, or qh.MERGEexact and coplanar
+
+  returns:
+    true if appends facet/neighbor to mergeset
+    sets facet->center as needed
+    does not change facet->seen
+
+  design:
+    if qh.cos_max is defined
+      if the angle between facet normals is too shallow
+        append an angle-coplanar merge to qh.mergeset
+        return True
+    make facet's centrum if needed
+    if facet's centrum is above the neighbor
+      set isconcave
+    else
+      if facet's centrum is not below the neighbor
+        set iscoplanar
+      make neighbor's centrum if needed
+      if neighbor's centrum is above the facet
+        set isconcave
+      else if neighbor's centrum is not below the facet
+        set iscoplanar
+   if isconcave or iscoplanar
+     get angle if needed
+     append concave or coplanar merge to qh.mergeset
+*/
+boolT qh_test_appendmerge(qhT *qh, facetT *facet, facetT *neighbor) {
+  realT dist, dist2= -REALmax, angle= -REALmax;
+  boolT isconcave= False, iscoplanar= False, okangle= False;
+
+  if (qh->SKIPconvex && !qh->POSTmerging)
+    return False;
+  if ((!qh->MERGEexact || qh->POSTmerging) && qh->cos_max < REALmax/2) {
+    angle= qh_getangle(qh, facet->normal, neighbor->normal);
+    zinc_(Zangletests);
+    if (angle > qh->cos_max) {
+      zinc_(Zcoplanarangle);
+      qh_appendmergeset(qh, facet, neighbor, MRGanglecoplanar, &angle);
+      trace2((qh, qh->ferr, 2039, "qh_test_appendmerge: coplanar angle %4.4g between f%d and f%d\n",
+         angle, facet->id, neighbor->id));
+      return True;
+    }else
+      okangle= True;
+  }
+  if (!facet->center)
+    facet->center= qh_getcentrum(qh, facet);
+  zzinc_(Zcentrumtests);
+  qh_distplane(qh, facet->center, neighbor, &dist);
+  if (dist > qh->centrum_radius)
+    isconcave= True;
+  else {
+    if (dist > -qh->centrum_radius)
+      iscoplanar= True;
+    if (!neighbor->center)
+      neighbor->center= qh_getcentrum(qh, neighbor);
+    zzinc_(Zcentrumtests);
+    qh_distplane(qh, neighbor->center, facet, &dist2);
+    if (dist2 > qh->centrum_radius)
+      isconcave= True;
+    else if (!iscoplanar && dist2 > -qh->centrum_radius)
+      iscoplanar= True;
+  }
+  if (!isconcave && (!iscoplanar || (qh->MERGEexact && !qh->POSTmerging)))
+    return False;
+  if (!okangle && qh->ANGLEmerge) {
+    angle= qh_getangle(qh, facet->normal, neighbor->normal);
+    zinc_(Zangletests);
+  }
+  if (isconcave) {
+    zinc_(Zconcaveridge);
+    if (qh->ANGLEmerge)
+      angle += qh_ANGLEconcave + 0.5;
+    qh_appendmergeset(qh, facet, neighbor, MRGconcave, &angle);
+    trace0((qh, qh->ferr, 18, "qh_test_appendmerge: concave f%d to f%d dist %4.4g and reverse dist %4.4g angle %4.4g during p%d\n",
+           facet->id, neighbor->id, dist, dist2, angle, qh->furthest_id));
+  }else /* iscoplanar */ {
+    zinc_(Zcoplanarcentrum);
+    qh_appendmergeset(qh, facet, neighbor, MRGcoplanar, &angle);
+    trace2((qh, qh->ferr, 2040, "qh_test_appendmerge: coplanar f%d to f%d dist %4.4g, reverse dist %4.4g angle %4.4g\n",
+              facet->id, neighbor->id, dist, dist2, angle));
+  }
+  return True;
+} /* test_appendmerge */
+
+/*-<a                             href="qh-merge_r.htm#TOC"
+  >-------------------------------</a><a name="test_vneighbors">-</a>
+
+  qh_test_vneighbors(qh)
+    test vertex neighbors for convexity
+    tests all facets on qh.newfacet_list
+
+  returns:
+    true if non-convex vneighbors appended to qh.facet_mergeset
+    initializes vertex neighbors if needed
+
+  notes:
+    assumes all facet neighbors have been tested
+    this can be expensive
+    this does not guarantee that a centrum is below all facets
+      but it is unlikely
+    uses qh.visit_id
+
+  design:
+    build vertex neighbors if necessary
+    for all new facets
+      for all vertices
+        for each unvisited facet neighbor of the vertex
+          test new facet and neighbor for convexity
+*/
+boolT qh_test_vneighbors(qhT *qh /* qh->newfacet_list */) {
+  facetT *newfacet, *neighbor, **neighborp;
+  vertexT *vertex, **vertexp;
+  int nummerges= 0;
+
+  trace1((qh, qh->ferr, 1015, "qh_test_vneighbors: testing vertex neighbors for convexity\n"));
+  if (!qh->VERTEXneighbors)
+    qh_vertexneighbors(qh);
+  FORALLnew_facets
+    newfacet->seen= False;
+  FORALLnew_facets {
+    newfacet->seen= True;
+    newfacet->visitid= qh->visit_id++;
+    FOREACHneighbor_(newfacet)
+      newfacet->visitid= qh->visit_id;
+    FOREACHvertex_(newfacet->vertices) {
+      FOREACHneighbor_(vertex) {
+        if (neighbor->seen || neighbor->visitid == qh->visit_id)
+          continue;
+        if (qh_test_appendmerge(qh, newfacet, neighbor))
+          nummerges++;
+      }
+    }
+  }
+  zadd_(Ztestvneighbor, nummerges);
+  trace1((qh, qh->ferr, 1016, "qh_test_vneighbors: found %d non-convex, vertex neighbors\n",
+           nummerges));
+  return (nummerges > 0);
+} /* test_vneighbors */
+
+/*-<a                             href="qh-merge_r.htm#TOC"
+  >-------------------------------</a><a name="tracemerge">-</a>
+
+  qh_tracemerge(qh, facet1, facet2 )
+    print trace message after merge
+*/
+void qh_tracemerge(qhT *qh, facetT *facet1, facetT *facet2) {
+  boolT waserror= False;
+
+#ifndef qh_NOtrace
+  if (qh->IStracing >= 4)
+    qh_errprint(qh, "MERGED", facet2, NULL, NULL, NULL);
+  if (facet2 == qh->tracefacet || (qh->tracevertex && qh->tracevertex->newlist)) {
+    qh_fprintf(qh, qh->ferr, 8085, "qh_tracemerge: trace facet and vertex after merge of f%d and f%d, furthest p%d\n", facet1->id, facet2->id, qh->furthest_id);
+    if (facet2 != qh->tracefacet)
+      qh_errprint(qh, "TRACE", qh->tracefacet,
+        (qh->tracevertex && qh->tracevertex->neighbors) ?
+           SETfirstt_(qh->tracevertex->neighbors, facetT) : NULL,
+        NULL, qh->tracevertex);
+  }
+  if (qh->tracevertex) {
+    if (qh->tracevertex->deleted)
+      qh_fprintf(qh, qh->ferr, 8086, "qh_tracemerge: trace vertex deleted at furthest p%d\n",
+            qh->furthest_id);
+    else
+      qh_checkvertex(qh, qh->tracevertex);
+  }
+  if (qh->tracefacet) {
+    qh_checkfacet(qh, qh->tracefacet, True, &waserror);
+    if (waserror)
+      qh_errexit(qh, qh_ERRqhull, qh->tracefacet, NULL);
+  }
+#endif /* !qh_NOtrace */
+  if (qh->CHECKfrequently || qh->IStracing >= 4) { /* can't check polygon here */
+    qh_checkfacet(qh, facet2, True, &waserror);
+    if (waserror)
+      qh_errexit(qh, qh_ERRqhull, NULL, NULL);
+  }
+} /* tracemerge */
+
+/*-<a                             href="qh-merge_r.htm#TOC"
+  >-------------------------------</a><a name="tracemerging">-</a>
+
+  qh_tracemerging(qh)
+    print trace message during POSTmerging
+
+  returns:
+    updates qh.mergereport
+
+  notes:
+    called from qh_mergecycle() and qh_mergefacet()
+
+  see:
+    qh_buildtracing()
+*/
+void qh_tracemerging(qhT *qh) {
+  realT cpu;
+  int total;
+  time_t timedata;
+  struct tm *tp;
+
+  qh->mergereport= zzval_(Ztotmerge);
+  time(&timedata);
+  tp= localtime(&timedata);
+  cpu= qh_CPUclock;
+  cpu /= qh_SECticks;
+  total= zzval_(Ztotmerge) - zzval_(Zcyclehorizon) + zzval_(Zcyclefacettot);
+  qh_fprintf(qh, qh->ferr, 8087, "\n\
+At %d:%d:%d & %2.5g CPU secs, qhull has merged %d facets.  The hull\n\
+  contains %d facets and %d vertices.\n",
+      tp->tm_hour, tp->tm_min, tp->tm_sec, cpu,
+      total, qh->num_facets - qh->num_visible,
+      qh->num_vertices-qh_setsize(qh, qh->del_vertices));
+} /* tracemerging */
+
+/*-<a                             href="qh-merge_r.htm#TOC"
+  >-------------------------------</a><a name="updatetested">-</a>
+
+  qh_updatetested(qh, facet1, facet2 )
+    clear facet2->tested and facet1->ridge->tested for merge
+
+  returns:
+    deletes facet2->center unless it's already large
+      if so, clears facet2->ridge->tested
+
+  design:
+    clear facet2->tested
+    clear ridge->tested for facet1's ridges
+    if facet2 has a centrum
+      if facet2 is large
+        set facet2->keepcentrum
+      else if facet2 has 3 vertices due to many merges, or not large and post merging
+        clear facet2->keepcentrum
+      unless facet2->keepcentrum
+        clear facet2->center to recompute centrum later
+        clear ridge->tested for facet2's ridges
+*/
+void qh_updatetested(qhT *qh, facetT *facet1, facetT *facet2) {
+  ridgeT *ridge, **ridgep;
+  int size;
+
+  facet2->tested= False;
+  FOREACHridge_(facet1->ridges)
+    ridge->tested= False;
+  if (!facet2->center)
+    return;
+  size= qh_setsize(qh, facet2->vertices);
+  if (!facet2->keepcentrum) {
+    if (size > qh->hull_dim + qh_MAXnewcentrum) {
+      facet2->keepcentrum= True;
+      zinc_(Zwidevertices);
+    }
+  }else if (size <= qh->hull_dim + qh_MAXnewcentrum) {
+    /* center and keepcentrum was set */
+    if (size == qh->hull_dim || qh->POSTmerging)
+      facet2->keepcentrum= False; /* if many merges need to recompute centrum */
+  }
+  if (!facet2->keepcentrum) {
+    qh_memfree(qh, facet2->center, qh->normal_size);
+    facet2->center= NULL;
+    FOREACHridge_(facet2->ridges)
+      ridge->tested= False;
+  }
+} /* updatetested */
+
+/*-<a                             href="qh-merge_r.htm#TOC"
+  >-------------------------------</a><a name="vertexridges">-</a>
+
+  qh_vertexridges(qh, vertex )
+    return temporary set of ridges adjacent to a vertex
+    vertex->neighbors defined
+
+  ntoes:
+    uses qh.visit_id
+    does not include implicit ridges for simplicial facets
+
+  design:
+    for each neighbor of vertex
+      add ridges that include the vertex to ridges
+*/
+setT *qh_vertexridges(qhT *qh, vertexT *vertex) {
+  facetT *neighbor, **neighborp;
+  setT *ridges= qh_settemp(qh, qh->TEMPsize);
+  int size;
+
+  qh->visit_id++;
+  FOREACHneighbor_(vertex)
+    neighbor->visitid= qh->visit_id;
+  FOREACHneighbor_(vertex) {
+    if (*neighborp)   /* no new ridges in last neighbor */
+      qh_vertexridges_facet(qh, vertex, neighbor, &ridges);
+  }
+  if (qh->PRINTstatistics || qh->IStracing) {
+    size= qh_setsize(qh, ridges);
+    zinc_(Zvertexridge);
+    zadd_(Zvertexridgetot, size);
+    zmax_(Zvertexridgemax, size);
+    trace3((qh, qh->ferr, 3011, "qh_vertexridges: found %d ridges for v%d\n",
+             size, vertex->id));
+  }
+  return ridges;
+} /* vertexridges */
+
+/*-<a                             href="qh-merge_r.htm#TOC"
+  >-------------------------------</a><a name="vertexridges_facet">-</a>
+
+  qh_vertexridges_facet(qh, vertex, facet, ridges )
+    add adjacent ridges for vertex in facet
+    neighbor->visitid==qh.visit_id if it hasn't been visited
+
+  returns:
+    ridges updated
+    sets facet->visitid to qh.visit_id-1
+
+  design:
+    for each ridge of facet
+      if ridge of visited neighbor (i.e., unprocessed)
+        if vertex in ridge
+          append ridge to vertex
+    mark facet processed
+*/
+void qh_vertexridges_facet(qhT *qh, vertexT *vertex, facetT *facet, setT **ridges) {
+  ridgeT *ridge, **ridgep;
+  facetT *neighbor;
+
+  FOREACHridge_(facet->ridges) {
+    neighbor= otherfacet_(ridge, facet);
+    if (neighbor->visitid == qh->visit_id
+    && qh_setin(ridge->vertices, vertex))
+      qh_setappend(qh, ridges, ridge);
+  }
+  facet->visitid= qh->visit_id-1;
+} /* vertexridges_facet */
+
+/*-<a                             href="qh-merge_r.htm#TOC"
+  >-------------------------------</a><a name="willdelete">-</a>
+
+  qh_willdelete(qh, facet, replace )
+    moves facet to visible list
+    sets facet->f.replace to replace (may be NULL)
+
+  returns:
+    bumps qh.num_visible
+*/
+void qh_willdelete(qhT *qh, facetT *facet, facetT *replace) {
+
+  qh_removefacet(qh, facet);
+  qh_prependfacet(qh, facet, &qh->visible_list);
+  qh->num_visible++;
+  facet->visible= True;
+  facet->f.replace= replace;
+} /* willdelete */
+
+#else /* qh_NOmerge */
+void qh_premerge(qhT *qh, vertexT *apex, realT maxcentrum, realT maxangle) {
+}
+void qh_postmerge(qhT *qh, const char *reason, realT maxcentrum, realT maxangle,
+                      boolT vneighbors) {
+}
+boolT qh_checkzero(qhT *qh, boolT testall) {
+   }
+#endif /* qh_NOmerge */
+
diff --git a/C/poly2_r.c b/C/poly2_r.c
new file mode 100644
--- /dev/null
+++ b/C/poly2_r.c
@@ -0,0 +1,3222 @@
+/*<html><pre>  -<a                             href="qh-poly_r.htm"
+  >-------------------------------</a><a name="TOP">-</a>
+
+   poly2_r.c
+   implements polygons and simplicies
+
+   see qh-poly_r.htm, poly_r.h and libqhull_r.h
+
+   frequently used code is in poly_r.c
+
+   Copyright (c) 1993-2015 The Geometry Center.
+   $Id: //main/2015/qhull/src/libqhull_r/poly2_r.c#10 $$Change: 2069 $
+   $DateTime: 2016/01/18 22:05:03 $$Author: bbarber $
+*/
+
+#include "qhull_ra.h"
+
+/*======== functions in alphabetical order ==========*/
+
+/*-<a                             href="qh-poly_r.htm#TOC"
+  >-------------------------------</a><a name="addhash">-</a>
+
+  qh_addhash( newelem, hashtable, hashsize, hash )
+    add newelem to linear hash table at hash if not already there
+*/
+void qh_addhash(void *newelem, setT *hashtable, int hashsize, int hash) {
+  int scan;
+  void *elem;
+
+  for (scan= (int)hash; (elem= SETelem_(hashtable, scan));
+       scan= (++scan >= hashsize ? 0 : scan)) {
+    if (elem == newelem)
+      break;
+  }
+  /* loop terminates because qh_HASHfactor >= 1.1 by qh_initbuffers */
+  if (!elem)
+    SETelem_(hashtable, scan)= newelem;
+} /* addhash */
+
+/*-<a                             href="qh-poly_r.htm#TOC"
+  >-------------------------------</a><a name="check_bestdist">-</a>
+
+  qh_check_bestdist(qh)
+    check that all points are within max_outside of the nearest facet
+    if qh.ONLYgood,
+      ignores !good facets
+
+  see:
+    qh_check_maxout(), qh_outerinner()
+
+  notes:
+    only called from qh_check_points()
+      seldom used since qh.MERGING is almost always set
+    if notverified>0 at end of routine
+      some points were well inside the hull.  If the hull contains
+      a lens-shaped component, these points were not verified.  Use
+      options 'Qi Tv' to verify all points.  (Exhaustive check also verifies)
+
+  design:
+    determine facet for each point (if any)
+    for each point
+      start with the assigned facet or with the first facet
+      find the best facet for the point and check all coplanar facets
+      error if point is outside of facet
+*/
+void qh_check_bestdist(qhT *qh) {
+  boolT waserror= False, unassigned;
+  facetT *facet, *bestfacet, *errfacet1= NULL, *errfacet2= NULL;
+  facetT *facetlist;
+  realT dist, maxoutside, maxdist= -REALmax;
+  pointT *point;
+  int numpart= 0, facet_i, facet_n, notgood= 0, notverified= 0;
+  setT *facets;
+
+  trace1((qh, qh->ferr, 1020, "qh_check_bestdist: check points below nearest facet.  Facet_list f%d\n",
+      qh->facet_list->id));
+  maxoutside= qh_maxouter(qh);
+  maxoutside += qh->DISTround;
+  /* one more qh.DISTround for check computation */
+  trace1((qh, qh->ferr, 1021, "qh_check_bestdist: check that all points are within %2.2g of best facet\n", maxoutside));
+  facets= qh_pointfacet(qh /*qh.facet_list*/);
+  if (!qh_QUICKhelp && qh->PRINTprecision)
+    qh_fprintf(qh, qh->ferr, 8091, "\n\
+qhull output completed.  Verifying that %d points are\n\
+below %2.2g of the nearest %sfacet.\n",
+             qh_setsize(qh, facets), maxoutside, (qh->ONLYgood ?  "good " : ""));
+  FOREACHfacet_i_(qh, facets) {  /* for each point with facet assignment */
+    if (facet)
+      unassigned= False;
+    else {
+      unassigned= True;
+      facet= qh->facet_list;
+    }
+    point= qh_point(qh, facet_i);
+    if (point == qh->GOODpointp)
+      continue;
+    qh_distplane(qh, point, facet, &dist);
+    numpart++;
+    bestfacet= qh_findbesthorizon(qh, !qh_IScheckmax, point, facet, qh_NOupper, &dist, &numpart);
+    /* occurs after statistics reported */
+    maximize_(maxdist, dist);
+    if (dist > maxoutside) {
+      if (qh->ONLYgood && !bestfacet->good
+          && !((bestfacet= qh_findgooddist(qh, point, bestfacet, &dist, &facetlist))
+               && dist > maxoutside))
+        notgood++;
+      else {
+        waserror= True;
+        qh_fprintf(qh, qh->ferr, 6109, "qhull precision error: point p%d is outside facet f%d, distance= %6.8g maxoutside= %6.8g\n",
+                facet_i, bestfacet->id, dist, maxoutside);
+        if (errfacet1 != bestfacet) {
+          errfacet2= errfacet1;
+          errfacet1= bestfacet;
+        }
+      }
+    }else if (unassigned && dist < -qh->MAXcoplanar)
+      notverified++;
+  }
+  qh_settempfree(qh, &facets);
+  if (notverified && !qh->DELAUNAY && !qh_QUICKhelp && qh->PRINTprecision)
+    qh_fprintf(qh, qh->ferr, 8092, "\n%d points were well inside the hull.  If the hull contains\n\
+a lens-shaped component, these points were not verified.  Use\n\
+options 'Qci Tv' to verify all points.\n", notverified);
+  if (maxdist > qh->outside_err) {
+    qh_fprintf(qh, qh->ferr, 6110, "qhull precision error (qh_check_bestdist): a coplanar point is %6.2g from convex hull.  The maximum value(qh.outside_err) is %6.2g\n",
+              maxdist, qh->outside_err);
+    qh_errexit2(qh, qh_ERRprec, errfacet1, errfacet2);
+  }else if (waserror && qh->outside_err > REALmax/2)
+    qh_errexit2(qh, qh_ERRprec, errfacet1, errfacet2);
+  /* else if waserror, the error was logged to qh.ferr but does not effect the output */
+  trace0((qh, qh->ferr, 20, "qh_check_bestdist: max distance outside %2.2g\n", maxdist));
+} /* check_bestdist */
+
+/*-<a                             href="qh-poly_r.htm#TOC"
+  >-------------------------------</a><a name="check_dupridge">-</a>
+
+  qh_check_dupridge(qh, facet1, dist1, facet2, dist2)
+    Check duplicate ridge between facet1 and facet2 for wide merge
+    dist1 is the maximum distance of facet1's vertices to facet2
+    dist2 is the maximum distance of facet2's vertices to facet1
+
+  Returns
+    Level 1 log of the duplicate ridge with the minimum distance between vertices
+    Throws error if the merge will increase the maximum facet width by qh_WIDEduplicate (100x)
+
+  called from:
+    qh_forcedmerges()
+*/
+#ifndef qh_NOmerge
+void qh_check_dupridge(qhT *qh, facetT *facet1, realT dist1, facetT *facet2, realT dist2) {
+  vertexT *vertex, **vertexp, *vertexA, **vertexAp;
+  realT dist, innerplane, mergedist, outerplane, prevdist, ratio;
+  realT minvertex= REALmax;
+
+  mergedist= fmin_(dist1, dist2);
+  qh_outerinner(qh, NULL, &outerplane, &innerplane);  /* ratio from qh_printsummary */
+  prevdist= fmax_(outerplane, innerplane);
+  maximize_(prevdist, qh->ONEmerge + qh->DISTround);
+  maximize_(prevdist, qh->MINoutside + qh->DISTround);
+  ratio= mergedist/prevdist;
+  FOREACHvertex_(facet1->vertices) {     /* The duplicate ridge is between facet1 and facet2, so either facet can be tested */
+    FOREACHvertexA_(facet1->vertices) {
+      if (vertex > vertexA){   /* Test each pair once */
+        dist= qh_pointdist(vertex->point, vertexA->point, qh->hull_dim);
+        minimize_(minvertex, dist);
+      }
+    }
+  }
+  trace0((qh, qh->ferr, 16, "qh_check_dupridge: duplicate ridge between f%d and f%d due to nearly-coincident vertices (%2.2g), dist %2.2g, reverse dist %2.2g, ratio %2.2g while processing p%d\n",
+        facet1->id, facet2->id, minvertex, dist1, dist2, ratio, qh->furthest_id));
+  if (ratio > qh_WIDEduplicate) {
+    qh_fprintf(qh, qh->ferr, 6271, "qhull precision error (qh_check_dupridge): wide merge (%.0f times wider) due to duplicate ridge with nearly coincident points (%2.2g) between f%d and f%d, merge dist %2.2g, while processing p%d\n- Ignore error with option 'Q12'\n- To be fixed in a later version of Qhull\n",
+          ratio, minvertex, facet1->id, facet2->id, mergedist, qh->furthest_id);
+    if (qh->DELAUNAY)
+      qh_fprintf(qh, qh->ferr, 8145, "- A bounding box for the input sites may alleviate this error.\n");
+    if(minvertex > qh_WIDEduplicate*prevdist)
+      qh_fprintf(qh, qh->ferr, 8146, "- Vertex distance %2.2g is greater than %d times maximum distance %2.2g\n  Please report to bradb@shore.net with steps to reproduce and all output\n",
+          minvertex, qh_WIDEduplicate, prevdist);
+    if (!qh->NOwide)
+      qh_errexit2(qh, qh_ERRqhull, facet1, facet2);
+  }
+} /* check_dupridge */
+#endif
+
+/*-<a                             href="qh-poly_r.htm#TOC"
+  >-------------------------------</a><a name="check_maxout">-</a>
+
+  qh_check_maxout(qh)
+    updates qh.max_outside by checking all points against bestfacet
+    if qh.ONLYgood, ignores !good facets
+
+  returns:
+    updates facet->maxoutside via qh_findbesthorizon()
+    sets qh.maxoutdone
+    if printing qh.min_vertex (qh_outerinner),
+      it is updated to the current vertices
+    removes inside/coplanar points from coplanarset as needed
+
+  notes:
+    defines coplanar as min_vertex instead of MAXcoplanar
+    may not need to check near-inside points because of qh.MAXcoplanar
+      and qh.KEEPnearinside (before it was -DISTround)
+
+  see also:
+    qh_check_bestdist()
+
+  design:
+    if qh.min_vertex is needed
+      for all neighbors of all vertices
+        test distance from vertex to neighbor
+    determine facet for each point (if any)
+    for each point with an assigned facet
+      find the best facet for the point and check all coplanar facets
+        (updates outer planes)
+    remove near-inside points from coplanar sets
+*/
+#ifndef qh_NOmerge
+void qh_check_maxout(qhT *qh) {
+  facetT *facet, *bestfacet, *neighbor, **neighborp, *facetlist;
+  realT dist, maxoutside, minvertex, old_maxoutside;
+  pointT *point;
+  int numpart= 0, facet_i, facet_n, notgood= 0;
+  setT *facets, *vertices;
+  vertexT *vertex;
+
+  trace1((qh, qh->ferr, 1022, "qh_check_maxout: check and update maxoutside for each facet.\n"));
+  maxoutside= minvertex= 0;
+  if (qh->VERTEXneighbors
+  && (qh->PRINTsummary || qh->KEEPinside || qh->KEEPcoplanar
+        || qh->TRACElevel || qh->PRINTstatistics
+        || qh->PRINTout[0] == qh_PRINTsummary || qh->PRINTout[0] == qh_PRINTnone)) {
+    trace1((qh, qh->ferr, 1023, "qh_check_maxout: determine actual maxoutside and minvertex\n"));
+    vertices= qh_pointvertex(qh /*qh.facet_list*/);
+    FORALLvertices {
+      FOREACHneighbor_(vertex) {
+        zinc_(Zdistvertex);  /* distance also computed by main loop below */
+        qh_distplane(qh, vertex->point, neighbor, &dist);
+        minimize_(minvertex, dist);
+        if (-dist > qh->TRACEdist || dist > qh->TRACEdist
+        || neighbor == qh->tracefacet || vertex == qh->tracevertex)
+          qh_fprintf(qh, qh->ferr, 8093, "qh_check_maxout: p%d(v%d) is %.2g from f%d\n",
+                    qh_pointid(qh, vertex->point), vertex->id, dist, neighbor->id);
+      }
+    }
+    if (qh->MERGING) {
+      wmin_(Wminvertex, qh->min_vertex);
+    }
+    qh->min_vertex= minvertex;
+    qh_settempfree(qh, &vertices);
+  }
+  facets= qh_pointfacet(qh /*qh.facet_list*/);
+  do {
+    old_maxoutside= fmax_(qh->max_outside, maxoutside);
+    FOREACHfacet_i_(qh, facets) {     /* for each point with facet assignment */
+      if (facet) {
+        point= qh_point(qh, facet_i);
+        if (point == qh->GOODpointp)
+          continue;
+        zzinc_(Ztotcheck);
+        qh_distplane(qh, point, facet, &dist);
+        numpart++;
+        bestfacet= qh_findbesthorizon(qh, qh_IScheckmax, point, facet, !qh_NOupper, &dist, &numpart);
+        if (bestfacet && dist > maxoutside) {
+          if (qh->ONLYgood && !bestfacet->good
+          && !((bestfacet= qh_findgooddist(qh, point, bestfacet, &dist, &facetlist))
+               && dist > maxoutside))
+            notgood++;
+          else
+            maxoutside= dist;
+        }
+        if (dist > qh->TRACEdist || (bestfacet && bestfacet == qh->tracefacet))
+          qh_fprintf(qh, qh->ferr, 8094, "qh_check_maxout: p%d is %.2g above f%d\n",
+          qh_pointid(qh, point), dist, (bestfacet ? bestfacet->id : UINT_MAX));
+      }
+    }
+  }while
+    (maxoutside > 2*old_maxoutside);
+    /* if qh.maxoutside increases substantially, qh_SEARCHdist is not valid
+          e.g., RBOX 5000 s Z1 G1e-13 t1001200614 | qhull */
+  zzadd_(Zcheckpart, numpart);
+  qh_settempfree(qh, &facets);
+  wval_(Wmaxout)= maxoutside - qh->max_outside;
+  wmax_(Wmaxoutside, qh->max_outside);
+  qh->max_outside= maxoutside;
+  qh_nearcoplanar(qh /*qh.facet_list*/);
+  qh->maxoutdone= True;
+  trace1((qh, qh->ferr, 1024, "qh_check_maxout: maxoutside %2.2g, min_vertex %2.2g, outside of not good %d\n",
+       maxoutside, qh->min_vertex, notgood));
+} /* check_maxout */
+#else /* qh_NOmerge */
+void qh_check_maxout(qhT *qh) {
+}
+#endif
+
+/*-<a                             href="qh-poly_r.htm#TOC"
+  >-------------------------------</a><a name="check_output">-</a>
+
+  qh_check_output(qh)
+    performs the checks at the end of qhull algorithm
+    Maybe called after voronoi output.  Will recompute otherwise centrums are Voronoi centers instead
+*/
+void qh_check_output(qhT *qh) {
+  int i;
+
+  if (qh->STOPcone)
+    return;
+  if (qh->VERIFYoutput | qh->IStracing | qh->CHECKfrequently) {
+    qh_checkpolygon(qh, qh->facet_list);
+    qh_checkflipped_all(qh, qh->facet_list);
+    qh_checkconvex(qh, qh->facet_list, qh_ALGORITHMfault);
+  }else if (!qh->MERGING && qh_newstats(qh, qh->qhstat.precision, &i)) {
+    qh_checkflipped_all(qh, qh->facet_list);
+    qh_checkconvex(qh, qh->facet_list, qh_ALGORITHMfault);
+  }
+} /* check_output */
+
+
+
+/*-<a                             href="qh-poly_r.htm#TOC"
+  >-------------------------------</a><a name="check_point">-</a>
+
+  qh_check_point(qh, point, facet, maxoutside, maxdist, errfacet1, errfacet2 )
+    check that point is less than maxoutside from facet
+*/
+void qh_check_point(qhT *qh, pointT *point, facetT *facet, realT *maxoutside, realT *maxdist, facetT **errfacet1, facetT **errfacet2) {
+  realT dist;
+
+  /* occurs after statistics reported */
+  qh_distplane(qh, point, facet, &dist);
+  if (dist > *maxoutside) {
+    if (*errfacet1 != facet) {
+      *errfacet2= *errfacet1;
+      *errfacet1= facet;
+    }
+    qh_fprintf(qh, qh->ferr, 6111, "qhull precision error: point p%d is outside facet f%d, distance= %6.8g maxoutside= %6.8g\n",
+              qh_pointid(qh, point), facet->id, dist, *maxoutside);
+  }
+  maximize_(*maxdist, dist);
+} /* qh_check_point */
+
+
+/*-<a                             href="qh-poly_r.htm#TOC"
+  >-------------------------------</a><a name="check_points">-</a>
+
+  qh_check_points(qh)
+    checks that all points are inside all facets
+
+  notes:
+    if many points and qh_check_maxout not called (i.e., !qh.MERGING),
+       calls qh_findbesthorizon (seldom done).
+    ignores flipped facets
+    maxoutside includes 2 qh.DISTrounds
+      one qh.DISTround for the computed distances in qh_check_points
+    qh_printafacet and qh_printsummary needs only one qh.DISTround
+    the computation for qh.VERIFYdirect does not account for qh.other_points
+
+  design:
+    if many points
+      use qh_check_bestdist()
+    else
+      for all facets
+        for all points
+          check that point is inside facet
+*/
+void qh_check_points(qhT *qh) {
+  facetT *facet, *errfacet1= NULL, *errfacet2= NULL;
+  realT total, maxoutside, maxdist= -REALmax;
+  pointT *point, **pointp, *pointtemp;
+  boolT testouter;
+
+  maxoutside= qh_maxouter(qh);
+  maxoutside += qh->DISTround;
+  /* one more qh.DISTround for check computation */
+  trace1((qh, qh->ferr, 1025, "qh_check_points: check all points below %2.2g of all facet planes\n",
+          maxoutside));
+  if (qh->num_good)   /* miss counts other_points and !good facets */
+     total= (float)qh->num_good * (float)qh->num_points;
+  else
+     total= (float)qh->num_facets * (float)qh->num_points;
+  if (total >= qh_VERIFYdirect && !qh->maxoutdone) {
+    if (!qh_QUICKhelp && qh->SKIPcheckmax && qh->MERGING)
+      qh_fprintf(qh, qh->ferr, 7075, "qhull input warning: merging without checking outer planes('Q5' or 'Po').\n\
+Verify may report that a point is outside of a facet.\n");
+    qh_check_bestdist(qh);
+  }else {
+    if (qh_MAXoutside && qh->maxoutdone)
+      testouter= True;
+    else
+      testouter= False;
+    if (!qh_QUICKhelp) {
+      if (qh->MERGEexact)
+        qh_fprintf(qh, qh->ferr, 7076, "qhull input warning: exact merge ('Qx').  Verify may report that a point\n\
+is outside of a facet.  See qh-optq.htm#Qx\n");
+      else if (qh->SKIPcheckmax || qh->NOnearinside)
+        qh_fprintf(qh, qh->ferr, 7077, "qhull input warning: no outer plane check ('Q5') or no processing of\n\
+near-inside points ('Q8').  Verify may report that a point is outside\n\
+of a facet.\n");
+    }
+    if (qh->PRINTprecision) {
+      if (testouter)
+        qh_fprintf(qh, qh->ferr, 8098, "\n\
+Output completed.  Verifying that all points are below outer planes of\n\
+all %sfacets.  Will make %2.0f distance computations.\n",
+              (qh->ONLYgood ?  "good " : ""), total);
+      else
+        qh_fprintf(qh, qh->ferr, 8099, "\n\
+Output completed.  Verifying that all points are below %2.2g of\n\
+all %sfacets.  Will make %2.0f distance computations.\n",
+              maxoutside, (qh->ONLYgood ?  "good " : ""), total);
+    }
+    FORALLfacets {
+      if (!facet->good && qh->ONLYgood)
+        continue;
+      if (facet->flipped)
+        continue;
+      if (!facet->normal) {
+        qh_fprintf(qh, qh->ferr, 7061, "qhull warning (qh_check_points): missing normal for facet f%d\n", facet->id);
+        continue;
+      }
+      if (testouter) {
+#if qh_MAXoutside
+        maxoutside= facet->maxoutside + 2* qh->DISTround;
+        /* one DISTround to actual point and another to computed point */
+#endif
+      }
+      FORALLpoints {
+        if (point != qh->GOODpointp)
+          qh_check_point(qh, point, facet, &maxoutside, &maxdist, &errfacet1, &errfacet2);
+      }
+      FOREACHpoint_(qh->other_points) {
+        if (point != qh->GOODpointp)
+          qh_check_point(qh, point, facet, &maxoutside, &maxdist, &errfacet1, &errfacet2);
+      }
+    }
+    if (maxdist > qh->outside_err) {
+      qh_fprintf(qh, qh->ferr, 6112, "qhull precision error (qh_check_points): a coplanar point is %6.2g from convex hull.  The maximum value(qh.outside_err) is %6.2g\n",
+                maxdist, qh->outside_err );
+      qh_errexit2(qh, qh_ERRprec, errfacet1, errfacet2 );
+    }else if (errfacet1 && qh->outside_err > REALmax/2)
+        qh_errexit2(qh, qh_ERRprec, errfacet1, errfacet2 );
+    /* else if errfacet1, the error was logged to qh.ferr but does not effect the output */
+    trace0((qh, qh->ferr, 21, "qh_check_points: max distance outside %2.2g\n", maxdist));
+  }
+} /* check_points */
+
+
+/*-<a                             href="qh-poly_r.htm#TOC"
+  >-------------------------------</a><a name="checkconvex">-</a>
+
+  qh_checkconvex(qh, facetlist, fault )
+    check that each ridge in facetlist is convex
+    fault = qh_DATAfault if reporting errors
+          = qh_ALGORITHMfault otherwise
+
+  returns:
+    counts Zconcaveridges and Zcoplanarridges
+    errors if concaveridge or if merging an coplanar ridge
+
+  note:
+    if not merging,
+      tests vertices for neighboring simplicial facets
+    else if ZEROcentrum,
+      tests vertices for neighboring simplicial   facets
+    else
+      tests centrums of neighboring facets
+
+  design:
+    for all facets
+      report flipped facets
+      if ZEROcentrum and simplicial neighbors
+        test vertices for neighboring simplicial facets
+      else
+        test centrum against all neighbors
+*/
+void qh_checkconvex(qhT *qh, facetT *facetlist, int fault) {
+  facetT *facet, *neighbor, **neighborp, *errfacet1=NULL, *errfacet2=NULL;
+  vertexT *vertex;
+  realT dist;
+  pointT *centrum;
+  boolT waserror= False, centrum_warning= False, tempcentrum= False, allsimplicial;
+  int neighbor_i;
+
+  trace1((qh, qh->ferr, 1026, "qh_checkconvex: check all ridges are convex\n"));
+  if (!qh->RERUN) {
+    zzval_(Zconcaveridges)= 0;
+    zzval_(Zcoplanarridges)= 0;
+  }
+  FORALLfacet_(facetlist) {
+    if (facet->flipped) {
+      qh_precision(qh, "flipped facet");
+      qh_fprintf(qh, qh->ferr, 6113, "qhull precision error: f%d is flipped(interior point is outside)\n",
+               facet->id);
+      errfacet1= facet;
+      waserror= True;
+      continue;
+    }
+    if (qh->MERGING && (!qh->ZEROcentrum || !facet->simplicial || facet->tricoplanar))
+      allsimplicial= False;
+    else {
+      allsimplicial= True;
+      neighbor_i= 0;
+      FOREACHneighbor_(facet) {
+        vertex= SETelemt_(facet->vertices, neighbor_i++, vertexT);
+        if (!neighbor->simplicial || neighbor->tricoplanar) {
+          allsimplicial= False;
+          continue;
+        }
+        qh_distplane(qh, vertex->point, neighbor, &dist);
+        if (dist > -qh->DISTround) {
+          if (fault == qh_DATAfault) {
+            qh_precision(qh, "coplanar or concave ridge");
+            qh_fprintf(qh, qh->ferr, 6114, "qhull precision error: initial simplex is not convex. Distance=%.2g\n", dist);
+            qh_errexit(qh, qh_ERRsingular, NULL, NULL);
+          }
+          if (dist > qh->DISTround) {
+            zzinc_(Zconcaveridges);
+            qh_precision(qh, "concave ridge");
+            qh_fprintf(qh, qh->ferr, 6115, "qhull precision error: f%d is concave to f%d, since p%d(v%d) is %6.4g above\n",
+              facet->id, neighbor->id, qh_pointid(qh, vertex->point), vertex->id, dist);
+            errfacet1= facet;
+            errfacet2= neighbor;
+            waserror= True;
+          }else if (qh->ZEROcentrum) {
+            if (dist > 0) {     /* qh_checkzero checks that dist < - qh->DISTround */
+              zzinc_(Zcoplanarridges);
+              qh_precision(qh, "coplanar ridge");
+              qh_fprintf(qh, qh->ferr, 6116, "qhull precision error: f%d is clearly not convex to f%d, since p%d(v%d) is %6.4g above\n",
+                facet->id, neighbor->id, qh_pointid(qh, vertex->point), vertex->id, dist);
+              errfacet1= facet;
+              errfacet2= neighbor;
+              waserror= True;
+            }
+          }else {
+            zzinc_(Zcoplanarridges);
+            qh_precision(qh, "coplanar ridge");
+            trace0((qh, qh->ferr, 22, "qhull precision error: f%d may be coplanar to f%d, since p%d(v%d) is within %6.4g during p%d\n",
+              facet->id, neighbor->id, qh_pointid(qh, vertex->point), vertex->id, dist, qh->furthest_id));
+          }
+        }
+      }
+    }
+    if (!allsimplicial) {
+      if (qh->CENTERtype == qh_AScentrum) {
+        if (!facet->center)
+          facet->center= qh_getcentrum(qh, facet);
+        centrum= facet->center;
+      }else {
+        if (!centrum_warning && (!facet->simplicial || facet->tricoplanar)) {
+           centrum_warning= True;
+           qh_fprintf(qh, qh->ferr, 7062, "qhull warning: recomputing centrums for convexity test.  This may lead to false, precision errors.\n");
+        }
+        centrum= qh_getcentrum(qh, facet);
+        tempcentrum= True;
+      }
+      FOREACHneighbor_(facet) {
+        if (qh->ZEROcentrum && facet->simplicial && neighbor->simplicial)
+          continue;
+        if (facet->tricoplanar || neighbor->tricoplanar)
+          continue;
+        zzinc_(Zdistconvex);
+        qh_distplane(qh, centrum, neighbor, &dist);
+        if (dist > qh->DISTround) {
+          zzinc_(Zconcaveridges);
+          qh_precision(qh, "concave ridge");
+          qh_fprintf(qh, qh->ferr, 6117, "qhull precision error: f%d is concave to f%d.  Centrum of f%d is %6.4g above f%d\n",
+            facet->id, neighbor->id, facet->id, dist, neighbor->id);
+          errfacet1= facet;
+          errfacet2= neighbor;
+          waserror= True;
+        }else if (dist >= 0.0) {   /* if arithmetic always rounds the same,
+                                     can test against centrum radius instead */
+          zzinc_(Zcoplanarridges);
+          qh_precision(qh, "coplanar ridge");
+          qh_fprintf(qh, qh->ferr, 6118, "qhull precision error: f%d is coplanar or concave to f%d.  Centrum of f%d is %6.4g above f%d\n",
+            facet->id, neighbor->id, facet->id, dist, neighbor->id);
+          errfacet1= facet;
+          errfacet2= neighbor;
+          waserror= True;
+        }
+      }
+      if (tempcentrum)
+        qh_memfree(qh, centrum, qh->normal_size);
+    }
+  }
+  if (waserror && !qh->FORCEoutput)
+    qh_errexit2(qh, qh_ERRprec, errfacet1, errfacet2);
+} /* checkconvex */
+
+
+/*-<a                             href="qh-poly_r.htm#TOC"
+  >-------------------------------</a><a name="checkfacet">-</a>
+
+  qh_checkfacet(qh, facet, newmerge, waserror )
+    checks for consistency errors in facet
+    newmerge set if from merge_r.c
+
+  returns:
+    sets waserror if any error occurs
+
+  checks:
+    vertex ids are inverse sorted
+    unless newmerge, at least hull_dim neighbors and vertices (exactly if simplicial)
+    if non-simplicial, at least as many ridges as neighbors
+    neighbors are not duplicated
+    ridges are not duplicated
+    in 3-d, ridges=verticies
+    (qh.hull_dim-1) ridge vertices
+    neighbors are reciprocated
+    ridge neighbors are facet neighbors and a ridge for every neighbor
+    simplicial neighbors match facetintersect
+    vertex intersection matches vertices of common ridges
+    vertex neighbors and facet vertices agree
+    all ridges have distinct vertex sets
+
+  notes:
+    uses neighbor->seen
+
+  design:
+    check sets
+    check vertices
+    check sizes of neighbors and vertices
+    check for qh_MERGEridge and qh_DUPLICATEridge flags
+    check neighbor set
+    check ridge set
+    check ridges, neighbors, and vertices
+*/
+void qh_checkfacet(qhT *qh, facetT *facet, boolT newmerge, boolT *waserrorp) {
+  facetT *neighbor, **neighborp, *errother=NULL;
+  ridgeT *ridge, **ridgep, *errridge= NULL, *ridge2;
+  vertexT *vertex, **vertexp;
+  unsigned previousid= INT_MAX;
+  int numneighbors, numvertices, numridges=0, numRvertices=0;
+  boolT waserror= False;
+  int skipA, skipB, ridge_i, ridge_n, i;
+  setT *intersection;
+
+  if (facet->visible) {
+    qh_fprintf(qh, qh->ferr, 6119, "qhull internal error (qh_checkfacet): facet f%d is on the visible_list\n",
+      facet->id);
+    qh_errexit(qh, qh_ERRqhull, facet, NULL);
+  }
+  if (!facet->normal) {
+    qh_fprintf(qh, qh->ferr, 6120, "qhull internal error (qh_checkfacet): facet f%d does not have  a normal\n",
+      facet->id);
+    waserror= True;
+  }
+  qh_setcheck(qh, facet->vertices, "vertices for f", facet->id);
+  qh_setcheck(qh, facet->ridges, "ridges for f", facet->id);
+  qh_setcheck(qh, facet->outsideset, "outsideset for f", facet->id);
+  qh_setcheck(qh, facet->coplanarset, "coplanarset for f", facet->id);
+  qh_setcheck(qh, facet->neighbors, "neighbors for f", facet->id);
+  FOREACHvertex_(facet->vertices) {
+    if (vertex->deleted) {
+      qh_fprintf(qh, qh->ferr, 6121, "qhull internal error (qh_checkfacet): deleted vertex v%d in f%d\n", vertex->id, facet->id);
+      qh_errprint(qh, "ERRONEOUS", NULL, NULL, NULL, vertex);
+      waserror= True;
+    }
+    if (vertex->id >= previousid) {
+      qh_fprintf(qh, qh->ferr, 6122, "qhull internal error (qh_checkfacet): vertices of f%d are not in descending id order at v%d\n", facet->id, vertex->id);
+      waserror= True;
+      break;
+    }
+    previousid= vertex->id;
+  }
+  numneighbors= qh_setsize(qh, facet->neighbors);
+  numvertices= qh_setsize(qh, facet->vertices);
+  numridges= qh_setsize(qh, facet->ridges);
+  if (facet->simplicial) {
+    if (numvertices+numneighbors != 2*qh->hull_dim
+    && !facet->degenerate && !facet->redundant) {
+      qh_fprintf(qh, qh->ferr, 6123, "qhull internal error (qh_checkfacet): for simplicial facet f%d, #vertices %d + #neighbors %d != 2*qh->hull_dim\n",
+                facet->id, numvertices, numneighbors);
+      qh_setprint(qh, qh->ferr, "", facet->neighbors);
+      waserror= True;
+    }
+  }else { /* non-simplicial */
+    if (!newmerge
+    &&(numvertices < qh->hull_dim || numneighbors < qh->hull_dim)
+    && !facet->degenerate && !facet->redundant) {
+      qh_fprintf(qh, qh->ferr, 6124, "qhull internal error (qh_checkfacet): for facet f%d, #vertices %d or #neighbors %d < qh->hull_dim\n",
+         facet->id, numvertices, numneighbors);
+       waserror= True;
+    }
+    /* in 3-d, can get a vertex twice in an edge list, e.g., RBOX 1000 s W1e-13 t995849315 D2 | QHULL d Tc Tv TP624 TW1e-13 T4 */
+    if (numridges < numneighbors
+    ||(qh->hull_dim == 3 && numvertices > numridges && !qh->NEWfacets)
+    ||(qh->hull_dim == 2 && numridges + numvertices + numneighbors != 6)) {
+      if (!facet->degenerate && !facet->redundant) {
+        qh_fprintf(qh, qh->ferr, 6125, "qhull internal error (qh_checkfacet): for facet f%d, #ridges %d < #neighbors %d or(3-d) > #vertices %d or(2-d) not all 2\n",
+            facet->id, numridges, numneighbors, numvertices);
+        waserror= True;
+      }
+    }
+  }
+  FOREACHneighbor_(facet) {
+    if (neighbor == qh_MERGEridge || neighbor == qh_DUPLICATEridge) {
+      qh_fprintf(qh, qh->ferr, 6126, "qhull internal error (qh_checkfacet): facet f%d still has a MERGE or DUP neighbor\n", facet->id);
+      qh_errexit(qh, qh_ERRqhull, facet, NULL);
+    }
+    neighbor->seen= True;
+  }
+  FOREACHneighbor_(facet) {
+    if (!qh_setin(neighbor->neighbors, facet)) {
+      qh_fprintf(qh, qh->ferr, 6127, "qhull internal error (qh_checkfacet): facet f%d has neighbor f%d, but f%d does not have neighbor f%d\n",
+              facet->id, neighbor->id, neighbor->id, facet->id);
+      errother= neighbor;
+      waserror= True;
+    }
+    if (!neighbor->seen) {
+      qh_fprintf(qh, qh->ferr, 6128, "qhull internal error (qh_checkfacet): facet f%d has a duplicate neighbor f%d\n",
+              facet->id, neighbor->id);
+      errother= neighbor;
+      waserror= True;
+    }
+    neighbor->seen= False;
+  }
+  FOREACHridge_(facet->ridges) {
+    qh_setcheck(qh, ridge->vertices, "vertices for r", ridge->id);
+    ridge->seen= False;
+  }
+  FOREACHridge_(facet->ridges) {
+    if (ridge->seen) {
+      qh_fprintf(qh, qh->ferr, 6129, "qhull internal error (qh_checkfacet): facet f%d has a duplicate ridge r%d\n",
+              facet->id, ridge->id);
+      errridge= ridge;
+      waserror= True;
+    }
+    ridge->seen= True;
+    numRvertices= qh_setsize(qh, ridge->vertices);
+    if (numRvertices != qh->hull_dim - 1) {
+      qh_fprintf(qh, qh->ferr, 6130, "qhull internal error (qh_checkfacet): ridge between f%d and f%d has %d vertices\n",
+                ridge->top->id, ridge->bottom->id, numRvertices);
+      errridge= ridge;
+      waserror= True;
+    }
+    neighbor= otherfacet_(ridge, facet);
+    neighbor->seen= True;
+    if (!qh_setin(facet->neighbors, neighbor)) {
+      qh_fprintf(qh, qh->ferr, 6131, "qhull internal error (qh_checkfacet): for facet f%d, neighbor f%d of ridge r%d not in facet\n",
+           facet->id, neighbor->id, ridge->id);
+      errridge= ridge;
+      waserror= True;
+    }
+  }
+  if (!facet->simplicial) {
+    FOREACHneighbor_(facet) {
+      if (!neighbor->seen) {
+        qh_fprintf(qh, qh->ferr, 6132, "qhull internal error (qh_checkfacet): facet f%d does not have a ridge for neighbor f%d\n",
+              facet->id, neighbor->id);
+        errother= neighbor;
+        waserror= True;
+      }
+      intersection= qh_vertexintersect_new(qh, facet->vertices, neighbor->vertices);
+      qh_settemppush(qh, intersection);
+      FOREACHvertex_(facet->vertices) {
+        vertex->seen= False;
+        vertex->seen2= False;
+      }
+      FOREACHvertex_(intersection)
+        vertex->seen= True;
+      FOREACHridge_(facet->ridges) {
+        if (neighbor != otherfacet_(ridge, facet))
+            continue;
+        FOREACHvertex_(ridge->vertices) {
+          if (!vertex->seen) {
+            qh_fprintf(qh, qh->ferr, 6133, "qhull internal error (qh_checkfacet): vertex v%d in r%d not in f%d intersect f%d\n",
+                  vertex->id, ridge->id, facet->id, neighbor->id);
+            qh_errexit(qh, qh_ERRqhull, facet, ridge);
+          }
+          vertex->seen2= True;
+        }
+      }
+      if (!newmerge) {
+        FOREACHvertex_(intersection) {
+          if (!vertex->seen2) {
+            if (qh->IStracing >=3 || !qh->MERGING) {
+              qh_fprintf(qh, qh->ferr, 6134, "qhull precision error (qh_checkfacet): vertex v%d in f%d intersect f%d but\n\
+ not in a ridge.  This is ok under merging.  Last point was p%d\n",
+                     vertex->id, facet->id, neighbor->id, qh->furthest_id);
+              if (!qh->FORCEoutput && !qh->MERGING) {
+                qh_errprint(qh, "ERRONEOUS", facet, neighbor, NULL, vertex);
+                if (!qh->MERGING)
+                  qh_errexit(qh, qh_ERRqhull, NULL, NULL);
+              }
+            }
+          }
+        }
+      }
+      qh_settempfree(qh, &intersection);
+    }
+  }else { /* simplicial */
+    FOREACHneighbor_(facet) {
+      if (neighbor->simplicial) {
+        skipA= SETindex_(facet->neighbors, neighbor);
+        skipB= qh_setindex(neighbor->neighbors, facet);
+        if (skipA<0 || skipB<0 || !qh_setequal_skip(facet->vertices, skipA, neighbor->vertices, skipB)) {
+          qh_fprintf(qh, qh->ferr, 6135, "qhull internal error (qh_checkfacet): facet f%d skip %d and neighbor f%d skip %d do not match \n",
+                   facet->id, skipA, neighbor->id, skipB);
+          errother= neighbor;
+          waserror= True;
+        }
+      }
+    }
+  }
+  if (qh->hull_dim < 5 && (qh->IStracing > 2 || qh->CHECKfrequently)) {
+    FOREACHridge_i_(qh, facet->ridges) {           /* expensive */
+      for (i=ridge_i+1; i < ridge_n; i++) {
+        ridge2= SETelemt_(facet->ridges, i, ridgeT);
+        if (qh_setequal(ridge->vertices, ridge2->vertices)) {
+          qh_fprintf(qh, qh->ferr, 6227, "Qhull internal error (qh_checkfacet): ridges r%d and r%d have the same vertices\n",
+                  ridge->id, ridge2->id);
+          errridge= ridge;
+          waserror= True;
+        }
+      }
+    }
+  }
+  if (waserror) {
+    qh_errprint(qh, "ERRONEOUS", facet, errother, errridge, NULL);
+    *waserrorp= True;
+  }
+} /* checkfacet */
+
+
+/*-<a                             href="qh-poly_r.htm#TOC"
+  >-------------------------------</a><a name="checkflipped_all">-</a>
+
+  qh_checkflipped_all(qh, facetlist )
+    checks orientation of facets in list against interior point
+*/
+void qh_checkflipped_all(qhT *qh, facetT *facetlist) {
+  facetT *facet;
+  boolT waserror= False;
+  realT dist;
+
+  if (facetlist == qh->facet_list)
+    zzval_(Zflippedfacets)= 0;
+  FORALLfacet_(facetlist) {
+    if (facet->normal && !qh_checkflipped(qh, facet, &dist, !qh_ALL)) {
+      qh_fprintf(qh, qh->ferr, 6136, "qhull precision error: facet f%d is flipped, distance= %6.12g\n",
+              facet->id, dist);
+      if (!qh->FORCEoutput) {
+        qh_errprint(qh, "ERRONEOUS", facet, NULL, NULL, NULL);
+        waserror= True;
+      }
+    }
+  }
+  if (waserror) {
+    qh_fprintf(qh, qh->ferr, 8101, "\n\
+A flipped facet occurs when its distance to the interior point is\n\
+greater than %2.2g, the maximum roundoff error.\n", -qh->DISTround);
+    qh_errexit(qh, qh_ERRprec, NULL, NULL);
+  }
+} /* checkflipped_all */
+
+/*-<a                             href="qh-poly_r.htm#TOC"
+  >-------------------------------</a><a name="checkpolygon">-</a>
+
+  qh_checkpolygon(qh, facetlist )
+    checks the correctness of the structure
+
+  notes:
+    call with either qh.facet_list or qh.newfacet_list
+    checks num_facets and num_vertices if qh.facet_list
+
+  design:
+    for each facet
+      checks facet and outside set
+    initializes vertexlist
+    for each facet
+      checks vertex set
+    if checking all facets(qh.facetlist)
+      check facet count
+      if qh.VERTEXneighbors
+        check vertex neighbors and count
+      check vertex count
+*/
+void qh_checkpolygon(qhT *qh, facetT *facetlist) {
+  facetT *facet;
+  vertexT *vertex, **vertexp, *vertexlist;
+  int numfacets= 0, numvertices= 0, numridges= 0;
+  int totvneighbors= 0, totvertices= 0;
+  boolT waserror= False, nextseen= False, visibleseen= False;
+
+  trace1((qh, qh->ferr, 1027, "qh_checkpolygon: check all facets from f%d\n", facetlist->id));
+  if (facetlist != qh->facet_list || qh->ONLYgood)
+    nextseen= True;
+  FORALLfacet_(facetlist) {
+    if (facet == qh->visible_list)
+      visibleseen= True;
+    if (!facet->visible) {
+      if (!nextseen) {
+        if (facet == qh->facet_next)
+          nextseen= True;
+        else if (qh_setsize(qh, facet->outsideset)) {
+          if (!qh->NARROWhull
+#if !qh_COMPUTEfurthest
+               || facet->furthestdist >= qh->MINoutside
+#endif
+                        ) {
+            qh_fprintf(qh, qh->ferr, 6137, "qhull internal error (qh_checkpolygon): f%d has outside points before qh->facet_next\n",
+                     facet->id);
+            qh_errexit(qh, qh_ERRqhull, facet, NULL);
+          }
+        }
+      }
+      numfacets++;
+      qh_checkfacet(qh, facet, False, &waserror);
+    }
+  }
+  if (qh->visible_list && !visibleseen && facetlist == qh->facet_list) {
+    qh_fprintf(qh, qh->ferr, 6138, "qhull internal error (qh_checkpolygon): visible list f%d no longer on facet list\n", qh->visible_list->id);
+    qh_printlists(qh);
+    qh_errexit(qh, qh_ERRqhull, qh->visible_list, NULL);
+  }
+  if (facetlist == qh->facet_list)
+    vertexlist= qh->vertex_list;
+  else if (facetlist == qh->newfacet_list)
+    vertexlist= qh->newvertex_list;
+  else
+    vertexlist= NULL;
+  FORALLvertex_(vertexlist) {
+    vertex->seen= False;
+    vertex->visitid= 0;
+  }
+  FORALLfacet_(facetlist) {
+    if (facet->visible)
+      continue;
+    if (facet->simplicial)
+      numridges += qh->hull_dim;
+    else
+      numridges += qh_setsize(qh, facet->ridges);
+    FOREACHvertex_(facet->vertices) {
+      vertex->visitid++;
+      if (!vertex->seen) {
+        vertex->seen= True;
+        numvertices++;
+        if (qh_pointid(qh, vertex->point) == qh_IDunknown) {
+          qh_fprintf(qh, qh->ferr, 6139, "qhull internal error (qh_checkpolygon): unknown point %p for vertex v%d first_point %p\n",
+                   vertex->point, vertex->id, qh->first_point);
+          waserror= True;
+        }
+      }
+    }
+  }
+  qh->vertex_visit += (unsigned int)numfacets;
+  if (facetlist == qh->facet_list) {
+    if (numfacets != qh->num_facets - qh->num_visible) {
+      qh_fprintf(qh, qh->ferr, 6140, "qhull internal error (qh_checkpolygon): actual number of facets is %d, cumulative facet count is %d - %d visible facets\n",
+              numfacets, qh->num_facets, qh->num_visible);
+      waserror= True;
+    }
+    qh->vertex_visit++;
+    if (qh->VERTEXneighbors) {
+      FORALLvertices {
+        qh_setcheck(qh, vertex->neighbors, "neighbors for v", vertex->id);
+        if (vertex->deleted)
+          continue;
+        totvneighbors += qh_setsize(qh, vertex->neighbors);
+      }
+      FORALLfacet_(facetlist)
+        totvertices += qh_setsize(qh, facet->vertices);
+      if (totvneighbors != totvertices) {
+        qh_fprintf(qh, qh->ferr, 6141, "qhull internal error (qh_checkpolygon): vertex neighbors inconsistent.  Totvneighbors %d, totvertices %d\n",
+                totvneighbors, totvertices);
+        waserror= True;
+      }
+    }
+    if (numvertices != qh->num_vertices - qh_setsize(qh, qh->del_vertices)) {
+      qh_fprintf(qh, qh->ferr, 6142, "qhull internal error (qh_checkpolygon): actual number of vertices is %d, cumulative vertex count is %d\n",
+              numvertices, qh->num_vertices - qh_setsize(qh, qh->del_vertices));
+      waserror= True;
+    }
+    if (qh->hull_dim == 2 && numvertices != numfacets) {
+      qh_fprintf(qh, qh->ferr, 6143, "qhull internal error (qh_checkpolygon): #vertices %d != #facets %d\n",
+        numvertices, numfacets);
+      waserror= True;
+    }
+    if (qh->hull_dim == 3 && numvertices + numfacets - numridges/2 != 2) {
+      qh_fprintf(qh, qh->ferr, 7063, "qhull warning: #vertices %d + #facets %d - #edges %d != 2\n\
+        A vertex appears twice in a edge list.  May occur during merging.",
+        numvertices, numfacets, numridges/2);
+      /* occurs if lots of merging and a vertex ends up twice in an edge list.  e.g., RBOX 1000 s W1e-13 t995849315 D2 | QHULL d Tc Tv */
+    }
+  }
+  if (waserror)
+    qh_errexit(qh, qh_ERRqhull, NULL, NULL);
+} /* checkpolygon */
+
+
+/*-<a                             href="qh-poly_r.htm#TOC"
+  >-------------------------------</a><a name="checkvertex">-</a>
+
+  qh_checkvertex(qh, vertex )
+    check vertex for consistency
+    checks vertex->neighbors
+
+  notes:
+    neighbors checked efficiently in checkpolygon
+*/
+void qh_checkvertex(qhT *qh, vertexT *vertex) {
+  boolT waserror= False;
+  facetT *neighbor, **neighborp, *errfacet=NULL;
+
+  if (qh_pointid(qh, vertex->point) == qh_IDunknown) {
+    qh_fprintf(qh, qh->ferr, 6144, "qhull internal error (qh_checkvertex): unknown point id %p\n", vertex->point);
+    waserror= True;
+  }
+  if (vertex->id >= qh->vertex_id) {
+    qh_fprintf(qh, qh->ferr, 6145, "qhull internal error (qh_checkvertex): unknown vertex id %d\n", vertex->id);
+    waserror= True;
+  }
+  if (!waserror && !vertex->deleted) {
+    if (qh_setsize(qh, vertex->neighbors)) {
+      FOREACHneighbor_(vertex) {
+        if (!qh_setin(neighbor->vertices, vertex)) {
+          qh_fprintf(qh, qh->ferr, 6146, "qhull internal error (qh_checkvertex): neighbor f%d does not contain v%d\n", neighbor->id, vertex->id);
+          errfacet= neighbor;
+          waserror= True;
+        }
+      }
+    }
+  }
+  if (waserror) {
+    qh_errprint(qh, "ERRONEOUS", NULL, NULL, NULL, vertex);
+    qh_errexit(qh, qh_ERRqhull, errfacet, NULL);
+  }
+} /* checkvertex */
+
+/*-<a                             href="qh-poly_r.htm#TOC"
+  >-------------------------------</a><a name="clearcenters">-</a>
+
+  qh_clearcenters(qh, type )
+    clear old data from facet->center
+
+  notes:
+    sets new centertype
+    nop if CENTERtype is the same
+*/
+void qh_clearcenters(qhT *qh, qh_CENTER type) {
+  facetT *facet;
+
+  if (qh->CENTERtype != type) {
+    FORALLfacets {
+      if (facet->tricoplanar && !facet->keepcentrum)
+          facet->center= NULL;  /* center is owned by the ->keepcentrum facet */
+      else if (qh->CENTERtype == qh_ASvoronoi){
+        if (facet->center) {
+          qh_memfree(qh, facet->center, qh->center_size);
+          facet->center= NULL;
+        }
+      }else /* qh->CENTERtype == qh_AScentrum */ {
+        if (facet->center) {
+          qh_memfree(qh, facet->center, qh->normal_size);
+          facet->center= NULL;
+        }
+      }
+    }
+    qh->CENTERtype= type;
+  }
+  trace2((qh, qh->ferr, 2043, "qh_clearcenters: switched to center type %d\n", type));
+} /* clearcenters */
+
+/*-<a                             href="qh-poly_r.htm#TOC"
+  >-------------------------------</a><a name="createsimplex">-</a>
+
+  qh_createsimplex(qh, vertices )
+    creates a simplex from a set of vertices
+
+  returns:
+    initializes qh.facet_list to the simplex
+    initializes qh.newfacet_list, .facet_tail
+    initializes qh.vertex_list, .newvertex_list, .vertex_tail
+
+  design:
+    initializes lists
+    for each vertex
+      create a new facet
+    for each new facet
+      create its neighbor set
+*/
+void qh_createsimplex(qhT *qh, setT *vertices) {
+  facetT *facet= NULL, *newfacet;
+  boolT toporient= True;
+  int vertex_i, vertex_n, nth;
+  setT *newfacets= qh_settemp(qh, qh->hull_dim+1);
+  vertexT *vertex;
+
+  qh->facet_list= qh->newfacet_list= qh->facet_tail= qh_newfacet(qh);
+  qh->num_facets= qh->num_vertices= qh->num_visible= 0;
+  qh->vertex_list= qh->newvertex_list= qh->vertex_tail= qh_newvertex(qh, NULL);
+  FOREACHvertex_i_(qh, vertices) {
+    newfacet= qh_newfacet(qh);
+    newfacet->vertices= qh_setnew_delnthsorted(qh, vertices, vertex_n,
+                                                vertex_i, 0);
+    newfacet->toporient= (unsigned char)toporient;
+    qh_appendfacet(qh, newfacet);
+    newfacet->newfacet= True;
+    qh_appendvertex(qh, vertex);
+    qh_setappend(qh, &newfacets, newfacet);
+    toporient ^= True;
+  }
+  FORALLnew_facets {
+    nth= 0;
+    FORALLfacet_(qh->newfacet_list) {
+      if (facet != newfacet)
+        SETelem_(newfacet->neighbors, nth++)= facet;
+    }
+    qh_settruncate(qh, newfacet->neighbors, qh->hull_dim);
+  }
+  qh_settempfree(qh, &newfacets);
+  trace1((qh, qh->ferr, 1028, "qh_createsimplex: created simplex\n"));
+} /* createsimplex */
+
+/*-<a                             href="qh-poly_r.htm#TOC"
+  >-------------------------------</a><a name="delridge">-</a>
+
+  qh_delridge(qh, ridge )
+    deletes ridge from data structures it belongs to
+    frees up its memory
+
+  notes:
+    in merge_r.c, caller sets vertex->delridge for each vertex
+    ridges also freed in qh_freeqhull
+*/
+void qh_delridge(qhT *qh, ridgeT *ridge) {
+  void **freelistp; /* used if !qh_NOmem by qh_memfree_() */
+
+  qh_setdel(ridge->top->ridges, ridge);
+  qh_setdel(ridge->bottom->ridges, ridge);
+  qh_setfree(qh, &(ridge->vertices));
+  qh_memfree_(qh, ridge, (int)sizeof(ridgeT), freelistp);
+} /* delridge */
+
+
+/*-<a                             href="qh-poly_r.htm#TOC"
+  >-------------------------------</a><a name="delvertex">-</a>
+
+  qh_delvertex(qh, vertex )
+    deletes a vertex and frees its memory
+
+  notes:
+    assumes vertex->adjacencies have been updated if needed
+    unlinks from vertex_list
+*/
+void qh_delvertex(qhT *qh, vertexT *vertex) {
+
+  if (vertex == qh->tracevertex)
+    qh->tracevertex= NULL;
+  qh_removevertex(qh, vertex);
+  qh_setfree(qh, &vertex->neighbors);
+  qh_memfree(qh, vertex, (int)sizeof(vertexT));
+} /* delvertex */
+
+
+/*-<a                             href="qh-poly_r.htm#TOC"
+  >-------------------------------</a><a name="facet3vertex">-</a>
+
+  qh_facet3vertex(qh, )
+    return temporary set of 3-d vertices in qh_ORIENTclock order
+
+  design:
+    if simplicial facet
+      build set from facet->vertices with facet->toporient
+    else
+      for each ridge in order
+        build set from ridge's vertices
+*/
+setT *qh_facet3vertex(qhT *qh, facetT *facet) {
+  ridgeT *ridge, *firstridge;
+  vertexT *vertex;
+  int cntvertices, cntprojected=0;
+  setT *vertices;
+
+  cntvertices= qh_setsize(qh, facet->vertices);
+  vertices= qh_settemp(qh, cntvertices);
+  if (facet->simplicial) {
+    if (cntvertices != 3) {
+      qh_fprintf(qh, qh->ferr, 6147, "qhull internal error (qh_facet3vertex): only %d vertices for simplicial facet f%d\n",
+                  cntvertices, facet->id);
+      qh_errexit(qh, qh_ERRqhull, facet, NULL);
+    }
+    qh_setappend(qh, &vertices, SETfirst_(facet->vertices));
+    if (facet->toporient ^ qh_ORIENTclock)
+      qh_setappend(qh, &vertices, SETsecond_(facet->vertices));
+    else
+      qh_setaddnth(qh, &vertices, 0, SETsecond_(facet->vertices));
+    qh_setappend(qh, &vertices, SETelem_(facet->vertices, 2));
+  }else {
+    ridge= firstridge= SETfirstt_(facet->ridges, ridgeT);   /* no infinite */
+    while ((ridge= qh_nextridge3d(ridge, facet, &vertex))) {
+      qh_setappend(qh, &vertices, vertex);
+      if (++cntprojected > cntvertices || ridge == firstridge)
+        break;
+    }
+    if (!ridge || cntprojected != cntvertices) {
+      qh_fprintf(qh, qh->ferr, 6148, "qhull internal error (qh_facet3vertex): ridges for facet %d don't match up.  got at least %d\n",
+                  facet->id, cntprojected);
+      qh_errexit(qh, qh_ERRqhull, facet, ridge);
+    }
+  }
+  return vertices;
+} /* facet3vertex */
+
+/*-<a                             href="qh-poly_r.htm#TOC"
+  >-------------------------------</a><a name="findbestfacet">-</a>
+
+  qh_findbestfacet(qh, point, bestoutside, bestdist, isoutside )
+    find facet that is furthest below a point
+
+    for Delaunay triangulations,
+      Use qh_setdelaunay() to lift point to paraboloid and scale by 'Qbb' if needed
+      Do not use options 'Qbk', 'QBk', or 'QbB' since they scale the coordinates.
+
+  returns:
+    if bestoutside is set (e.g., qh_ALL)
+      returns best facet that is not upperdelaunay
+      if Delaunay and inside, point is outside circumsphere of bestfacet
+    else
+      returns first facet below point
+      if point is inside, returns nearest, !upperdelaunay facet
+    distance to facet
+    isoutside set if outside of facet
+
+  notes:
+    For tricoplanar facets, this finds one of the tricoplanar facets closest
+    to the point.  For Delaunay triangulations, the point may be inside a
+    different tricoplanar facet. See <a href="../html/qh-code.htm#findfacet">locate a facet with qh_findbestfacet()</a>
+
+    If inside, qh_findbestfacet performs an exhaustive search
+       this may be too conservative.  Sometimes it is clearly required.
+
+    qh_findbestfacet is not used by qhull.
+    uses qh.visit_id and qh.coplanarset
+
+  see:
+    <a href="geom_r.c#findbest">qh_findbest</a>
+*/
+facetT *qh_findbestfacet(qhT *qh, pointT *point, boolT bestoutside,
+           realT *bestdist, boolT *isoutside) {
+  facetT *bestfacet= NULL;
+  int numpart, totpart= 0;
+
+  bestfacet= qh_findbest(qh, point, qh->facet_list,
+                            bestoutside, !qh_ISnewfacets, bestoutside /* qh_NOupper */,
+                            bestdist, isoutside, &totpart);
+  if (*bestdist < -qh->DISTround) {
+    bestfacet= qh_findfacet_all(qh, point, bestdist, isoutside, &numpart);
+    totpart += numpart;
+    if ((isoutside && *isoutside && bestoutside)
+    || (isoutside && !*isoutside && bestfacet->upperdelaunay)) {
+      bestfacet= qh_findbest(qh, point, bestfacet,
+                            bestoutside, False, bestoutside,
+                            bestdist, isoutside, &totpart);
+      totpart += numpart;
+    }
+  }
+  trace3((qh, qh->ferr, 3014, "qh_findbestfacet: f%d dist %2.2g isoutside %d totpart %d\n",
+          bestfacet->id, *bestdist, (isoutside ? *isoutside : UINT_MAX), totpart));
+  return bestfacet;
+} /* findbestfacet */
+
+/*-<a                             href="qh-poly_r.htm#TOC"
+  >-------------------------------</a><a name="findbestlower">-</a>
+
+  qh_findbestlower(qh, facet, point, bestdist, numpart )
+    returns best non-upper, non-flipped neighbor of facet for point
+    if needed, searches vertex neighbors
+
+  returns:
+    returns bestdist and updates numpart
+
+  notes:
+    if Delaunay and inside, point is outside of circumsphere of bestfacet
+    called by qh_findbest() for points above an upperdelaunay facet
+
+*/
+facetT *qh_findbestlower(qhT *qh, facetT *upperfacet, pointT *point, realT *bestdistp, int *numpart) {
+  facetT *neighbor, **neighborp, *bestfacet= NULL;
+  realT bestdist= -REALmax/2 /* avoid underflow */;
+  realT dist;
+  vertexT *vertex;
+  boolT isoutside= False;  /* not used */
+
+  zinc_(Zbestlower);
+  FOREACHneighbor_(upperfacet) {
+    if (neighbor->upperdelaunay || neighbor->flipped)
+      continue;
+    (*numpart)++;
+    qh_distplane(qh, point, neighbor, &dist);
+    if (dist > bestdist) {
+      bestfacet= neighbor;
+      bestdist= dist;
+    }
+  }
+  if (!bestfacet) {
+    zinc_(Zbestlowerv);
+    /* rarely called, numpart does not count nearvertex computations */
+    vertex= qh_nearvertex(qh, upperfacet, point, &dist);
+    qh_vertexneighbors(qh);
+    FOREACHneighbor_(vertex) {
+      if (neighbor->upperdelaunay || neighbor->flipped)
+        continue;
+      (*numpart)++;
+      qh_distplane(qh, point, neighbor, &dist);
+      if (dist > bestdist) {
+        bestfacet= neighbor;
+        bestdist= dist;
+      }
+    }
+  }
+  if (!bestfacet) {
+    zinc_(Zbestlowerall);  /* invoked once per point in outsideset */
+    zmax_(Zbestloweralln, qh->num_facets);
+    /* [dec'15] Previously reported as QH6228 */
+    trace3((qh, qh->ferr, 3025, "qh_findbestlower: all neighbors of facet %d are flipped or upper Delaunay.  Search all facets\n",
+       upperfacet->id));
+    /* rarely called */
+    bestfacet= qh_findfacet_all(qh, point, &bestdist, &isoutside, numpart);
+  }
+  *bestdistp= bestdist;
+  trace3((qh, qh->ferr, 3015, "qh_findbestlower: f%d dist %2.2g for f%d p%d\n",
+          bestfacet->id, bestdist, upperfacet->id, qh_pointid(qh, point)));
+  return bestfacet;
+} /* findbestlower */
+
+/*-<a                             href="qh-poly_r.htm#TOC"
+  >-------------------------------</a><a name="findfacet_all">-</a>
+
+  qh_findfacet_all(qh, point, bestdist, isoutside, numpart )
+    exhaustive search for facet below a point
+
+    for Delaunay triangulations,
+      Use qh_setdelaunay() to lift point to paraboloid and scale by 'Qbb' if needed
+      Do not use options 'Qbk', 'QBk', or 'QbB' since they scale the coordinates.
+
+  returns:
+    returns first facet below point
+    if point is inside,
+      returns nearest facet
+    distance to facet
+    isoutside if point is outside of the hull
+    number of distance tests
+
+  notes:
+    primarily for library users, rarely used by Qhull
+*/
+facetT *qh_findfacet_all(qhT *qh, pointT *point, realT *bestdist, boolT *isoutside,
+                          int *numpart) {
+  facetT *bestfacet= NULL, *facet;
+  realT dist;
+  int totpart= 0;
+
+  *bestdist= -REALmax;
+  *isoutside= False;
+  FORALLfacets {
+    if (facet->flipped || !facet->normal)
+      continue;
+    totpart++;
+    qh_distplane(qh, point, facet, &dist);
+    if (dist > *bestdist) {
+      *bestdist= dist;
+      bestfacet= facet;
+      if (dist > qh->MINoutside) {
+        *isoutside= True;
+        break;
+      }
+    }
+  }
+  *numpart= totpart;
+  trace3((qh, qh->ferr, 3016, "qh_findfacet_all: f%d dist %2.2g isoutside %d totpart %d\n",
+          getid_(bestfacet), *bestdist, *isoutside, totpart));
+  return bestfacet;
+} /* findfacet_all */
+
+/*-<a                             href="qh-poly_r.htm#TOC"
+  >-------------------------------</a><a name="findgood">-</a>
+
+  qh_findgood(qh, facetlist, goodhorizon )
+    identify good facets for qh.PRINTgood
+    if qh.GOODvertex>0
+      facet includes point as vertex
+      if !match, returns goodhorizon
+      inactive if qh.MERGING
+    if qh.GOODpoint
+      facet is visible or coplanar (>0) or not visible (<0)
+    if qh.GOODthreshold
+      facet->normal matches threshold
+    if !goodhorizon and !match,
+      selects facet with closest angle
+      sets GOODclosest
+
+  returns:
+    number of new, good facets found
+    determines facet->good
+    may update qh.GOODclosest
+
+  notes:
+    qh_findgood_all further reduces the good region
+
+  design:
+    count good facets
+    mark good facets for qh.GOODpoint
+    mark good facets for qh.GOODthreshold
+    if necessary
+      update qh.GOODclosest
+*/
+int qh_findgood(qhT *qh, facetT *facetlist, int goodhorizon) {
+  facetT *facet, *bestfacet= NULL;
+  realT angle, bestangle= REALmax, dist;
+  int  numgood=0;
+
+  FORALLfacet_(facetlist) {
+    if (facet->good)
+      numgood++;
+  }
+  if (qh->GOODvertex>0 && !qh->MERGING) {
+    FORALLfacet_(facetlist) {
+      if (!qh_isvertex(qh->GOODvertexp, facet->vertices)) {
+        facet->good= False;
+        numgood--;
+      }
+    }
+  }
+  if (qh->GOODpoint && numgood) {
+    FORALLfacet_(facetlist) {
+      if (facet->good && facet->normal) {
+        zinc_(Zdistgood);
+        qh_distplane(qh, qh->GOODpointp, facet, &dist);
+        if ((qh->GOODpoint > 0) ^ (dist > 0.0)) {
+          facet->good= False;
+          numgood--;
+        }
+      }
+    }
+  }
+  if (qh->GOODthreshold && (numgood || goodhorizon || qh->GOODclosest)) {
+    FORALLfacet_(facetlist) {
+      if (facet->good && facet->normal) {
+        if (!qh_inthresholds(qh, facet->normal, &angle)) {
+          facet->good= False;
+          numgood--;
+          if (angle < bestangle) {
+            bestangle= angle;
+            bestfacet= facet;
+          }
+        }
+      }
+    }
+    if (!numgood && (!goodhorizon || qh->GOODclosest)) {
+      if (qh->GOODclosest) {
+        if (qh->GOODclosest->visible)
+          qh->GOODclosest= NULL;
+        else {
+          qh_inthresholds(qh, qh->GOODclosest->normal, &angle);
+          if (angle < bestangle)
+            bestfacet= qh->GOODclosest;
+        }
+      }
+      if (bestfacet && bestfacet != qh->GOODclosest) {
+        if (qh->GOODclosest)
+          qh->GOODclosest->good= False;
+        qh->GOODclosest= bestfacet;
+        bestfacet->good= True;
+        numgood++;
+        trace2((qh, qh->ferr, 2044, "qh_findgood: f%d is closest(%2.2g) to thresholds\n",
+           bestfacet->id, bestangle));
+        return numgood;
+      }
+    }else if (qh->GOODclosest) { /* numgood > 0 */
+      qh->GOODclosest->good= False;
+      qh->GOODclosest= NULL;
+    }
+  }
+  zadd_(Zgoodfacet, numgood);
+  trace2((qh, qh->ferr, 2045, "qh_findgood: found %d good facets with %d good horizon\n",
+               numgood, goodhorizon));
+  if (!numgood && qh->GOODvertex>0 && !qh->MERGING)
+    return goodhorizon;
+  return numgood;
+} /* findgood */
+
+/*-<a                             href="qh-poly_r.htm#TOC"
+  >-------------------------------</a><a name="findgood_all">-</a>
+
+  qh_findgood_all(qh, facetlist )
+    apply other constraints for good facets (used by qh.PRINTgood)
+    if qh.GOODvertex
+      facet includes (>0) or doesn't include (<0) point as vertex
+      if last good facet and ONLYgood, prints warning and continues
+    if qh.SPLITthresholds
+      facet->normal matches threshold, or if none, the closest one
+    calls qh_findgood
+    nop if good not used
+
+  returns:
+    clears facet->good if not good
+    sets qh.num_good
+
+  notes:
+    this is like qh_findgood but more restrictive
+
+  design:
+    uses qh_findgood to mark good facets
+    marks facets for qh.GOODvertex
+    marks facets for qh.SPLITthreholds
+*/
+void qh_findgood_all(qhT *qh, facetT *facetlist) {
+  facetT *facet, *bestfacet=NULL;
+  realT angle, bestangle= REALmax;
+  int  numgood=0, startgood;
+
+  if (!qh->GOODvertex && !qh->GOODthreshold && !qh->GOODpoint
+  && !qh->SPLITthresholds)
+    return;
+  if (!qh->ONLYgood)
+    qh_findgood(qh, qh->facet_list, 0);
+  FORALLfacet_(facetlist) {
+    if (facet->good)
+      numgood++;
+  }
+  if (qh->GOODvertex <0 || (qh->GOODvertex > 0 && qh->MERGING)) {
+    FORALLfacet_(facetlist) {
+      if (facet->good && ((qh->GOODvertex > 0) ^ !!qh_isvertex(qh->GOODvertexp, facet->vertices))) {
+        if (!--numgood) {
+          if (qh->ONLYgood) {
+            qh_fprintf(qh, qh->ferr, 7064, "qhull warning: good vertex p%d does not match last good facet f%d.  Ignored.\n",
+               qh_pointid(qh, qh->GOODvertexp), facet->id);
+            return;
+          }else if (qh->GOODvertex > 0)
+            qh_fprintf(qh, qh->ferr, 7065, "qhull warning: point p%d is not a vertex('QV%d').\n",
+                qh->GOODvertex-1, qh->GOODvertex-1);
+          else
+            qh_fprintf(qh, qh->ferr, 7066, "qhull warning: point p%d is a vertex for every facet('QV-%d').\n",
+                -qh->GOODvertex - 1, -qh->GOODvertex - 1);
+        }
+        facet->good= False;
+      }
+    }
+  }
+  startgood= numgood;
+  if (qh->SPLITthresholds) {
+    FORALLfacet_(facetlist) {
+      if (facet->good) {
+        if (!qh_inthresholds(qh, facet->normal, &angle)) {
+          facet->good= False;
+          numgood--;
+          if (angle < bestangle) {
+            bestangle= angle;
+            bestfacet= facet;
+          }
+        }
+      }
+    }
+    if (!numgood && bestfacet) {
+      bestfacet->good= True;
+      numgood++;
+      trace0((qh, qh->ferr, 23, "qh_findgood_all: f%d is closest(%2.2g) to thresholds\n",
+           bestfacet->id, bestangle));
+      return;
+    }
+  }
+  qh->num_good= numgood;
+  trace0((qh, qh->ferr, 24, "qh_findgood_all: %d good facets remain out of %d facets\n",
+        numgood, startgood));
+} /* findgood_all */
+
+/*-<a                             href="qh-poly_r.htm#TOC"
+  >-------------------------------</a><a name="furthestnext">-</a>
+
+  qh_furthestnext()
+    set qh.facet_next to facet with furthest of all furthest points
+    searches all facets on qh.facet_list
+
+  notes:
+    this may help avoid precision problems
+*/
+void qh_furthestnext(qhT *qh /* qh->facet_list */) {
+  facetT *facet, *bestfacet= NULL;
+  realT dist, bestdist= -REALmax;
+
+  FORALLfacets {
+    if (facet->outsideset) {
+#if qh_COMPUTEfurthest
+      pointT *furthest;
+      furthest= (pointT*)qh_setlast(facet->outsideset);
+      zinc_(Zcomputefurthest);
+      qh_distplane(qh, furthest, facet, &dist);
+#else
+      dist= facet->furthestdist;
+#endif
+      if (dist > bestdist) {
+        bestfacet= facet;
+        bestdist= dist;
+      }
+    }
+  }
+  if (bestfacet) {
+    qh_removefacet(qh, bestfacet);
+    qh_prependfacet(qh, bestfacet, &qh->facet_next);
+    trace1((qh, qh->ferr, 1029, "qh_furthestnext: made f%d next facet(dist %.2g)\n",
+            bestfacet->id, bestdist));
+  }
+} /* furthestnext */
+
+/*-<a                             href="qh-poly_r.htm#TOC"
+  >-------------------------------</a><a name="furthestout">-</a>
+
+  qh_furthestout(qh, facet )
+    make furthest outside point the last point of outsideset
+
+  returns:
+    updates facet->outsideset
+    clears facet->notfurthest
+    sets facet->furthestdist
+
+  design:
+    determine best point of outsideset
+    make it the last point of outsideset
+*/
+void qh_furthestout(qhT *qh, facetT *facet) {
+  pointT *point, **pointp, *bestpoint= NULL;
+  realT dist, bestdist= -REALmax;
+
+  FOREACHpoint_(facet->outsideset) {
+    qh_distplane(qh, point, facet, &dist);
+    zinc_(Zcomputefurthest);
+    if (dist > bestdist) {
+      bestpoint= point;
+      bestdist= dist;
+    }
+  }
+  if (bestpoint) {
+    qh_setdel(facet->outsideset, point);
+    qh_setappend(qh, &facet->outsideset, point);
+#if !qh_COMPUTEfurthest
+    facet->furthestdist= bestdist;
+#endif
+  }
+  facet->notfurthest= False;
+  trace3((qh, qh->ferr, 3017, "qh_furthestout: p%d is furthest outside point of f%d\n",
+          qh_pointid(qh, point), facet->id));
+} /* furthestout */
+
+
+/*-<a                             href="qh-qhull_r.htm#TOC"
+  >-------------------------------</a><a name="infiniteloop">-</a>
+
+  qh_infiniteloop(qh, facet )
+    report infinite loop error due to facet
+*/
+void qh_infiniteloop(qhT *qh, facetT *facet) {
+
+  qh_fprintf(qh, qh->ferr, 6149, "qhull internal error (qh_infiniteloop): potential infinite loop detected\n");
+  qh_errexit(qh, qh_ERRqhull, facet, NULL);
+} /* qh_infiniteloop */
+
+/*-<a                             href="qh-poly_r.htm#TOC"
+  >-------------------------------</a><a name="initbuild">-</a>
+
+  qh_initbuild()
+    initialize hull and outside sets with point array
+    qh.FIRSTpoint/qh.NUMpoints is point array
+    if qh.GOODpoint
+      adds qh.GOODpoint to initial hull
+
+  returns:
+    qh_facetlist with initial hull
+    points partioned into outside sets, coplanar sets, or inside
+    initializes qh.GOODpointp, qh.GOODvertexp,
+
+  design:
+    initialize global variables used during qh_buildhull
+    determine precision constants and points with max/min coordinate values
+      if qh.SCALElast, scale last coordinate(for 'd')
+    build initial simplex
+    partition input points into facets of initial simplex
+    set up lists
+    if qh.ONLYgood
+      check consistency
+      add qh.GOODvertex if defined
+*/
+void qh_initbuild(qhT *qh) {
+  setT *maxpoints, *vertices;
+  facetT *facet;
+  int i, numpart;
+  realT dist;
+  boolT isoutside;
+
+  qh->furthest_id= qh_IDunknown;
+  qh->lastreport= 0;
+  qh->facet_id= qh->vertex_id= qh->ridge_id= 0;
+  qh->visit_id= qh->vertex_visit= 0;
+  qh->maxoutdone= False;
+
+  if (qh->GOODpoint > 0)
+    qh->GOODpointp= qh_point(qh, qh->GOODpoint-1);
+  else if (qh->GOODpoint < 0)
+    qh->GOODpointp= qh_point(qh, -qh->GOODpoint-1);
+  if (qh->GOODvertex > 0)
+    qh->GOODvertexp= qh_point(qh, qh->GOODvertex-1);
+  else if (qh->GOODvertex < 0)
+    qh->GOODvertexp= qh_point(qh, -qh->GOODvertex-1);
+  if ((qh->GOODpoint
+       && (qh->GOODpointp < qh->first_point  /* also catches !GOODpointp */
+           || qh->GOODpointp > qh_point(qh, qh->num_points-1)))
+    || (qh->GOODvertex
+        && (qh->GOODvertexp < qh->first_point  /* also catches !GOODvertexp */
+            || qh->GOODvertexp > qh_point(qh, qh->num_points-1)))) {
+    qh_fprintf(qh, qh->ferr, 6150, "qhull input error: either QGn or QVn point is > p%d\n",
+             qh->num_points-1);
+    qh_errexit(qh, qh_ERRinput, NULL, NULL);
+  }
+  maxpoints= qh_maxmin(qh, qh->first_point, qh->num_points, qh->hull_dim);
+  if (qh->SCALElast)
+    qh_scalelast(qh, qh->first_point, qh->num_points, qh->hull_dim,
+               qh->MINlastcoord, qh->MAXlastcoord, qh->MAXwidth);
+  qh_detroundoff(qh);
+  if (qh->DELAUNAY && qh->upper_threshold[qh->hull_dim-1] > REALmax/2
+                  && qh->lower_threshold[qh->hull_dim-1] < -REALmax/2) {
+    for (i=qh_PRINTEND; i--; ) {
+      if (qh->PRINTout[i] == qh_PRINTgeom && qh->DROPdim < 0
+          && !qh->GOODthreshold && !qh->SPLITthresholds)
+        break;  /* in this case, don't set upper_threshold */
+    }
+    if (i < 0) {
+      if (qh->UPPERdelaunay) { /* matches qh.upperdelaunay in qh_setfacetplane */
+        qh->lower_threshold[qh->hull_dim-1]= qh->ANGLEround * qh_ZEROdelaunay;
+        qh->GOODthreshold= True;
+      }else {
+        qh->upper_threshold[qh->hull_dim-1]= -qh->ANGLEround * qh_ZEROdelaunay;
+        if (!qh->GOODthreshold)
+          qh->SPLITthresholds= True; /* build upper-convex hull even if Qg */
+          /* qh_initqhull_globals errors if Qg without Pdk/etc. */
+      }
+    }
+  }
+  vertices= qh_initialvertices(qh, qh->hull_dim, maxpoints, qh->first_point, qh->num_points);
+  qh_initialhull(qh, vertices);  /* initial qh->facet_list */
+  qh_partitionall(qh, vertices, qh->first_point, qh->num_points);
+  if (qh->PRINToptions1st || qh->TRACElevel || qh->IStracing) {
+    if (qh->TRACElevel || qh->IStracing)
+      qh_fprintf(qh, qh->ferr, 8103, "\nTrace level %d for %s | %s\n",
+         qh->IStracing ? qh->IStracing : qh->TRACElevel, qh->rbox_command, qh->qhull_command);
+    qh_fprintf(qh, qh->ferr, 8104, "Options selected for Qhull %s:\n%s\n", qh_version, qh->qhull_options);
+  }
+  qh_resetlists(qh, False, qh_RESETvisible /*qh.visible_list newvertex_list newfacet_list */);
+  qh->facet_next= qh->facet_list;
+  qh_furthestnext(qh /* qh->facet_list */);
+  if (qh->PREmerge) {
+    qh->cos_max= qh->premerge_cos;
+    qh->centrum_radius= qh->premerge_centrum;
+  }
+  if (qh->ONLYgood) {
+    if (qh->GOODvertex > 0 && qh->MERGING) {
+      qh_fprintf(qh, qh->ferr, 6151, "qhull input error: 'Qg QVn' (only good vertex) does not work with merging.\nUse 'QJ' to joggle the input or 'Q0' to turn off merging.\n");
+      qh_errexit(qh, qh_ERRinput, NULL, NULL);
+    }
+    if (!(qh->GOODthreshold || qh->GOODpoint
+         || (!qh->MERGEexact && !qh->PREmerge && qh->GOODvertexp))) {
+      qh_fprintf(qh, qh->ferr, 6152, "qhull input error: 'Qg' (ONLYgood) needs a good threshold('Pd0D0'), a\n\
+good point(QGn or QG-n), or a good vertex with 'QJ' or 'Q0' (QVn).\n");
+      qh_errexit(qh, qh_ERRinput, NULL, NULL);
+    }
+    if (qh->GOODvertex > 0  && !qh->MERGING  /* matches qh_partitionall */
+        && !qh_isvertex(qh->GOODvertexp, vertices)) {
+      facet= qh_findbestnew(qh, qh->GOODvertexp, qh->facet_list,
+                          &dist, !qh_ALL, &isoutside, &numpart);
+      zadd_(Zdistgood, numpart);
+      if (!isoutside) {
+        qh_fprintf(qh, qh->ferr, 6153, "qhull input error: point for QV%d is inside initial simplex.  It can not be made a vertex.\n",
+               qh_pointid(qh, qh->GOODvertexp));
+        qh_errexit(qh, qh_ERRinput, NULL, NULL);
+      }
+      if (!qh_addpoint(qh, qh->GOODvertexp, facet, False)) {
+        qh_settempfree(qh, &vertices);
+        qh_settempfree(qh, &maxpoints);
+        return;
+      }
+    }
+    qh_findgood(qh, qh->facet_list, 0);
+  }
+  qh_settempfree(qh, &vertices);
+  qh_settempfree(qh, &maxpoints);
+  trace1((qh, qh->ferr, 1030, "qh_initbuild: initial hull created and points partitioned\n"));
+} /* initbuild */
+
+/*-<a                             href="qh-poly_r.htm#TOC"
+  >-------------------------------</a><a name="initialhull">-</a>
+
+  qh_initialhull(qh, vertices )
+    constructs the initial hull as a DIM3 simplex of vertices
+
+  design:
+    creates a simplex (initializes lists)
+    determines orientation of simplex
+    sets hyperplanes for facets
+    doubles checks orientation (in case of axis-parallel facets with Gaussian elimination)
+    checks for flipped facets and qh.NARROWhull
+    checks the result
+*/
+void qh_initialhull(qhT *qh, setT *vertices) {
+  facetT *facet, *firstfacet, *neighbor, **neighborp;
+  realT dist, angle, minangle= REALmax;
+#ifndef qh_NOtrace
+  int k;
+#endif
+
+  qh_createsimplex(qh, vertices);  /* qh->facet_list */
+  qh_resetlists(qh, False, qh_RESETvisible);
+  qh->facet_next= qh->facet_list;      /* advance facet when processed */
+  qh->interior_point= qh_getcenter(qh, vertices);
+  firstfacet= qh->facet_list;
+  qh_setfacetplane(qh, firstfacet);
+  zinc_(Znumvisibility); /* needs to be in printsummary */
+  qh_distplane(qh, qh->interior_point, firstfacet, &dist);
+  if (dist > 0) {
+    FORALLfacets
+      facet->toporient ^= (unsigned char)True;
+  }
+  FORALLfacets
+    qh_setfacetplane(qh, facet);
+  FORALLfacets {
+    if (!qh_checkflipped(qh, facet, NULL, qh_ALL)) {/* due to axis-parallel facet */
+      trace1((qh, qh->ferr, 1031, "qh_initialhull: initial orientation incorrect.  Correct all facets\n"));
+      facet->flipped= False;
+      FORALLfacets {
+        facet->toporient ^= (unsigned char)True;
+        qh_orientoutside(qh, facet);
+      }
+      break;
+    }
+  }
+  FORALLfacets {
+    if (!qh_checkflipped(qh, facet, NULL, !qh_ALL)) {  /* can happen with 'R0.1' */
+      if (qh->DELAUNAY && ! qh->ATinfinity) {
+        if (qh->UPPERdelaunay)
+          qh_fprintf(qh, qh->ferr, 6240, "Qhull precision error: Initial simplex is cocircular or cospherical.  Option 'Qs' searches all points.  Can not compute the upper Delaunay triangulation or upper Voronoi diagram of cocircular/cospherical points.\n");
+        else
+          qh_fprintf(qh, qh->ferr, 6239, "Qhull precision error: Initial simplex is cocircular or cospherical.  Use option 'Qz' for the Delaunay triangulation or Voronoi diagram of cocircular/cospherical points.  Option 'Qz' adds a point \"at infinity\".  Use option 'Qs' to search all points for the initial simplex.\n");
+        qh_errexit(qh, qh_ERRinput, NULL, NULL);
+      }
+      qh_precision(qh, "initial simplex is flat");
+      qh_fprintf(qh, qh->ferr, 6154, "Qhull precision error: Initial simplex is flat (facet %d is coplanar with the interior point)\n",
+                   facet->id);
+      qh_errexit(qh, qh_ERRsingular, NULL, NULL);  /* calls qh_printhelp_singular */
+    }
+    FOREACHneighbor_(facet) {
+      angle= qh_getangle(qh, facet->normal, neighbor->normal);
+      minimize_( minangle, angle);
+    }
+  }
+  if (minangle < qh_MAXnarrow && !qh->NOnarrow) {
+    realT diff= 1.0 + minangle;
+
+    qh->NARROWhull= True;
+    qh_option(qh, "_narrow-hull", NULL, &diff);
+    if (minangle < qh_WARNnarrow && !qh->RERUN && qh->PRINTprecision)
+      qh_printhelp_narrowhull(qh, qh->ferr, minangle);
+  }
+  zzval_(Zprocessed)= qh->hull_dim+1;
+  qh_checkpolygon(qh, qh->facet_list);
+  qh_checkconvex(qh, qh->facet_list,   qh_DATAfault);
+#ifndef qh_NOtrace
+  if (qh->IStracing >= 1) {
+    qh_fprintf(qh, qh->ferr, 8105, "qh_initialhull: simplex constructed, interior point:");
+    for (k=0; k < qh->hull_dim; k++)
+      qh_fprintf(qh, qh->ferr, 8106, " %6.4g", qh->interior_point[k]);
+    qh_fprintf(qh, qh->ferr, 8107, "\n");
+  }
+#endif
+} /* initialhull */
+
+/*-<a                             href="qh-poly_r.htm#TOC"
+  >-------------------------------</a><a name="initialvertices">-</a>
+
+  qh_initialvertices(qh, dim, maxpoints, points, numpoints )
+    determines a non-singular set of initial vertices
+    maxpoints may include duplicate points
+
+  returns:
+    temporary set of dim+1 vertices in descending order by vertex id
+    if qh.RANDOMoutside && !qh.ALLpoints
+      picks random points
+    if dim >= qh_INITIALmax,
+      uses min/max x and max points with non-zero determinants
+
+  notes:
+    unless qh.ALLpoints,
+      uses maxpoints as long as determinate is non-zero
+*/
+setT *qh_initialvertices(qhT *qh, int dim, setT *maxpoints, pointT *points, int numpoints) {
+  pointT *point, **pointp;
+  setT *vertices, *simplex, *tested;
+  realT randr;
+  int idx, point_i, point_n, k;
+  boolT nearzero= False;
+
+  vertices= qh_settemp(qh, dim + 1);
+  simplex= qh_settemp(qh, dim+1);
+  if (qh->ALLpoints)
+    qh_maxsimplex(qh, dim, NULL, points, numpoints, &simplex);
+  else if (qh->RANDOMoutside) {
+    while (qh_setsize(qh, simplex) != dim+1) {
+      randr= qh_RANDOMint;
+      randr= randr/(qh_RANDOMmax+1);
+      idx= (int)floor(qh->num_points * randr);
+      while (qh_setin(simplex, qh_point(qh, idx))) {
+            idx++; /* in case qh_RANDOMint always returns the same value */
+        idx= idx < qh->num_points ? idx : 0;
+      }
+      qh_setappend(qh, &simplex, qh_point(qh, idx));
+    }
+  }else if (qh->hull_dim >= qh_INITIALmax) {
+    tested= qh_settemp(qh, dim+1);
+    qh_setappend(qh, &simplex, SETfirst_(maxpoints));   /* max and min X coord */
+    qh_setappend(qh, &simplex, SETsecond_(maxpoints));
+    qh_maxsimplex(qh, fmin_(qh_INITIALsearch, dim), maxpoints, points, numpoints, &simplex);
+    k= qh_setsize(qh, simplex);
+    FOREACHpoint_i_(qh, maxpoints) {
+      if (point_i & 0x1) {     /* first pick up max. coord. points */
+        if (!qh_setin(simplex, point) && !qh_setin(tested, point)){
+          qh_detsimplex(qh, point, simplex, k, &nearzero);
+          if (nearzero)
+            qh_setappend(qh, &tested, point);
+          else {
+            qh_setappend(qh, &simplex, point);
+            if (++k == dim)  /* use search for last point */
+              break;
+          }
+        }
+      }
+    }
+    while (k != dim && (point= (pointT*)qh_setdellast(maxpoints))) {
+      if (!qh_setin(simplex, point) && !qh_setin(tested, point)){
+        qh_detsimplex(qh, point, simplex, k, &nearzero);
+        if (nearzero)
+          qh_setappend(qh, &tested, point);
+        else {
+          qh_setappend(qh, &simplex, point);
+          k++;
+        }
+      }
+    }
+    idx= 0;
+    while (k != dim && (point= qh_point(qh, idx++))) {
+      if (!qh_setin(simplex, point) && !qh_setin(tested, point)){
+        qh_detsimplex(qh, point, simplex, k, &nearzero);
+        if (!nearzero){
+          qh_setappend(qh, &simplex, point);
+          k++;
+        }
+      }
+    }
+    qh_settempfree(qh, &tested);
+    qh_maxsimplex(qh, dim, maxpoints, points, numpoints, &simplex);
+  }else
+    qh_maxsimplex(qh, dim, maxpoints, points, numpoints, &simplex);
+  FOREACHpoint_(simplex)
+    qh_setaddnth(qh, &vertices, 0, qh_newvertex(qh, point)); /* descending order */
+  qh_settempfree(qh, &simplex);
+  return vertices;
+} /* initialvertices */
+
+
+/*-<a                             href="qh-poly_r.htm#TOC"
+  >-------------------------------</a><a name="isvertex">-</a>
+
+  qh_isvertex( point, vertices )
+    returns vertex if point is in vertex set, else returns NULL
+
+  notes:
+    for qh.GOODvertex
+*/
+vertexT *qh_isvertex(pointT *point, setT *vertices) {
+  vertexT *vertex, **vertexp;
+
+  FOREACHvertex_(vertices) {
+    if (vertex->point == point)
+      return vertex;
+  }
+  return NULL;
+} /* isvertex */
+
+/*-<a                             href="qh-poly_r.htm#TOC"
+  >-------------------------------</a><a name="makenewfacets">-</a>
+
+  qh_makenewfacets(qh, point )
+    make new facets from point and qh.visible_list
+
+  returns:
+    qh.newfacet_list= list of new facets with hyperplanes and ->newfacet
+    qh.newvertex_list= list of vertices in new facets with ->newlist set
+
+    if (qh.ONLYgood)
+      newfacets reference horizon facets, but not vice versa
+      ridges reference non-simplicial horizon ridges, but not vice versa
+      does not change existing facets
+    else
+      sets qh.NEWfacets
+      new facets attached to horizon facets and ridges
+      for visible facets,
+        visible->r.replace is corresponding new facet
+
+  see also:
+    qh_makenewplanes() -- make hyperplanes for facets
+    qh_attachnewfacets() -- attachnewfacets if not done here(qh->ONLYgood)
+    qh_matchnewfacets() -- match up neighbors
+    qh_updatevertices() -- update vertex neighbors and delvertices
+    qh_deletevisible() -- delete visible facets
+    qh_checkpolygon() --check the result
+    qh_triangulate() -- triangulate a non-simplicial facet
+
+  design:
+    for each visible facet
+      make new facets to its horizon facets
+      update its f.replace
+      clear its neighbor set
+*/
+vertexT *qh_makenewfacets(qhT *qh, pointT *point /*visible_list*/) {
+  facetT *visible, *newfacet= NULL, *newfacet2= NULL, *neighbor, **neighborp;
+  vertexT *apex;
+  int numnew=0;
+
+  qh->newfacet_list= qh->facet_tail;
+  qh->newvertex_list= qh->vertex_tail;
+  apex= qh_newvertex(qh, point);
+  qh_appendvertex(qh, apex);
+  qh->visit_id++;
+  if (!qh->ONLYgood)
+    qh->NEWfacets= True;
+  FORALLvisible_facets {
+    FOREACHneighbor_(visible)
+      neighbor->seen= False;
+    if (visible->ridges) {
+      visible->visitid= qh->visit_id;
+      newfacet2= qh_makenew_nonsimplicial(qh, visible, apex, &numnew);
+    }
+    if (visible->simplicial)
+      newfacet= qh_makenew_simplicial(qh, visible, apex, &numnew);
+    if (!qh->ONLYgood) {
+      if (newfacet2)  /* newfacet is null if all ridges defined */
+        newfacet= newfacet2;
+      if (newfacet)
+        visible->f.replace= newfacet;
+      else
+        zinc_(Zinsidevisible);
+      SETfirst_(visible->neighbors)= NULL;
+    }
+  }
+  trace1((qh, qh->ferr, 1032, "qh_makenewfacets: created %d new facets from point p%d to horizon\n",
+          numnew, qh_pointid(qh, point)));
+  if (qh->IStracing >= 4)
+    qh_printfacetlist(qh, qh->newfacet_list, NULL, qh_ALL);
+  return apex;
+} /* makenewfacets */
+
+/*-<a                             href="qh-poly_r.htm#TOC"
+  >-------------------------------</a><a name="matchduplicates">-</a>
+
+  qh_matchduplicates(qh, atfacet, atskip, hashsize, hashcount )
+    match duplicate ridges in qh.hash_table for atfacet/atskip
+    duplicates marked with ->dupridge and qh_DUPLICATEridge
+
+  returns:
+    picks match with worst merge (min distance apart)
+    updates hashcount
+
+  see also:
+    qh_matchneighbor
+
+  notes:
+
+  design:
+    compute hash value for atfacet and atskip
+    repeat twice -- once to make best matches, once to match the rest
+      for each possible facet in qh.hash_table
+        if it is a matching facet and pass 2
+          make match
+          unless tricoplanar, mark match for merging (qh_MERGEridge)
+          [e.g., tricoplanar RBOX s 1000 t993602376 | QHULL C-1e-3 d Qbb FA Qt]
+        if it is a matching facet and pass 1
+          test if this is a better match
+      if pass 1,
+        make best match (it will not be merged)
+*/
+#ifndef qh_NOmerge
+void qh_matchduplicates(qhT *qh, facetT *atfacet, int atskip, int hashsize, int *hashcount) {
+  boolT same, ismatch;
+  int hash, scan;
+  facetT *facet, *newfacet, *maxmatch= NULL, *maxmatch2= NULL, *nextfacet;
+  int skip, newskip, nextskip= 0, maxskip= 0, maxskip2= 0, makematch;
+  realT maxdist= -REALmax, mindist, dist2, low, high;
+
+  hash= qh_gethash(qh, hashsize, atfacet->vertices, qh->hull_dim, 1,
+                     SETelem_(atfacet->vertices, atskip));
+  trace2((qh, qh->ferr, 2046, "qh_matchduplicates: find duplicate matches for f%d skip %d hash %d hashcount %d\n",
+          atfacet->id, atskip, hash, *hashcount));
+  for (makematch= 0; makematch < 2; makematch++) {
+    qh->visit_id++;
+    for (newfacet= atfacet, newskip= atskip; newfacet; newfacet= nextfacet, newskip= nextskip) {
+      zinc_(Zhashlookup);
+      nextfacet= NULL;
+      newfacet->visitid= qh->visit_id;
+      for (scan= hash; (facet= SETelemt_(qh->hash_table, scan, facetT));
+           scan= (++scan >= hashsize ? 0 : scan)) {
+        if (!facet->dupridge || facet->visitid == qh->visit_id)
+          continue;
+        zinc_(Zhashtests);
+        if (qh_matchvertices(qh, 1, newfacet->vertices, newskip, facet->vertices, &skip, &same)) {
+          ismatch= (same == (boolT)(newfacet->toporient ^ facet->toporient));
+          if (SETelemt_(facet->neighbors, skip, facetT) != qh_DUPLICATEridge) {
+            if (!makematch) {
+              qh_fprintf(qh, qh->ferr, 6155, "qhull internal error (qh_matchduplicates): missing dupridge at f%d skip %d for new f%d skip %d hash %d\n",
+                     facet->id, skip, newfacet->id, newskip, hash);
+              qh_errexit2(qh, qh_ERRqhull, facet, newfacet);
+            }
+          }else if (ismatch && makematch) {
+            if (SETelemt_(newfacet->neighbors, newskip, facetT) == qh_DUPLICATEridge) {
+              SETelem_(facet->neighbors, skip)= newfacet;
+              if (newfacet->tricoplanar)
+                SETelem_(newfacet->neighbors, newskip)= facet;
+              else
+                SETelem_(newfacet->neighbors, newskip)= qh_MERGEridge;
+              *hashcount -= 2; /* removed two unmatched facets */
+              trace4((qh, qh->ferr, 4059, "qh_matchduplicates: duplicate f%d skip %d matched with new f%d skip %d merge\n",
+                    facet->id, skip, newfacet->id, newskip));
+            }
+          }else if (ismatch) {
+            mindist= qh_getdistance(qh, facet, newfacet, &low, &high);
+            dist2= qh_getdistance(qh, newfacet, facet, &low, &high);
+            minimize_(mindist, dist2);
+            if (mindist > maxdist) {
+              maxdist= mindist;
+              maxmatch= facet;
+              maxskip= skip;
+              maxmatch2= newfacet;
+              maxskip2= newskip;
+            }
+            trace3((qh, qh->ferr, 3018, "qh_matchduplicates: duplicate f%d skip %d new f%d skip %d at dist %2.2g, max is now f%d f%d\n",
+                    facet->id, skip, newfacet->id, newskip, mindist,
+                    maxmatch->id, maxmatch2->id));
+          }else { /* !ismatch */
+            nextfacet= facet;
+            nextskip= skip;
+          }
+        }
+        if (makematch && !facet
+        && SETelemt_(facet->neighbors, skip, facetT) == qh_DUPLICATEridge) {
+          qh_fprintf(qh, qh->ferr, 6156, "qhull internal error (qh_matchduplicates): no MERGEridge match for duplicate f%d skip %d at hash %d\n",
+                     newfacet->id, newskip, hash);
+          qh_errexit(qh, qh_ERRqhull, newfacet, NULL);
+        }
+      }
+    } /* end of for each new facet at hash */
+    if (!makematch) {
+      if (!maxmatch) {
+        qh_fprintf(qh, qh->ferr, 6157, "qhull internal error (qh_matchduplicates): no maximum match at duplicate f%d skip %d at hash %d\n",
+                     atfacet->id, atskip, hash);
+        qh_errexit(qh, qh_ERRqhull, atfacet, NULL);
+      }
+      SETelem_(maxmatch->neighbors, maxskip)= maxmatch2; /* maxmatch!=0 by QH6157 */
+      SETelem_(maxmatch2->neighbors, maxskip2)= maxmatch;
+      *hashcount -= 2; /* removed two unmatched facets */
+      zzinc_(Zmultiridge);
+      trace0((qh, qh->ferr, 25, "qh_matchduplicates: duplicate f%d skip %d matched with new f%d skip %d keep\n",
+              maxmatch->id, maxskip, maxmatch2->id, maxskip2));
+      qh_precision(qh, "ridge with multiple neighbors");
+      if (qh->IStracing >= 4)
+        qh_errprint(qh, "DUPLICATED/MATCH", maxmatch, maxmatch2, NULL, NULL);
+    }
+  }
+} /* matchduplicates */
+
+/*-<a                             href="qh-poly_r.htm#TOC"
+  >-------------------------------</a><a name="nearcoplanar">-</a>
+
+  qh_nearcoplanar()
+    for all facets, remove near-inside points from facet->coplanarset</li>
+    coplanar points defined by innerplane from qh_outerinner()
+
+  returns:
+    if qh->KEEPcoplanar && !qh->KEEPinside
+      facet->coplanarset only contains coplanar points
+    if qh.JOGGLEmax
+      drops inner plane by another qh.JOGGLEmax diagonal since a
+        vertex could shift out while a coplanar point shifts in
+
+  notes:
+    used for qh.PREmerge and qh.JOGGLEmax
+    must agree with computation of qh.NEARcoplanar in qh_detroundoff(qh)
+  design:
+    if not keeping coplanar or inside points
+      free all coplanar sets
+    else if not keeping both coplanar and inside points
+      remove !coplanar or !inside points from coplanar sets
+*/
+void qh_nearcoplanar(qhT *qh /* qh.facet_list */) {
+  facetT *facet;
+  pointT *point, **pointp;
+  int numpart;
+  realT dist, innerplane;
+
+  if (!qh->KEEPcoplanar && !qh->KEEPinside) {
+    FORALLfacets {
+      if (facet->coplanarset)
+        qh_setfree(qh, &facet->coplanarset);
+    }
+  }else if (!qh->KEEPcoplanar || !qh->KEEPinside) {
+    qh_outerinner(qh, NULL, NULL, &innerplane);
+    if (qh->JOGGLEmax < REALmax/2)
+      innerplane -= qh->JOGGLEmax * sqrt((realT)qh->hull_dim);
+    numpart= 0;
+    FORALLfacets {
+      if (facet->coplanarset) {
+        FOREACHpoint_(facet->coplanarset) {
+          numpart++;
+          qh_distplane(qh, point, facet, &dist);
+          if (dist < innerplane) {
+            if (!qh->KEEPinside)
+              SETref_(point)= NULL;
+          }else if (!qh->KEEPcoplanar)
+            SETref_(point)= NULL;
+        }
+        qh_setcompact(qh, facet->coplanarset);
+      }
+    }
+    zzadd_(Zcheckpart, numpart);
+  }
+} /* nearcoplanar */
+
+/*-<a                             href="qh-poly_r.htm#TOC"
+  >-------------------------------</a><a name="nearvertex">-</a>
+
+  qh_nearvertex(qh, facet, point, bestdist )
+    return nearest vertex in facet to point
+
+  returns:
+    vertex and its distance
+
+  notes:
+    if qh.DELAUNAY
+      distance is measured in the input set
+    searches neighboring tricoplanar facets (requires vertexneighbors)
+      Slow implementation.  Recomputes vertex set for each point.
+    The vertex set could be stored in the qh.keepcentrum facet.
+*/
+vertexT *qh_nearvertex(qhT *qh, facetT *facet, pointT *point, realT *bestdistp) {
+  realT bestdist= REALmax, dist;
+  vertexT *bestvertex= NULL, *vertex, **vertexp, *apex;
+  coordT *center;
+  facetT *neighbor, **neighborp;
+  setT *vertices;
+  int dim= qh->hull_dim;
+
+  if (qh->DELAUNAY)
+    dim--;
+  if (facet->tricoplanar) {
+    if (!qh->VERTEXneighbors || !facet->center) {
+      qh_fprintf(qh, qh->ferr, 6158, "qhull internal error (qh_nearvertex): qh.VERTEXneighbors and facet->center required for tricoplanar facets\n");
+      qh_errexit(qh, qh_ERRqhull, facet, NULL);
+    }
+    vertices= qh_settemp(qh, qh->TEMPsize);
+    apex= SETfirstt_(facet->vertices, vertexT);
+    center= facet->center;
+    FOREACHneighbor_(apex) {
+      if (neighbor->center == center) {
+        FOREACHvertex_(neighbor->vertices)
+          qh_setappend(qh, &vertices, vertex);
+      }
+    }
+  }else
+    vertices= facet->vertices;
+  FOREACHvertex_(vertices) {
+    dist= qh_pointdist(vertex->point, point, -dim);
+    if (dist < bestdist) {
+      bestdist= dist;
+      bestvertex= vertex;
+    }
+  }
+  if (facet->tricoplanar)
+    qh_settempfree(qh, &vertices);
+  *bestdistp= sqrt(bestdist);
+  if (!bestvertex) {
+      qh_fprintf(qh, qh->ferr, 6261, "qhull internal error (qh_nearvertex): did not find bestvertex for f%d p%d\n", facet->id, qh_pointid(qh, point));
+      qh_errexit(qh, qh_ERRqhull, facet, NULL);
+  }
+  trace3((qh, qh->ferr, 3019, "qh_nearvertex: v%d dist %2.2g for f%d p%d\n",
+        bestvertex->id, *bestdistp, facet->id, qh_pointid(qh, point))); /* bestvertex!=0 by QH2161 */
+  return bestvertex;
+} /* nearvertex */
+
+/*-<a                             href="qh-poly_r.htm#TOC"
+  >-------------------------------</a><a name="newhashtable">-</a>
+
+  qh_newhashtable(qh, newsize )
+    returns size of qh.hash_table of at least newsize slots
+
+  notes:
+    assumes qh.hash_table is NULL
+    qh_HASHfactor determines the number of extra slots
+    size is not divisible by 2, 3, or 5
+*/
+int qh_newhashtable(qhT *qh, int newsize) {
+  int size;
+
+  size= ((newsize+1)*qh_HASHfactor) | 0x1;  /* odd number */
+  while (True) {
+    if (newsize<0 || size<0) {
+        qh_fprintf(qh, qh->qhmem.ferr, 6236, "qhull error (qh_newhashtable): negative request (%d) or size (%d).  Did int overflow due to high-D?\n", newsize, size); /* WARN64 */
+        qh_errexit(qh, qhmem_ERRmem, NULL, NULL);
+    }
+    if ((size%3) && (size%5))
+      break;
+    size += 2;
+    /* loop terminates because there is an infinite number of primes */
+  }
+  qh->hash_table= qh_setnew(qh, size);
+  qh_setzero(qh, qh->hash_table, 0, size);
+  return size;
+} /* newhashtable */
+
+/*-<a                             href="qh-poly_r.htm#TOC"
+  >-------------------------------</a><a name="newvertex">-</a>
+
+  qh_newvertex(qh, point )
+    returns a new vertex for point
+*/
+vertexT *qh_newvertex(qhT *qh, pointT *point) {
+  vertexT *vertex;
+
+  zinc_(Ztotvertices);
+  vertex= (vertexT *)qh_memalloc(qh, (int)sizeof(vertexT));
+  memset((char *) vertex, (size_t)0, sizeof(vertexT));
+  if (qh->vertex_id == UINT_MAX) {
+    qh_memfree(qh, vertex, (int)sizeof(vertexT));
+    qh_fprintf(qh, qh->ferr, 6159, "qhull error: more than 2^32 vertices.  vertexT.id field overflows.  Vertices would not be sorted correctly.\n");
+    qh_errexit(qh, qh_ERRqhull, NULL, NULL);
+  }
+  if (qh->vertex_id == qh->tracevertex_id)
+    qh->tracevertex= vertex;
+  vertex->id= qh->vertex_id++;
+  vertex->point= point;
+  trace4((qh, qh->ferr, 4060, "qh_newvertex: vertex p%d(v%d) created\n", qh_pointid(qh, vertex->point),
+          vertex->id));
+  return(vertex);
+} /* newvertex */
+
+/*-<a                             href="qh-poly_r.htm#TOC"
+  >-------------------------------</a><a name="nextridge3d">-</a>
+
+  qh_nextridge3d( atridge, facet, vertex )
+    return next ridge and vertex for a 3d facet
+    returns NULL on error
+    [for QhullFacet::nextRidge3d] Does not call qh_errexit nor access qhT.
+
+  notes:
+    in qh_ORIENTclock order
+    this is a O(n^2) implementation to trace all ridges
+    be sure to stop on any 2nd visit
+    same as QhullRidge::nextRidge3d
+    does not use qhT or qh_errexit [QhullFacet.cpp]
+
+  design:
+    for each ridge
+      exit if it is the ridge after atridge
+*/
+ridgeT *qh_nextridge3d(ridgeT *atridge, facetT *facet, vertexT **vertexp) {
+  vertexT *atvertex, *vertex, *othervertex;
+  ridgeT *ridge, **ridgep;
+
+  if ((atridge->top == facet) ^ qh_ORIENTclock)
+    atvertex= SETsecondt_(atridge->vertices, vertexT);
+  else
+    atvertex= SETfirstt_(atridge->vertices, vertexT);
+  FOREACHridge_(facet->ridges) {
+    if (ridge == atridge)
+      continue;
+    if ((ridge->top == facet) ^ qh_ORIENTclock) {
+      othervertex= SETsecondt_(ridge->vertices, vertexT);
+      vertex= SETfirstt_(ridge->vertices, vertexT);
+    }else {
+      vertex= SETsecondt_(ridge->vertices, vertexT);
+      othervertex= SETfirstt_(ridge->vertices, vertexT);
+    }
+    if (vertex == atvertex) {
+      if (vertexp)
+        *vertexp= othervertex;
+      return ridge;
+    }
+  }
+  return NULL;
+} /* nextridge3d */
+#else /* qh_NOmerge */
+void qh_matchduplicates(qhT *qh, facetT *atfacet, int atskip, int hashsize, int *hashcount) {
+}
+ridgeT *qh_nextridge3d(ridgeT *atridge, facetT *facet, vertexT **vertexp) {
+
+  return NULL;
+}
+#endif /* qh_NOmerge */
+
+/*-<a                             href="qh-poly_r.htm#TOC"
+  >-------------------------------</a><a name="outcoplanar">-</a>
+
+  qh_outcoplanar()
+    move points from all facets' outsidesets to their coplanarsets
+
+  notes:
+    for post-processing under qh.NARROWhull
+
+  design:
+    for each facet
+      for each outside point for facet
+        partition point into coplanar set
+*/
+void qh_outcoplanar(qhT *qh /* facet_list */) {
+  pointT *point, **pointp;
+  facetT *facet;
+  realT dist;
+
+  trace1((qh, qh->ferr, 1033, "qh_outcoplanar: move outsideset to coplanarset for qh->NARROWhull\n"));
+  FORALLfacets {
+    FOREACHpoint_(facet->outsideset) {
+      qh->num_outside--;
+      if (qh->KEEPcoplanar || qh->KEEPnearinside) {
+        qh_distplane(qh, point, facet, &dist);
+        zinc_(Zpartition);
+        qh_partitioncoplanar(qh, point, facet, &dist);
+      }
+    }
+    qh_setfree(qh, &facet->outsideset);
+  }
+} /* outcoplanar */
+
+/*-<a                             href="qh-poly_r.htm#TOC"
+  >-------------------------------</a><a name="point">-</a>
+
+  qh_point(qh, id )
+    return point for a point id, or NULL if unknown
+
+  alternative code:
+    return((pointT *)((unsigned   long)qh.first_point
+           + (unsigned long)((id)*qh.normal_size)));
+*/
+pointT *qh_point(qhT *qh, int id) {
+
+  if (id < 0)
+    return NULL;
+  if (id < qh->num_points)
+    return qh->first_point + id * qh->hull_dim;
+  id -= qh->num_points;
+  if (id < qh_setsize(qh, qh->other_points))
+    return SETelemt_(qh->other_points, id, pointT);
+  return NULL;
+} /* point */
+
+/*-<a                             href="qh-poly_r.htm#TOC"
+  >-------------------------------</a><a name="point_add">-</a>
+
+  qh_point_add(qh, set, point, elem )
+    stores elem at set[point.id]
+
+  returns:
+    access function for qh_pointfacet and qh_pointvertex
+
+  notes:
+    checks point.id
+*/
+void qh_point_add(qhT *qh, setT *set, pointT *point, void *elem) {
+  int id, size;
+
+  SETreturnsize_(set, size);
+  if ((id= qh_pointid(qh, point)) < 0)
+    qh_fprintf(qh, qh->ferr, 7067, "qhull internal warning (point_add): unknown point %p id %d\n",
+      point, id);
+  else if (id >= size) {
+    qh_fprintf(qh, qh->ferr, 6160, "qhull internal errror(point_add): point p%d is out of bounds(%d)\n",
+             id, size);
+    qh_errexit(qh, qh_ERRqhull, NULL, NULL);
+  }else
+    SETelem_(set, id)= elem;
+} /* point_add */
+
+
+/*-<a                             href="qh-poly_r.htm#TOC"
+  >-------------------------------</a><a name="pointfacet">-</a>
+
+  qh_pointfacet()
+    return temporary set of facet for each point
+    the set is indexed by point id
+
+  notes:
+    vertices assigned to one of the facets
+    coplanarset assigned to the facet
+    outside set assigned to the facet
+    NULL if no facet for point (inside)
+      includes qh.GOODpointp
+
+  access:
+    FOREACHfacet_i_(qh, facets) { ... }
+    SETelem_(facets, i)
+
+  design:
+    for each facet
+      add each vertex
+      add each coplanar point
+      add each outside point
+*/
+setT *qh_pointfacet(qhT *qh /*qh.facet_list*/) {
+  int numpoints= qh->num_points + qh_setsize(qh, qh->other_points);
+  setT *facets;
+  facetT *facet;
+  vertexT *vertex, **vertexp;
+  pointT *point, **pointp;
+
+  facets= qh_settemp(qh, numpoints);
+  qh_setzero(qh, facets, 0, numpoints);
+  qh->vertex_visit++;
+  FORALLfacets {
+    FOREACHvertex_(facet->vertices) {
+      if (vertex->visitid != qh->vertex_visit) {
+        vertex->visitid= qh->vertex_visit;
+        qh_point_add(qh, facets, vertex->point, facet);
+      }
+    }
+    FOREACHpoint_(facet->coplanarset)
+      qh_point_add(qh, facets, point, facet);
+    FOREACHpoint_(facet->outsideset)
+      qh_point_add(qh, facets, point, facet);
+  }
+  return facets;
+} /* pointfacet */
+
+/*-<a                             href="qh-poly_r.htm#TOC"
+  >-------------------------------</a><a name="pointvertex">-</a>
+
+  qh_pointvertex(qh, )
+    return temporary set of vertices indexed by point id
+    entry is NULL if no vertex for a point
+      this will include qh.GOODpointp
+
+  access:
+    FOREACHvertex_i_(qh, vertices) { ... }
+    SETelem_(vertices, i)
+*/
+setT *qh_pointvertex(qhT *qh /*qh.facet_list*/) {
+  int numpoints= qh->num_points + qh_setsize(qh, qh->other_points);
+  setT *vertices;
+  vertexT *vertex;
+
+  vertices= qh_settemp(qh, numpoints);
+  qh_setzero(qh, vertices, 0, numpoints);
+  FORALLvertices
+    qh_point_add(qh, vertices, vertex->point, vertex);
+  return vertices;
+} /* pointvertex */
+
+
+/*-<a                             href="qh-poly_r.htm#TOC"
+  >-------------------------------</a><a name="prependfacet">-</a>
+
+  qh_prependfacet(qh, facet, facetlist )
+    prepend facet to the start of a facetlist
+
+  returns:
+    increments qh.numfacets
+    updates facetlist, qh.facet_list, facet_next
+
+  notes:
+    be careful of prepending since it can lose a pointer.
+      e.g., can lose _next by deleting and then prepending before _next
+*/
+void qh_prependfacet(qhT *qh, facetT *facet, facetT **facetlist) {
+  facetT *prevfacet, *list;
+
+
+  trace4((qh, qh->ferr, 4061, "qh_prependfacet: prepend f%d before f%d\n",
+          facet->id, getid_(*facetlist)));
+  if (!*facetlist)
+    (*facetlist)= qh->facet_tail;
+  list= *facetlist;
+  prevfacet= list->previous;
+  facet->previous= prevfacet;
+  if (prevfacet)
+    prevfacet->next= facet;
+  list->previous= facet;
+  facet->next= *facetlist;
+  if (qh->facet_list == list)  /* this may change *facetlist */
+    qh->facet_list= facet;
+  if (qh->facet_next == list)
+    qh->facet_next= facet;
+  *facetlist= facet;
+  qh->num_facets++;
+} /* prependfacet */
+
+
+/*-<a                             href="qh-poly_r.htm#TOC"
+  >-------------------------------</a><a name="printhashtable">-</a>
+
+  qh_printhashtable(qh, fp )
+    print hash table to fp
+
+  notes:
+    not in I/O to avoid bringing io_r.c in
+
+  design:
+    for each hash entry
+      if defined
+        if unmatched or will merge (NULL, qh_MERGEridge, qh_DUPLICATEridge)
+          print entry and neighbors
+*/
+void qh_printhashtable(qhT *qh, FILE *fp) {
+  facetT *facet, *neighbor;
+  int id, facet_i, facet_n, neighbor_i= 0, neighbor_n= 0;
+  vertexT *vertex, **vertexp;
+
+  FOREACHfacet_i_(qh, qh->hash_table) {
+    if (facet) {
+      FOREACHneighbor_i_(qh, facet) {
+        if (!neighbor || neighbor == qh_MERGEridge || neighbor == qh_DUPLICATEridge)
+          break;
+      }
+      if (neighbor_i == neighbor_n)
+        continue;
+      qh_fprintf(qh, fp, 9283, "hash %d f%d ", facet_i, facet->id);
+      FOREACHvertex_(facet->vertices)
+        qh_fprintf(qh, fp, 9284, "v%d ", vertex->id);
+      qh_fprintf(qh, fp, 9285, "\n neighbors:");
+      FOREACHneighbor_i_(qh, facet) {
+        if (neighbor == qh_MERGEridge)
+          id= -3;
+        else if (neighbor == qh_DUPLICATEridge)
+          id= -2;
+        else
+          id= getid_(neighbor);
+        qh_fprintf(qh, fp, 9286, " %d", id);
+      }
+      qh_fprintf(qh, fp, 9287, "\n");
+    }
+  }
+} /* printhashtable */
+
+
+/*-<a                             href="qh-poly_r.htm#TOC"
+  >-------------------------------</a><a name="printlists">-</a>
+
+  qh_printlists(qh, fp )
+    print out facet and vertex list for debugging (without 'f/v' tags)
+*/
+void qh_printlists(qhT *qh) {
+  facetT *facet;
+  vertexT *vertex;
+  int count= 0;
+
+  qh_fprintf(qh, qh->ferr, 8108, "qh_printlists: facets:");
+  FORALLfacets {
+    if (++count % 100 == 0)
+      qh_fprintf(qh, qh->ferr, 8109, "\n     ");
+    qh_fprintf(qh, qh->ferr, 8110, " %d", facet->id);
+  }
+  qh_fprintf(qh, qh->ferr, 8111, "\n  new facets %d visible facets %d next facet for qh_addpoint %d\n  vertices(new %d):",
+     getid_(qh->newfacet_list), getid_(qh->visible_list), getid_(qh->facet_next),
+     getid_(qh->newvertex_list));
+  count = 0;
+  FORALLvertices {
+    if (++count % 100 == 0)
+      qh_fprintf(qh, qh->ferr, 8112, "\n     ");
+    qh_fprintf(qh, qh->ferr, 8113, " %d", vertex->id);
+  }
+  qh_fprintf(qh, qh->ferr, 8114, "\n");
+} /* printlists */
+
+/*-<a                             href="qh-poly_r.htm#TOC"
+  >-------------------------------</a><a name="resetlists">-</a>
+
+  qh_resetlists(qh, stats, qh_RESETvisible )
+    reset newvertex_list, newfacet_list, visible_list
+    if stats,
+      maintains statistics
+
+  returns:
+    visible_list is empty if qh_deletevisible was called
+*/
+void qh_resetlists(qhT *qh, boolT stats, boolT resetVisible /*qh.newvertex_list newfacet_list visible_list*/) {
+  vertexT *vertex;
+  facetT *newfacet, *visible;
+  int totnew=0, totver=0;
+
+  if (stats) {
+    FORALLvertex_(qh->newvertex_list)
+      totver++;
+    FORALLnew_facets
+      totnew++;
+    zadd_(Zvisvertextot, totver);
+    zmax_(Zvisvertexmax, totver);
+    zadd_(Znewfacettot, totnew);
+    zmax_(Znewfacetmax, totnew);
+  }
+  FORALLvertex_(qh->newvertex_list)
+    vertex->newlist= False;
+  qh->newvertex_list= NULL;
+  FORALLnew_facets
+    newfacet->newfacet= False;
+  qh->newfacet_list= NULL;
+  if (resetVisible) {
+    FORALLvisible_facets {
+      visible->f.replace= NULL;
+      visible->visible= False;
+    }
+    qh->num_visible= 0;
+  }
+  qh->visible_list= NULL; /* may still have visible facets via qh_triangulate */
+  qh->NEWfacets= False;
+} /* resetlists */
+
+/*-<a                             href="qh-poly_r.htm#TOC"
+  >-------------------------------</a><a name="setvoronoi_all">-</a>
+
+  qh_setvoronoi_all(qh)
+    compute Voronoi centers for all facets
+    includes upperDelaunay facets if qh.UPPERdelaunay ('Qu')
+
+  returns:
+    facet->center is the Voronoi center
+
+  notes:
+    this is unused/untested code
+      please email bradb@shore.net if this works ok for you
+
+  use:
+    FORALLvertices {...} to locate the vertex for a point.
+    FOREACHneighbor_(vertex) {...} to visit the Voronoi centers for a Voronoi cell.
+*/
+void qh_setvoronoi_all(qhT *qh) {
+  facetT *facet;
+
+  qh_clearcenters(qh, qh_ASvoronoi);
+  qh_vertexneighbors(qh);
+
+  FORALLfacets {
+    if (!facet->normal || !facet->upperdelaunay || qh->UPPERdelaunay) {
+      if (!facet->center)
+        facet->center= qh_facetcenter(qh, facet->vertices);
+    }
+  }
+} /* setvoronoi_all */
+
+#ifndef qh_NOmerge
+
+/*-<a                             href="qh-poly_r.htm#TOC"
+  >-------------------------------</a><a name="triangulate">-</a>
+
+  qh_triangulate()
+    triangulate non-simplicial facets on qh.facet_list,
+    if qh->VORONOI, sets Voronoi centers of non-simplicial facets
+    nop if hasTriangulation
+
+  returns:
+    all facets simplicial
+    each tricoplanar facet has ->f.triowner == owner of ->center,normal,etc.
+
+  notes:
+    call after qh_check_output since may switch to Voronoi centers
+    Output may overwrite ->f.triowner with ->f.area
+*/
+void qh_triangulate(qhT *qh /*qh.facet_list*/) {
+  facetT *facet, *nextfacet, *owner;
+  int onlygood= qh->ONLYgood;
+  facetT *neighbor, *visible= NULL, *facet1, *facet2, *new_facet_list= NULL;
+  facetT *orig_neighbor= NULL, *otherfacet;
+  vertexT *new_vertex_list= NULL;
+  mergeT *merge;
+  mergeType mergetype;
+  int neighbor_i, neighbor_n;
+
+  if (qh->hasTriangulation)
+      return;
+  trace1((qh, qh->ferr, 1034, "qh_triangulate: triangulate non-simplicial facets\n"));
+  if (qh->hull_dim == 2)
+    return;
+  if (qh->VORONOI) {  /* otherwise lose Voronoi centers [could rebuild vertex set from tricoplanar] */
+    qh_clearcenters(qh, qh_ASvoronoi);
+    qh_vertexneighbors(qh);
+  }
+  qh->ONLYgood= False; /* for makenew_nonsimplicial */
+  qh->visit_id++;
+  qh->NEWfacets= True;
+  qh->degen_mergeset= qh_settemp(qh, qh->TEMPsize);
+  qh->newvertex_list= qh->vertex_tail;
+  for (facet= qh->facet_list; facet && facet->next; facet= nextfacet) { /* non-simplicial facets moved to end */
+    nextfacet= facet->next;
+    if (facet->visible || facet->simplicial)
+      continue;
+    /* triangulate all non-simplicial facets, otherwise merging does not work, e.g., RBOX c P-0.1 P+0.1 P+0.1 D3 | QHULL d Qt Tv */
+    if (!new_facet_list)
+      new_facet_list= facet;  /* will be moved to end */
+    qh_triangulate_facet(qh, facet, &new_vertex_list);
+  }
+  trace2((qh, qh->ferr, 2047, "qh_triangulate: delete null facets from f%d -- apex same as second vertex\n", getid_(new_facet_list)));
+  for (facet= new_facet_list; facet && facet->next; facet= nextfacet) { /* null facets moved to end */
+    nextfacet= facet->next;
+    if (facet->visible)
+      continue;
+    if (facet->ridges) {
+      if (qh_setsize(qh, facet->ridges) > 0) {
+        qh_fprintf(qh, qh->ferr, 6161, "qhull error (qh_triangulate): ridges still defined for f%d\n", facet->id);
+        qh_errexit(qh, qh_ERRqhull, facet, NULL);
+      }
+      qh_setfree(qh, &facet->ridges);
+    }
+    if (SETfirst_(facet->vertices) == SETsecond_(facet->vertices)) {
+      zinc_(Ztrinull);
+      qh_triangulate_null(qh, facet);
+    }
+  }
+  trace2((qh, qh->ferr, 2048, "qh_triangulate: delete %d or more mirror facets -- same vertices and neighbors\n", qh_setsize(qh, qh->degen_mergeset)));
+  qh->visible_list= qh->facet_tail;
+  while ((merge= (mergeT*)qh_setdellast(qh->degen_mergeset))) {
+    facet1= merge->facet1;
+    facet2= merge->facet2;
+    mergetype= merge->type;
+    qh_memfree(qh, merge, (int)sizeof(mergeT));
+    if (mergetype == MRGmirror) {
+      zinc_(Ztrimirror);
+      qh_triangulate_mirror(qh, facet1, facet2);
+    }
+  }
+  qh_settempfree(qh, &qh->degen_mergeset);
+  trace2((qh, qh->ferr, 2049, "qh_triangulate: update neighbor lists for vertices from v%d\n", getid_(new_vertex_list)));
+  qh->newvertex_list= new_vertex_list;  /* all vertices of new facets */
+  qh->visible_list= NULL;
+  qh_updatevertices(qh /*qh.newvertex_list, empty newfacet_list and visible_list*/);
+  qh_resetlists(qh, False, !qh_RESETvisible /*qh.newvertex_list, empty newfacet_list and visible_list*/);
+
+  trace2((qh, qh->ferr, 2050, "qh_triangulate: identify degenerate tricoplanar facets from f%d\n", getid_(new_facet_list)));
+  trace2((qh, qh->ferr, 2051, "qh_triangulate: and replace facet->f.triowner with tricoplanar facets that own center, normal, etc.\n"));
+  FORALLfacet_(new_facet_list) {
+    if (facet->tricoplanar && !facet->visible) {
+      FOREACHneighbor_i_(qh, facet) {
+        if (neighbor_i == 0) {  /* first iteration */
+          if (neighbor->tricoplanar)
+            orig_neighbor= neighbor->f.triowner;
+          else
+            orig_neighbor= neighbor;
+        }else {
+          if (neighbor->tricoplanar)
+            otherfacet= neighbor->f.triowner;
+          else
+            otherfacet= neighbor;
+          if (orig_neighbor == otherfacet) {
+            zinc_(Ztridegen);
+            facet->degenerate= True;
+            break;
+          }
+        }
+      }
+    }
+  }
+
+  trace2((qh, qh->ferr, 2052, "qh_triangulate: delete visible facets -- non-simplicial, null, and mirrored facets\n"));
+  owner= NULL;
+  visible= NULL;
+  for (facet= new_facet_list; facet && facet->next; facet= nextfacet) { /* may delete facet */
+    nextfacet= facet->next;
+    if (facet->visible) {
+      if (facet->tricoplanar) { /* a null or mirrored facet */
+        qh_delfacet(qh, facet);
+        qh->num_visible--;
+      }else {  /* a non-simplicial facet followed by its tricoplanars */
+        if (visible && !owner) {
+          /*  RBOX 200 s D5 t1001471447 | QHULL Qt C-0.01 Qx Qc Tv Qt -- f4483 had 6 vertices/neighbors and 8 ridges */
+          trace2((qh, qh->ferr, 2053, "qh_triangulate: all tricoplanar facets degenerate for non-simplicial facet f%d\n",
+                       visible->id));
+          qh_delfacet(qh, visible);
+          qh->num_visible--;
+        }
+        visible= facet;
+        owner= NULL;
+      }
+    }else if (facet->tricoplanar) {
+      if (facet->f.triowner != visible || visible==NULL) {
+        qh_fprintf(qh, qh->ferr, 6162, "qhull error (qh_triangulate): tricoplanar facet f%d not owned by its visible, non-simplicial facet f%d\n", facet->id, getid_(visible));
+        qh_errexit2(qh, qh_ERRqhull, facet, visible);
+      }
+      if (owner)
+        facet->f.triowner= owner;
+      else if (!facet->degenerate) {
+        owner= facet;
+        nextfacet= visible->next; /* rescan tricoplanar facets with owner, visible!=0 by QH6162 */
+        facet->keepcentrum= True;  /* one facet owns ->normal, etc. */
+        facet->coplanarset= visible->coplanarset;
+        facet->outsideset= visible->outsideset;
+        visible->coplanarset= NULL;
+        visible->outsideset= NULL;
+        if (!qh->TRInormals) { /* center and normal copied to tricoplanar facets */
+          visible->center= NULL;
+          visible->normal= NULL;
+        }
+        qh_delfacet(qh, visible);
+        qh->num_visible--;
+      }
+    }
+  }
+  if (visible && !owner) {
+    trace2((qh, qh->ferr, 2054, "qh_triangulate: all tricoplanar facets degenerate for last non-simplicial facet f%d\n",
+                 visible->id));
+    qh_delfacet(qh, visible);
+    qh->num_visible--;
+  }
+  qh->NEWfacets= False;
+  qh->ONLYgood= onlygood; /* restore value */
+  if (qh->CHECKfrequently)
+    qh_checkpolygon(qh, qh->facet_list);
+  qh->hasTriangulation= True;
+} /* triangulate */
+
+
+/*-<a                             href="qh-poly_r.htm#TOC"
+  >-------------------------------</a><a name="triangulate_facet">-</a>
+
+  qh_triangulate_facet(qh, facetA, &firstVertex )
+    triangulate a non-simplicial facet
+      if qh.CENTERtype=qh_ASvoronoi, sets its Voronoi center
+  returns:
+    qh.newfacet_list == simplicial facets
+      facet->tricoplanar set and ->keepcentrum false
+      facet->degenerate set if duplicated apex
+      facet->f.trivisible set to facetA
+      facet->center copied from facetA (created if qh_ASvoronoi)
+        qh_eachvoronoi, qh_detvridge, qh_detvridge3 assume centers copied
+      facet->normal,offset,maxoutside copied from facetA
+
+  notes:
+      only called by qh_triangulate
+      qh_makenew_nonsimplicial uses neighbor->seen for the same
+      if qh.TRInormals, newfacet->normal will need qh_free
+        if qh.TRInormals and qh_AScentrum, newfacet->center will need qh_free
+        keepcentrum is also set on Zwidefacet in qh_mergefacet
+        freed by qh_clearcenters
+
+  see also:
+      qh_addpoint() -- add a point
+      qh_makenewfacets() -- construct a cone of facets for a new vertex
+
+  design:
+      if qh_ASvoronoi,
+         compute Voronoi center (facet->center)
+      select first vertex (highest ID to preserve ID ordering of ->vertices)
+      triangulate from vertex to ridges
+      copy facet->center, normal, offset
+      update vertex neighbors
+*/
+void qh_triangulate_facet(qhT *qh, facetT *facetA, vertexT **first_vertex) {
+  facetT *newfacet;
+  facetT *neighbor, **neighborp;
+  vertexT *apex;
+  int numnew=0;
+
+  trace3((qh, qh->ferr, 3020, "qh_triangulate_facet: triangulate facet f%d\n", facetA->id));
+
+  if (qh->IStracing >= 4)
+    qh_printfacet(qh, qh->ferr, facetA);
+  FOREACHneighbor_(facetA) {
+    neighbor->seen= False;
+    neighbor->coplanar= False;
+  }
+  if (qh->CENTERtype == qh_ASvoronoi && !facetA->center  /* matches upperdelaunay in qh_setfacetplane() */
+        && fabs_(facetA->normal[qh->hull_dim -1]) >= qh->ANGLEround * qh_ZEROdelaunay) {
+    facetA->center= qh_facetcenter(qh, facetA->vertices);
+  }
+  qh_willdelete(qh, facetA, NULL);
+  qh->newfacet_list= qh->facet_tail;
+  facetA->visitid= qh->visit_id;
+  apex= SETfirstt_(facetA->vertices, vertexT);
+  qh_makenew_nonsimplicial(qh, facetA, apex, &numnew);
+  SETfirst_(facetA->neighbors)= NULL;
+  FORALLnew_facets {
+    newfacet->tricoplanar= True;
+    newfacet->f.trivisible= facetA;
+    newfacet->degenerate= False;
+    newfacet->upperdelaunay= facetA->upperdelaunay;
+    newfacet->good= facetA->good;
+    if (qh->TRInormals) { /* 'Q11' triangulate duplicates ->normal and ->center */
+      newfacet->keepcentrum= True;
+      if(facetA->normal){
+        newfacet->normal= qh_memalloc(qh, qh->normal_size);
+        memcpy((char *)newfacet->normal, facetA->normal, qh->normal_size);
+      }
+      if (qh->CENTERtype == qh_AScentrum)
+        newfacet->center= qh_getcentrum(qh, newfacet);
+      else if (qh->CENTERtype == qh_ASvoronoi && facetA->center){
+        newfacet->center= qh_memalloc(qh, qh->center_size);
+        memcpy((char *)newfacet->center, facetA->center, qh->center_size);
+      }
+    }else {
+      newfacet->keepcentrum= False;
+      /* one facet will have keepcentrum=True at end of qh_triangulate */
+      newfacet->normal= facetA->normal;
+      newfacet->center= facetA->center;
+    }
+    newfacet->offset= facetA->offset;
+#if qh_MAXoutside
+    newfacet->maxoutside= facetA->maxoutside;
+#endif
+  }
+  qh_matchnewfacets(qh /*qh.newfacet_list*/);
+  zinc_(Ztricoplanar);
+  zadd_(Ztricoplanartot, numnew);
+  zmax_(Ztricoplanarmax, numnew);
+  qh->visible_list= NULL;
+  if (!(*first_vertex))
+    (*first_vertex)= qh->newvertex_list;
+  qh->newvertex_list= NULL;
+  qh_updatevertices(qh /*qh.newfacet_list, qh.empty visible_list and qh.newvertex_list*/);
+  qh_resetlists(qh, False, !qh_RESETvisible /*qh.newfacet_list, qh.empty visible_list and qh.newvertex_list*/);
+} /* triangulate_facet */
+
+/*-<a                             href="qh-poly_r.htm#TOC"
+  >-------------------------------</a><a name="triangulate_link">-</a>
+
+  qh_triangulate_link(qh, oldfacetA, facetA, oldfacetB, facetB)
+    relink facetA to facetB via oldfacets
+  returns:
+    adds mirror facets to qh->degen_mergeset (4-d and up only)
+  design:
+    if they are already neighbors, the opposing neighbors become MRGmirror facets
+*/
+void qh_triangulate_link(qhT *qh, facetT *oldfacetA, facetT *facetA, facetT *oldfacetB, facetT *facetB) {
+  int errmirror= False;
+
+  trace3((qh, qh->ferr, 3021, "qh_triangulate_link: relink old facets f%d and f%d between neighbors f%d and f%d\n",
+         oldfacetA->id, oldfacetB->id, facetA->id, facetB->id));
+  if (qh_setin(facetA->neighbors, facetB)) {
+    if (!qh_setin(facetB->neighbors, facetA))
+      errmirror= True;
+    else
+      qh_appendmergeset(qh, facetA, facetB, MRGmirror, NULL);
+  }else if (qh_setin(facetB->neighbors, facetA))
+    errmirror= True;
+  if (errmirror) {
+    qh_fprintf(qh, qh->ferr, 6163, "qhull error (qh_triangulate_link): mirror facets f%d and f%d do not match for old facets f%d and f%d\n",
+       facetA->id, facetB->id, oldfacetA->id, oldfacetB->id);
+    qh_errexit2(qh, qh_ERRqhull, facetA, facetB);
+  }
+  qh_setreplace(qh, facetB->neighbors, oldfacetB, facetA);
+  qh_setreplace(qh, facetA->neighbors, oldfacetA, facetB);
+} /* triangulate_link */
+
+/*-<a                             href="qh-poly_r.htm#TOC"
+  >-------------------------------</a><a name="triangulate_mirror">-</a>
+
+  qh_triangulate_mirror(qh, facetA, facetB)
+    delete mirrored facets from qh_triangulate_null() and qh_triangulate_mirror
+      a mirrored facet shares the same vertices of a logical ridge
+  design:
+    since a null facet duplicates the first two vertices, the opposing neighbors absorb the null facet
+    if they are already neighbors, the opposing neighbors become MRGmirror facets
+*/
+void qh_triangulate_mirror(qhT *qh, facetT *facetA, facetT *facetB) {
+  facetT *neighbor, *neighborB;
+  int neighbor_i, neighbor_n;
+
+  trace3((qh, qh->ferr, 3022, "qh_triangulate_mirror: delete mirrored facets f%d and f%d\n",
+         facetA->id, facetB->id));
+  FOREACHneighbor_i_(qh, facetA) {
+    neighborB= SETelemt_(facetB->neighbors, neighbor_i, facetT);
+    if (neighbor == neighborB)
+      continue; /* occurs twice */
+    qh_triangulate_link(qh, facetA, neighbor, facetB, neighborB);
+  }
+  qh_willdelete(qh, facetA, NULL);
+  qh_willdelete(qh, facetB, NULL);
+} /* triangulate_mirror */
+
+/*-<a                             href="qh-poly_r.htm#TOC"
+  >-------------------------------</a><a name="triangulate_null">-</a>
+
+  qh_triangulate_null(qh, facetA)
+    remove null facetA from qh_triangulate_facet()
+      a null facet has vertex #1 (apex) == vertex #2
+  returns:
+    adds facetA to ->visible for deletion after qh_updatevertices
+    qh->degen_mergeset contains mirror facets (4-d and up only)
+  design:
+    since a null facet duplicates the first two vertices, the opposing neighbors absorb the null facet
+    if they are already neighbors, the opposing neighbors become MRGmirror facets
+*/
+void qh_triangulate_null(qhT *qh, facetT *facetA) {
+  facetT *neighbor, *otherfacet;
+
+  trace3((qh, qh->ferr, 3023, "qh_triangulate_null: delete null facet f%d\n", facetA->id));
+  neighbor= SETfirstt_(facetA->neighbors, facetT);
+  otherfacet= SETsecondt_(facetA->neighbors, facetT);
+  qh_triangulate_link(qh, facetA, neighbor, facetA, otherfacet);
+  qh_willdelete(qh, facetA, NULL);
+} /* triangulate_null */
+
+#else /* qh_NOmerge */
+void qh_triangulate(qhT *qh) {
+}
+#endif /* qh_NOmerge */
+
+   /*-<a                             href="qh-poly_r.htm#TOC"
+  >-------------------------------</a><a name="vertexintersect">-</a>
+
+  qh_vertexintersect(qh, vertexsetA, vertexsetB )
+    intersects two vertex sets (inverse id ordered)
+    vertexsetA is a temporary set at the top of qh->qhmem.tempstack
+
+  returns:
+    replaces vertexsetA with the intersection
+
+  notes:
+    could overwrite vertexsetA if currently too slow
+*/
+void qh_vertexintersect(qhT *qh, setT **vertexsetA,setT *vertexsetB) {
+  setT *intersection;
+
+  intersection= qh_vertexintersect_new(qh, *vertexsetA, vertexsetB);
+  qh_settempfree(qh, vertexsetA);
+  *vertexsetA= intersection;
+  qh_settemppush(qh, intersection);
+} /* vertexintersect */
+
+/*-<a                             href="qh-poly_r.htm#TOC"
+  >-------------------------------</a><a name="vertexintersect_new">-</a>
+
+  qh_vertexintersect_new(qh, )
+    intersects two vertex sets (inverse id ordered)
+
+  returns:
+    a new set
+*/
+setT *qh_vertexintersect_new(qhT *qh, setT *vertexsetA,setT *vertexsetB) {
+  setT *intersection= qh_setnew(qh, qh->hull_dim - 1);
+  vertexT **vertexA= SETaddr_(vertexsetA, vertexT);
+  vertexT **vertexB= SETaddr_(vertexsetB, vertexT);
+
+  while (*vertexA && *vertexB) {
+    if (*vertexA  == *vertexB) {
+      qh_setappend(qh, &intersection, *vertexA);
+      vertexA++; vertexB++;
+    }else {
+      if ((*vertexA)->id > (*vertexB)->id)
+        vertexA++;
+      else
+        vertexB++;
+    }
+  }
+  return intersection;
+} /* vertexintersect_new */
+
+/*-<a                             href="qh-poly_r.htm#TOC"
+  >-------------------------------</a><a name="vertexneighbors">-</a>
+
+  qh_vertexneighbors(qh)
+    for each vertex in qh.facet_list,
+      determine its neighboring facets
+
+  returns:
+    sets qh.VERTEXneighbors
+      nop if qh.VERTEXneighbors already set
+      qh_addpoint() will maintain them
+
+  notes:
+    assumes all vertex->neighbors are NULL
+
+  design:
+    for each facet
+      for each vertex
+        append facet to vertex->neighbors
+*/
+void qh_vertexneighbors(qhT *qh /*qh.facet_list*/) {
+  facetT *facet;
+  vertexT *vertex, **vertexp;
+
+  if (qh->VERTEXneighbors)
+    return;
+  trace1((qh, qh->ferr, 1035, "qh_vertexneighbors: determining neighboring facets for each vertex\n"));
+  qh->vertex_visit++;
+  FORALLfacets {
+    if (facet->visible)
+      continue;
+    FOREACHvertex_(facet->vertices) {
+      if (vertex->visitid != qh->vertex_visit) {
+        vertex->visitid= qh->vertex_visit;
+        vertex->neighbors= qh_setnew(qh, qh->hull_dim);
+      }
+      qh_setappend(qh, &vertex->neighbors, facet);
+    }
+  }
+  qh->VERTEXneighbors= True;
+} /* vertexneighbors */
+
+/*-<a                             href="qh-poly_r.htm#TOC"
+  >-------------------------------</a><a name="vertexsubset">-</a>
+
+  qh_vertexsubset( vertexsetA, vertexsetB )
+    returns True if vertexsetA is a subset of vertexsetB
+    assumes vertexsets are sorted
+
+  note:
+    empty set is a subset of any other set
+*/
+boolT qh_vertexsubset(setT *vertexsetA, setT *vertexsetB) {
+  vertexT **vertexA= (vertexT **) SETaddr_(vertexsetA, vertexT);
+  vertexT **vertexB= (vertexT **) SETaddr_(vertexsetB, vertexT);
+
+  while (True) {
+    if (!*vertexA)
+      return True;
+    if (!*vertexB)
+      return False;
+    if ((*vertexA)->id > (*vertexB)->id)
+      return False;
+    if (*vertexA  == *vertexB)
+      vertexA++;
+    vertexB++;
+  }
+  return False; /* avoid warnings */
+} /* vertexsubset */
diff --git a/C/poly_r.c b/C/poly_r.c
new file mode 100644
--- /dev/null
+++ b/C/poly_r.c
@@ -0,0 +1,1205 @@
+/*<html><pre>  -<a                             href="qh-poly_r.htm"
+  >-------------------------------</a><a name="TOP">-</a>
+
+   poly_r.c
+   implements polygons and simplices
+
+   see qh-poly_r.htm, poly_r.h and libqhull_r.h
+
+   infrequent code is in poly2_r.c
+   (all but top 50 and their callers 12/3/95)
+
+   Copyright (c) 1993-2015 The Geometry Center.
+   $Id: //main/2015/qhull/src/libqhull_r/poly_r.c#3 $$Change: 2064 $
+   $DateTime: 2016/01/18 12:36:08 $$Author: bbarber $
+*/
+
+#include "qhull_ra.h"
+
+/*======== functions in alphabetical order ==========*/
+
+/*-<a                             href="qh-poly_r.htm#TOC"
+  >-------------------------------</a><a name="appendfacet">-</a>
+
+  qh_appendfacet(qh, facet )
+    appends facet to end of qh.facet_list,
+
+  returns:
+    updates qh.newfacet_list, facet_next, facet_list
+    increments qh.numfacets
+
+  notes:
+    assumes qh.facet_list/facet_tail is defined (createsimplex)
+
+  see:
+    qh_removefacet()
+
+*/
+void qh_appendfacet(qhT *qh, facetT *facet) {
+  facetT *tail= qh->facet_tail;
+
+  if (tail == qh->newfacet_list)
+    qh->newfacet_list= facet;
+  if (tail == qh->facet_next)
+    qh->facet_next= facet;
+  facet->previous= tail->previous;
+  facet->next= tail;
+  if (tail->previous)
+    tail->previous->next= facet;
+  else
+    qh->facet_list= facet;
+  tail->previous= facet;
+  qh->num_facets++;
+  trace4((qh, qh->ferr, 4044, "qh_appendfacet: append f%d to facet_list\n", facet->id));
+} /* appendfacet */
+
+
+/*-<a                             href="qh-poly_r.htm#TOC"
+  >-------------------------------</a><a name="appendvertex">-</a>
+
+  qh_appendvertex(qh, vertex )
+    appends vertex to end of qh.vertex_list,
+
+  returns:
+    sets vertex->newlist
+    updates qh.vertex_list, newvertex_list
+    increments qh.num_vertices
+
+  notes:
+    assumes qh.vertex_list/vertex_tail is defined (createsimplex)
+
+*/
+void qh_appendvertex(qhT *qh, vertexT *vertex) {
+  vertexT *tail= qh->vertex_tail;
+
+  if (tail == qh->newvertex_list)
+    qh->newvertex_list= vertex;
+  vertex->newlist= True;
+  vertex->previous= tail->previous;
+  vertex->next= tail;
+  if (tail->previous)
+    tail->previous->next= vertex;
+  else
+    qh->vertex_list= vertex;
+  tail->previous= vertex;
+  qh->num_vertices++;
+  trace4((qh, qh->ferr, 4045, "qh_appendvertex: append v%d to vertex_list\n", vertex->id));
+} /* appendvertex */
+
+
+/*-<a                             href="qh-poly_r.htm#TOC"
+  >-------------------------------</a><a name="attachnewfacets">-</a>
+
+  qh_attachnewfacets(qh, )
+    attach horizon facets to new facets in qh.newfacet_list
+    newfacets have neighbor and ridge links to horizon but not vice versa
+    only needed for qh.ONLYgood
+
+  returns:
+    set qh.NEWfacets
+    horizon facets linked to new facets
+      ridges changed from visible facets to new facets
+      simplicial ridges deleted
+    qh.visible_list, no ridges valid
+    facet->f.replace is a newfacet (if any)
+
+  design:
+    delete interior ridges and neighbor sets by
+      for each visible, non-simplicial facet
+        for each ridge
+          if last visit or if neighbor is simplicial
+            if horizon neighbor
+              delete ridge for horizon's ridge set
+            delete ridge
+        erase neighbor set
+    attach horizon facets and new facets by
+      for all new facets
+        if corresponding horizon facet is simplicial
+          locate corresponding visible facet {may be more than one}
+          link visible facet to new facet
+          replace visible facet with new facet in horizon
+        else it's non-simplicial
+          for all visible neighbors of the horizon facet
+            link visible neighbor to new facet
+            delete visible neighbor from horizon facet
+          append new facet to horizon's neighbors
+          the first ridge of the new facet is the horizon ridge
+          link the new facet into the horizon ridge
+*/
+void qh_attachnewfacets(qhT *qh /* qh.visible_list, newfacet_list */) {
+  facetT *newfacet= NULL, *neighbor, **neighborp, *horizon, *visible;
+  ridgeT *ridge, **ridgep;
+
+  qh->NEWfacets= True;
+  trace3((qh, qh->ferr, 3012, "qh_attachnewfacets: delete interior ridges\n"));
+  qh->visit_id++;
+  FORALLvisible_facets {
+    visible->visitid= qh->visit_id;
+    if (visible->ridges) {
+      FOREACHridge_(visible->ridges) {
+        neighbor= otherfacet_(ridge, visible);
+        if (neighbor->visitid == qh->visit_id
+            || (!neighbor->visible && neighbor->simplicial)) {
+          if (!neighbor->visible)  /* delete ridge for simplicial horizon */
+            qh_setdel(neighbor->ridges, ridge);
+          qh_setfree(qh, &(ridge->vertices)); /* delete on 2nd visit */
+          qh_memfree(qh, ridge, (int)sizeof(ridgeT));
+        }
+      }
+      SETfirst_(visible->ridges)= NULL;
+    }
+    SETfirst_(visible->neighbors)= NULL;
+  }
+  trace1((qh, qh->ferr, 1017, "qh_attachnewfacets: attach horizon facets to new facets\n"));
+  FORALLnew_facets {
+    horizon= SETfirstt_(newfacet->neighbors, facetT);
+    if (horizon->simplicial) {
+      visible= NULL;
+      FOREACHneighbor_(horizon) {   /* may have more than one horizon ridge */
+        if (neighbor->visible) {
+          if (visible) {
+            if (qh_setequal_skip(newfacet->vertices, 0, horizon->vertices,
+                                  SETindex_(horizon->neighbors, neighbor))) {
+              visible= neighbor;
+              break;
+            }
+          }else
+            visible= neighbor;
+        }
+      }
+      if (visible) {
+        visible->f.replace= newfacet;
+        qh_setreplace(qh, horizon->neighbors, visible, newfacet);
+      }else {
+        qh_fprintf(qh, qh->ferr, 6102, "qhull internal error (qh_attachnewfacets): couldn't find visible facet for horizon f%d of newfacet f%d\n",
+                 horizon->id, newfacet->id);
+        qh_errexit2(qh, qh_ERRqhull, horizon, newfacet);
+      }
+    }else { /* non-simplicial, with a ridge for newfacet */
+      FOREACHneighbor_(horizon) {    /* may hold for many new facets */
+        if (neighbor->visible) {
+          neighbor->f.replace= newfacet;
+          qh_setdelnth(qh, horizon->neighbors,
+                        SETindex_(horizon->neighbors, neighbor));
+          neighborp--; /* repeat */
+        }
+      }
+      qh_setappend(qh, &horizon->neighbors, newfacet);
+      ridge= SETfirstt_(newfacet->ridges, ridgeT);
+      if (ridge->top == horizon)
+        ridge->bottom= newfacet;
+      else
+        ridge->top= newfacet;
+      }
+  } /* newfacets */
+  if (qh->PRINTstatistics) {
+    FORALLvisible_facets {
+      if (!visible->f.replace)
+        zinc_(Zinsidevisible);
+    }
+  }
+} /* attachnewfacets */
+
+/*-<a                             href="qh-poly_r.htm#TOC"
+  >-------------------------------</a><a name="checkflipped">-</a>
+
+  qh_checkflipped(qh, facet, dist, allerror )
+    checks facet orientation to interior point
+
+    if allerror set,
+      tests against qh.DISTround
+    else
+      tests against 0 since tested against DISTround before
+
+  returns:
+    False if it flipped orientation (sets facet->flipped)
+    distance if non-NULL
+*/
+boolT qh_checkflipped(qhT *qh, facetT *facet, realT *distp, boolT allerror) {
+  realT dist;
+
+  if (facet->flipped && !distp)
+    return False;
+  zzinc_(Zdistcheck);
+  qh_distplane(qh, qh->interior_point, facet, &dist);
+  if (distp)
+    *distp= dist;
+  if ((allerror && dist > -qh->DISTround)|| (!allerror && dist >= 0.0)) {
+    facet->flipped= True;
+    zzinc_(Zflippedfacets);
+    trace0((qh, qh->ferr, 19, "qh_checkflipped: facet f%d is flipped, distance= %6.12g during p%d\n",
+              facet->id, dist, qh->furthest_id));
+    qh_precision(qh, "flipped facet");
+    return False;
+  }
+  return True;
+} /* checkflipped */
+
+/*-<a                             href="qh-poly_r.htm#TOC"
+  >-------------------------------</a><a name="delfacet">-</a>
+
+  qh_delfacet(qh, facet )
+    removes facet from facet_list and frees up its memory
+
+  notes:
+    assumes vertices and ridges already freed
+*/
+void qh_delfacet(qhT *qh, facetT *facet) {
+  void **freelistp; /* used if !qh_NOmem by qh_memfree_() */
+
+  trace4((qh, qh->ferr, 4046, "qh_delfacet: delete f%d\n", facet->id));
+  if (facet == qh->tracefacet)
+    qh->tracefacet= NULL;
+  if (facet == qh->GOODclosest)
+    qh->GOODclosest= NULL;
+  qh_removefacet(qh, facet);
+  if (!facet->tricoplanar || facet->keepcentrum) {
+    qh_memfree_(qh, facet->normal, qh->normal_size, freelistp);
+    if (qh->CENTERtype == qh_ASvoronoi) {   /* braces for macro calls */
+      qh_memfree_(qh, facet->center, qh->center_size, freelistp);
+    }else /* AScentrum */ {
+      qh_memfree_(qh, facet->center, qh->normal_size, freelistp);
+    }
+  }
+  qh_setfree(qh, &(facet->neighbors));
+  if (facet->ridges)
+    qh_setfree(qh, &(facet->ridges));
+  qh_setfree(qh, &(facet->vertices));
+  if (facet->outsideset)
+    qh_setfree(qh, &(facet->outsideset));
+  if (facet->coplanarset)
+    qh_setfree(qh, &(facet->coplanarset));
+  qh_memfree_(qh, facet, (int)sizeof(facetT), freelistp);
+} /* delfacet */
+
+
+/*-<a                             href="qh-poly_r.htm#TOC"
+  >-------------------------------</a><a name="deletevisible">-</a>
+
+  qh_deletevisible()
+    delete visible facets and vertices
+
+  returns:
+    deletes each facet and removes from facetlist
+    at exit, qh.visible_list empty (== qh.newfacet_list)
+
+  notes:
+    ridges already deleted
+    horizon facets do not reference facets on qh.visible_list
+    new facets in qh.newfacet_list
+    uses   qh.visit_id;
+*/
+void qh_deletevisible(qhT *qh /*qh.visible_list*/) {
+  facetT *visible, *nextfacet;
+  vertexT *vertex, **vertexp;
+  int numvisible= 0, numdel= qh_setsize(qh, qh->del_vertices);
+
+  trace1((qh, qh->ferr, 1018, "qh_deletevisible: delete %d visible facets and %d vertices\n",
+         qh->num_visible, numdel));
+  for (visible= qh->visible_list; visible && visible->visible;
+                visible= nextfacet) { /* deleting current */
+    nextfacet= visible->next;
+    numvisible++;
+    qh_delfacet(qh, visible);
+  }
+  if (numvisible != qh->num_visible) {
+    qh_fprintf(qh, qh->ferr, 6103, "qhull internal error (qh_deletevisible): qh->num_visible %d is not number of visible facets %d\n",
+             qh->num_visible, numvisible);
+    qh_errexit(qh, qh_ERRqhull, NULL, NULL);
+  }
+  qh->num_visible= 0;
+  zadd_(Zvisfacettot, numvisible);
+  zmax_(Zvisfacetmax, numvisible);
+  zzadd_(Zdelvertextot, numdel);
+  zmax_(Zdelvertexmax, numdel);
+  FOREACHvertex_(qh->del_vertices)
+    qh_delvertex(qh, vertex);
+  qh_settruncate(qh, qh->del_vertices, 0);
+} /* deletevisible */
+
+/*-<a                             href="qh-poly_r.htm#TOC"
+  >-------------------------------</a><a name="facetintersect">-</a>
+
+  qh_facetintersect(qh, facetA, facetB, skipa, skipB, prepend )
+    return vertices for intersection of two simplicial facets
+    may include 1 prepended entry (if more, need to settemppush)
+
+  returns:
+    returns set of qh.hull_dim-1 + prepend vertices
+    returns skipped index for each test and checks for exactly one
+
+  notes:
+    does not need settemp since set in quick memory
+
+  see also:
+    qh_vertexintersect and qh_vertexintersect_new
+    use qh_setnew_delnthsorted to get nth ridge (no skip information)
+
+  design:
+    locate skipped vertex by scanning facet A's neighbors
+    locate skipped vertex by scanning facet B's neighbors
+    intersect the vertex sets
+*/
+setT *qh_facetintersect(qhT *qh, facetT *facetA, facetT *facetB,
+                         int *skipA,int *skipB, int prepend) {
+  setT *intersect;
+  int dim= qh->hull_dim, i, j;
+  facetT **neighborsA, **neighborsB;
+
+  neighborsA= SETaddr_(facetA->neighbors, facetT);
+  neighborsB= SETaddr_(facetB->neighbors, facetT);
+  i= j= 0;
+  if (facetB == *neighborsA++)
+    *skipA= 0;
+  else if (facetB == *neighborsA++)
+    *skipA= 1;
+  else if (facetB == *neighborsA++)
+    *skipA= 2;
+  else {
+    for (i=3; i < dim; i++) {
+      if (facetB == *neighborsA++) {
+        *skipA= i;
+        break;
+      }
+    }
+  }
+  if (facetA == *neighborsB++)
+    *skipB= 0;
+  else if (facetA == *neighborsB++)
+    *skipB= 1;
+  else if (facetA == *neighborsB++)
+    *skipB= 2;
+  else {
+    for (j=3; j < dim; j++) {
+      if (facetA == *neighborsB++) {
+        *skipB= j;
+        break;
+      }
+    }
+  }
+  if (i >= dim || j >= dim) {
+    qh_fprintf(qh, qh->ferr, 6104, "qhull internal error (qh_facetintersect): f%d or f%d not in others neighbors\n",
+            facetA->id, facetB->id);
+    qh_errexit2(qh, qh_ERRqhull, facetA, facetB);
+  }
+  intersect= qh_setnew_delnthsorted(qh, facetA->vertices, qh->hull_dim, *skipA, prepend);
+  trace4((qh, qh->ferr, 4047, "qh_facetintersect: f%d skip %d matches f%d skip %d\n",
+          facetA->id, *skipA, facetB->id, *skipB));
+  return(intersect);
+} /* facetintersect */
+
+/*-<a                             href="qh-poly_r.htm#TOC"
+  >-------------------------------</a><a name="gethash">-</a>
+
+  qh_gethash(qh, hashsize, set, size, firstindex, skipelem )
+    return hashvalue for a set with firstindex and skipelem
+
+  notes:
+    returned hash is in [0,hashsize)
+    assumes at least firstindex+1 elements
+    assumes skipelem is NULL, in set, or part of hash
+
+    hashes memory addresses which may change over different runs of the same data
+    using sum for hash does badly in high d
+*/
+int qh_gethash(qhT *qh, int hashsize, setT *set, int size, int firstindex, void *skipelem) {
+  void **elemp= SETelemaddr_(set, firstindex, void);
+  ptr_intT hash = 0, elem;
+  unsigned result;
+  int i;
+#ifdef _MSC_VER                   /* Microsoft Visual C++ -- warn about 64-bit issues */
+#pragma warning( push)            /* WARN64 -- ptr_intT holds a 64-bit pointer */
+#pragma warning( disable : 4311)  /* 'type cast': pointer truncation from 'void*' to 'ptr_intT' */
+#endif
+
+  switch (size-firstindex) {
+  case 1:
+    hash= (ptr_intT)(*elemp) - (ptr_intT) skipelem;
+    break;
+  case 2:
+    hash= (ptr_intT)(*elemp) + (ptr_intT)elemp[1] - (ptr_intT) skipelem;
+    break;
+  case 3:
+    hash= (ptr_intT)(*elemp) + (ptr_intT)elemp[1] + (ptr_intT)elemp[2]
+      - (ptr_intT) skipelem;
+    break;
+  case 4:
+    hash= (ptr_intT)(*elemp) + (ptr_intT)elemp[1] + (ptr_intT)elemp[2]
+      + (ptr_intT)elemp[3] - (ptr_intT) skipelem;
+    break;
+  case 5:
+    hash= (ptr_intT)(*elemp) + (ptr_intT)elemp[1] + (ptr_intT)elemp[2]
+      + (ptr_intT)elemp[3] + (ptr_intT)elemp[4] - (ptr_intT) skipelem;
+    break;
+  case 6:
+    hash= (ptr_intT)(*elemp) + (ptr_intT)elemp[1] + (ptr_intT)elemp[2]
+      + (ptr_intT)elemp[3] + (ptr_intT)elemp[4]+ (ptr_intT)elemp[5]
+      - (ptr_intT) skipelem;
+    break;
+  default:
+    hash= 0;
+    i= 3;
+    do {     /* this is about 10% in 10-d */
+      if ((elem= (ptr_intT)*elemp++) != (ptr_intT)skipelem) {
+        hash ^= (elem << i) + (elem >> (32-i));
+        i += 3;
+        if (i >= 32)
+          i -= 32;
+      }
+    }while (*elemp);
+    break;
+  }
+  if (hashsize<0) {
+    qh_fprintf(qh, qh->ferr, 6202, "qhull internal error: negative hashsize %d passed to qh_gethash [poly.c]\n", hashsize);
+    qh_errexit2(qh, qh_ERRqhull, NULL, NULL);
+  }
+  result= (unsigned)hash;
+  result %= (unsigned)hashsize;
+  /* result= 0; for debugging */
+  return result;
+#ifdef _MSC_VER
+#pragma warning( pop)
+#endif
+} /* gethash */
+
+/*-<a                             href="qh-poly_r.htm#TOC"
+  >-------------------------------</a><a name="makenewfacet">-</a>
+
+  qh_makenewfacet(qh, vertices, toporient, horizon )
+    creates a toporient? facet from vertices
+
+  returns:
+    returns newfacet
+      adds newfacet to qh.facet_list
+      newfacet->vertices= vertices
+      if horizon
+        newfacet->neighbor= horizon, but not vice versa
+    newvertex_list updated with vertices
+*/
+facetT *qh_makenewfacet(qhT *qh, setT *vertices, boolT toporient,facetT *horizon) {
+  facetT *newfacet;
+  vertexT *vertex, **vertexp;
+
+  FOREACHvertex_(vertices) {
+    if (!vertex->newlist) {
+      qh_removevertex(qh, vertex);
+      qh_appendvertex(qh, vertex);
+    }
+  }
+  newfacet= qh_newfacet(qh);
+  newfacet->vertices= vertices;
+  newfacet->toporient= (unsigned char)toporient;
+  if (horizon)
+    qh_setappend(qh, &(newfacet->neighbors), horizon);
+  qh_appendfacet(qh, newfacet);
+  return(newfacet);
+} /* makenewfacet */
+
+
+/*-<a                             href="qh-poly_r.htm#TOC"
+  >-------------------------------</a><a name="makenewplanes">-</a>
+
+  qh_makenewplanes()
+    make new hyperplanes for facets on qh.newfacet_list
+
+  returns:
+    all facets have hyperplanes or are marked for   merging
+    doesn't create hyperplane if horizon is coplanar (will merge)
+    updates qh.min_vertex if qh.JOGGLEmax
+
+  notes:
+    facet->f.samecycle is defined for facet->mergehorizon facets
+*/
+void qh_makenewplanes(qhT *qh /* qh.newfacet_list */) {
+  facetT *newfacet;
+
+  FORALLnew_facets {
+    if (!newfacet->mergehorizon)
+      qh_setfacetplane(qh, newfacet);
+  }
+  if (qh->JOGGLEmax < REALmax/2)
+    minimize_(qh->min_vertex, -wwval_(Wnewvertexmax));
+} /* makenewplanes */
+
+/*-<a                             href="qh-poly_r.htm#TOC"
+  >-------------------------------</a><a name="makenew_nonsimplicial">-</a>
+
+  qh_makenew_nonsimplicial(qh, visible, apex, numnew )
+    make new facets for ridges of a visible facet
+
+  returns:
+    first newfacet, bumps numnew as needed
+    attaches new facets if !qh.ONLYgood
+    marks ridge neighbors for simplicial visible
+    if (qh.ONLYgood)
+      ridges on newfacet, horizon, and visible
+    else
+      ridge and neighbors between newfacet and   horizon
+      visible facet's ridges are deleted
+
+  notes:
+    qh.visit_id if visible has already been processed
+    sets neighbor->seen for building f.samecycle
+      assumes all 'seen' flags initially false
+
+  design:
+    for each ridge of visible facet
+      get neighbor of visible facet
+      if neighbor was already processed
+        delete the ridge (will delete all visible facets later)
+      if neighbor is a horizon facet
+        create a new facet
+        if neighbor coplanar
+          adds newfacet to f.samecycle for later merging
+        else
+          updates neighbor's neighbor set
+          (checks for non-simplicial facet with multiple ridges to visible facet)
+        updates neighbor's ridge set
+        (checks for simplicial neighbor to non-simplicial visible facet)
+        (deletes ridge if neighbor is simplicial)
+
+*/
+#ifndef qh_NOmerge
+facetT *qh_makenew_nonsimplicial(qhT *qh, facetT *visible, vertexT *apex, int *numnew) {
+  void **freelistp; /* used if !qh_NOmem by qh_memfree_() */
+  ridgeT *ridge, **ridgep;
+  facetT *neighbor, *newfacet= NULL, *samecycle;
+  setT *vertices;
+  boolT toporient;
+  int ridgeid;
+
+  FOREACHridge_(visible->ridges) {
+    ridgeid= ridge->id;
+    neighbor= otherfacet_(ridge, visible);
+    if (neighbor->visible) {
+      if (!qh->ONLYgood) {
+        if (neighbor->visitid == qh->visit_id) {
+          qh_setfree(qh, &(ridge->vertices));  /* delete on 2nd visit */
+          qh_memfree_(qh, ridge, (int)sizeof(ridgeT), freelistp);
+        }
+      }
+    }else {  /* neighbor is an horizon facet */
+      toporient= (ridge->top == visible);
+      vertices= qh_setnew(qh, qh->hull_dim); /* makes sure this is quick */
+      qh_setappend(qh, &vertices, apex);
+      qh_setappend_set(qh, &vertices, ridge->vertices);
+      newfacet= qh_makenewfacet(qh, vertices, toporient, neighbor);
+      (*numnew)++;
+      if (neighbor->coplanar) {
+        newfacet->mergehorizon= True;
+        if (!neighbor->seen) {
+          newfacet->f.samecycle= newfacet;
+          neighbor->f.newcycle= newfacet;
+        }else {
+          samecycle= neighbor->f.newcycle;
+          newfacet->f.samecycle= samecycle->f.samecycle;
+          samecycle->f.samecycle= newfacet;
+        }
+      }
+      if (qh->ONLYgood) {
+        if (!neighbor->simplicial)
+          qh_setappend(qh, &(newfacet->ridges), ridge);
+      }else {  /* qh_attachnewfacets */
+        if (neighbor->seen) {
+          if (neighbor->simplicial) {
+            qh_fprintf(qh, qh->ferr, 6105, "qhull internal error (qh_makenew_nonsimplicial): simplicial f%d sharing two ridges with f%d\n",
+                   neighbor->id, visible->id);
+            qh_errexit2(qh, qh_ERRqhull, neighbor, visible);
+          }
+          qh_setappend(qh, &(neighbor->neighbors), newfacet);
+        }else
+          qh_setreplace(qh, neighbor->neighbors, visible, newfacet);
+        if (neighbor->simplicial) {
+          qh_setdel(neighbor->ridges, ridge);
+          qh_setfree(qh, &(ridge->vertices));
+          qh_memfree(qh, ridge, (int)sizeof(ridgeT));
+        }else {
+          qh_setappend(qh, &(newfacet->ridges), ridge);
+          if (toporient)
+            ridge->top= newfacet;
+          else
+            ridge->bottom= newfacet;
+        }
+      trace4((qh, qh->ferr, 4048, "qh_makenew_nonsimplicial: created facet f%d from v%d and r%d of horizon f%d\n",
+            newfacet->id, apex->id, ridgeid, neighbor->id));
+      }
+    }
+    neighbor->seen= True;
+  } /* for each ridge */
+  if (!qh->ONLYgood)
+    SETfirst_(visible->ridges)= NULL;
+  return newfacet;
+} /* makenew_nonsimplicial */
+#else /* qh_NOmerge */
+facetT *qh_makenew_nonsimplicial(qhT *qh, facetT *visible, vertexT *apex, int *numnew) {
+  return NULL;
+}
+#endif /* qh_NOmerge */
+
+/*-<a                             href="qh-poly_r.htm#TOC"
+  >-------------------------------</a><a name="makenew_simplicial">-</a>
+
+  qh_makenew_simplicial(qh, visible, apex, numnew )
+    make new facets for simplicial visible facet and apex
+
+  returns:
+    attaches new facets if (!qh.ONLYgood)
+      neighbors between newfacet and horizon
+
+  notes:
+    nop if neighbor->seen or neighbor->visible(see qh_makenew_nonsimplicial)
+
+  design:
+    locate neighboring horizon facet for visible facet
+    determine vertices and orientation
+    create new facet
+    if coplanar,
+      add new facet to f.samecycle
+    update horizon facet's neighbor list
+*/
+facetT *qh_makenew_simplicial(qhT *qh, facetT *visible, vertexT *apex, int *numnew) {
+  facetT *neighbor, **neighborp, *newfacet= NULL;
+  setT *vertices;
+  boolT flip, toporient;
+  int horizonskip= 0, visibleskip= 0;
+
+  FOREACHneighbor_(visible) {
+    if (!neighbor->seen && !neighbor->visible) {
+      vertices= qh_facetintersect(qh, neighbor,visible, &horizonskip, &visibleskip, 1);
+      SETfirst_(vertices)= apex;
+      flip= ((horizonskip & 0x1) ^ (visibleskip & 0x1));
+      if (neighbor->toporient)
+        toporient= horizonskip & 0x1;
+      else
+        toporient= (horizonskip & 0x1) ^ 0x1;
+      newfacet= qh_makenewfacet(qh, vertices, toporient, neighbor);
+      (*numnew)++;
+      if (neighbor->coplanar && (qh->PREmerge || qh->MERGEexact)) {
+#ifndef qh_NOmerge
+        newfacet->f.samecycle= newfacet;
+        newfacet->mergehorizon= True;
+#endif
+      }
+      if (!qh->ONLYgood)
+        SETelem_(neighbor->neighbors, horizonskip)= newfacet;
+      trace4((qh, qh->ferr, 4049, "qh_makenew_simplicial: create facet f%d top %d from v%d and horizon f%d skip %d top %d and visible f%d skip %d, flip? %d\n",
+            newfacet->id, toporient, apex->id, neighbor->id, horizonskip,
+              neighbor->toporient, visible->id, visibleskip, flip));
+    }
+  }
+  return newfacet;
+} /* makenew_simplicial */
+
+/*-<a                             href="qh-poly_r.htm#TOC"
+  >-------------------------------</a><a name="matchneighbor">-</a>
+
+  qh_matchneighbor(qh, newfacet, newskip, hashsize, hashcount )
+    either match subridge of newfacet with neighbor or add to hash_table
+
+  returns:
+    duplicate ridges are unmatched and marked by qh_DUPLICATEridge
+
+  notes:
+    ridge is newfacet->vertices w/o newskip vertex
+    do not allocate memory (need to free hash_table cleanly)
+    uses linear hash chains
+
+  see also:
+    qh_matchduplicates
+
+  design:
+    for each possible matching facet in qh.hash_table
+      if vertices match
+        set ismatch, if facets have opposite orientation
+        if ismatch and matching facet doesn't have a match
+          match the facets by updating their neighbor sets
+        else
+          indicate a duplicate ridge
+          set facet hyperplane for later testing
+          add facet to hashtable
+          unless the other facet was already a duplicate ridge
+            mark both facets with a duplicate ridge
+            add other facet (if defined) to hash table
+*/
+void qh_matchneighbor(qhT *qh, facetT *newfacet, int newskip, int hashsize, int *hashcount) {
+  boolT newfound= False;   /* True, if new facet is already in hash chain */
+  boolT same, ismatch;
+  int hash, scan;
+  facetT *facet, *matchfacet;
+  int skip, matchskip;
+
+  hash= qh_gethash(qh, hashsize, newfacet->vertices, qh->hull_dim, 1,
+                     SETelem_(newfacet->vertices, newskip));
+  trace4((qh, qh->ferr, 4050, "qh_matchneighbor: newfacet f%d skip %d hash %d hashcount %d\n",
+          newfacet->id, newskip, hash, *hashcount));
+  zinc_(Zhashlookup);
+  for (scan= hash; (facet= SETelemt_(qh->hash_table, scan, facetT));
+       scan= (++scan >= hashsize ? 0 : scan)) {
+    if (facet == newfacet) {
+      newfound= True;
+      continue;
+    }
+    zinc_(Zhashtests);
+    if (qh_matchvertices(qh, 1, newfacet->vertices, newskip, facet->vertices, &skip, &same)) {
+      if (SETelem_(newfacet->vertices, newskip) ==
+          SETelem_(facet->vertices, skip)) {
+        qh_precision(qh, "two facets with the same vertices");
+        qh_fprintf(qh, qh->ferr, 6106, "qhull precision error: Vertex sets are the same for f%d and f%d.  Can not force output.\n",
+          facet->id, newfacet->id);
+        qh_errexit2(qh, qh_ERRprec, facet, newfacet);
+      }
+      ismatch= (same == (boolT)((newfacet->toporient ^ facet->toporient)));
+      matchfacet= SETelemt_(facet->neighbors, skip, facetT);
+      if (ismatch && !matchfacet) {
+        SETelem_(facet->neighbors, skip)= newfacet;
+        SETelem_(newfacet->neighbors, newskip)= facet;
+        (*hashcount)--;
+        trace4((qh, qh->ferr, 4051, "qh_matchneighbor: f%d skip %d matched with new f%d skip %d\n",
+           facet->id, skip, newfacet->id, newskip));
+        return;
+      }
+      if (!qh->PREmerge && !qh->MERGEexact) {
+        qh_precision(qh, "a ridge with more than two neighbors");
+        qh_fprintf(qh, qh->ferr, 6107, "qhull precision error: facets f%d, f%d and f%d meet at a ridge with more than 2 neighbors.  Can not continue.\n",
+                 facet->id, newfacet->id, getid_(matchfacet));
+        qh_errexit2(qh, qh_ERRprec, facet, newfacet);
+      }
+      SETelem_(newfacet->neighbors, newskip)= qh_DUPLICATEridge;
+      newfacet->dupridge= True;
+      if (!newfacet->normal)
+        qh_setfacetplane(qh, newfacet);
+      qh_addhash(newfacet, qh->hash_table, hashsize, hash);
+      (*hashcount)++;
+      if (!facet->normal)
+        qh_setfacetplane(qh, facet);
+      if (matchfacet != qh_DUPLICATEridge) {
+        SETelem_(facet->neighbors, skip)= qh_DUPLICATEridge;
+        facet->dupridge= True;
+        if (!facet->normal)
+          qh_setfacetplane(qh, facet);
+        if (matchfacet) {
+          matchskip= qh_setindex(matchfacet->neighbors, facet);
+          if (matchskip<0) {
+              qh_fprintf(qh, qh->ferr, 6260, "qhull internal error (qh_matchneighbor): matchfacet f%d is in f%d neighbors but not vice versa.  Can not continue.\n",
+                  matchfacet->id, facet->id);
+              qh_errexit2(qh, qh_ERRqhull, matchfacet, facet);
+          }
+          SETelem_(matchfacet->neighbors, matchskip)= qh_DUPLICATEridge; /* matchskip>=0 by QH6260 */
+          matchfacet->dupridge= True;
+          if (!matchfacet->normal)
+            qh_setfacetplane(qh, matchfacet);
+          qh_addhash(matchfacet, qh->hash_table, hashsize, hash);
+          *hashcount += 2;
+        }
+      }
+      trace4((qh, qh->ferr, 4052, "qh_matchneighbor: new f%d skip %d duplicates ridge for f%d skip %d matching f%d ismatch %d at hash %d\n",
+           newfacet->id, newskip, facet->id, skip,
+           (matchfacet == qh_DUPLICATEridge ? -2 : getid_(matchfacet)),
+           ismatch, hash));
+      return; /* end of duplicate ridge */
+    }
+  }
+  if (!newfound)
+    SETelem_(qh->hash_table, scan)= newfacet;  /* same as qh_addhash */
+  (*hashcount)++;
+  trace4((qh, qh->ferr, 4053, "qh_matchneighbor: no match for f%d skip %d at hash %d\n",
+           newfacet->id, newskip, hash));
+} /* matchneighbor */
+
+
+/*-<a                             href="qh-poly_r.htm#TOC"
+  >-------------------------------</a><a name="matchnewfacets">-</a>
+
+  qh_matchnewfacets()
+    match newfacets in qh.newfacet_list to their newfacet neighbors
+
+  returns:
+    qh.newfacet_list with full neighbor sets
+      get vertices with nth neighbor by deleting nth vertex
+    if qh.PREmerge/MERGEexact or qh.FORCEoutput
+      sets facet->flippped if flipped normal (also prevents point partitioning)
+    if duplicate ridges and qh.PREmerge/MERGEexact
+      sets facet->dupridge
+      missing neighbor links identifies extra ridges to be merging (qh_MERGEridge)
+
+  notes:
+    newfacets already have neighbor[0] (horizon facet)
+    assumes qh.hash_table is NULL
+    vertex->neighbors has not been updated yet
+    do not allocate memory after qh.hash_table (need to free it cleanly)
+
+  design:
+    delete neighbor sets for all new facets
+    initialize a hash table
+    for all new facets
+      match facet with neighbors
+    if unmatched facets (due to duplicate ridges)
+      for each new facet with a duplicate ridge
+        match it with a facet
+    check for flipped facets
+*/
+void qh_matchnewfacets(qhT *qh /* qh.newfacet_list */) {
+  int numnew=0, hashcount=0, newskip;
+  facetT *newfacet, *neighbor;
+  int dim= qh->hull_dim, hashsize, neighbor_i, neighbor_n;
+  setT *neighbors;
+#ifndef qh_NOtrace
+  int facet_i, facet_n, numfree= 0;
+  facetT *facet;
+#endif
+
+  trace1((qh, qh->ferr, 1019, "qh_matchnewfacets: match neighbors for new facets.\n"));
+  FORALLnew_facets {
+    numnew++;
+    {  /* inline qh_setzero(qh, newfacet->neighbors, 1, qh->hull_dim); */
+      neighbors= newfacet->neighbors;
+      neighbors->e[neighbors->maxsize].i= dim+1; /*may be overwritten*/
+      memset((char *)SETelemaddr_(neighbors, 1, void), 0, dim * SETelemsize);
+    }
+  }
+
+  qh_newhashtable(qh, numnew*(qh->hull_dim-1)); /* twice what is normally needed,
+                                     but every ridge could be DUPLICATEridge */
+  hashsize= qh_setsize(qh, qh->hash_table);
+  FORALLnew_facets {
+    for (newskip=1; newskip<qh->hull_dim; newskip++) /* furthest/horizon already matched */
+      /* hashsize>0 because hull_dim>1 and numnew>0 */
+      qh_matchneighbor(qh, newfacet, newskip, hashsize, &hashcount);
+#if 0   /* use the following to trap hashcount errors */
+    {
+      int count= 0, k;
+      facetT *facet, *neighbor;
+
+      count= 0;
+      FORALLfacet_(qh->newfacet_list) {  /* newfacet already in use */
+        for (k=1; k < qh->hull_dim; k++) {
+          neighbor= SETelemt_(facet->neighbors, k, facetT);
+          if (!neighbor || neighbor == qh_DUPLICATEridge)
+            count++;
+        }
+        if (facet == newfacet)
+          break;
+      }
+      if (count != hashcount) {
+        qh_fprintf(qh, qh->ferr, 8088, "qh_matchnewfacets: after adding facet %d, hashcount %d != count %d\n",
+                 newfacet->id, hashcount, count);
+        qh_errexit(qh, qh_ERRqhull, newfacet, NULL);
+      }
+    }
+#endif  /* end of trap code */
+  }
+  if (hashcount) {
+    FORALLnew_facets {
+      if (newfacet->dupridge) {
+        FOREACHneighbor_i_(qh, newfacet) {
+          if (neighbor == qh_DUPLICATEridge) {
+            qh_matchduplicates(qh, newfacet, neighbor_i, hashsize, &hashcount);
+                    /* this may report MERGEfacet */
+          }
+        }
+      }
+    }
+  }
+  if (hashcount) {
+    qh_fprintf(qh, qh->ferr, 6108, "qhull internal error (qh_matchnewfacets): %d neighbors did not match up\n",
+        hashcount);
+    qh_printhashtable(qh, qh->ferr);
+    qh_errexit(qh, qh_ERRqhull, NULL, NULL);
+  }
+#ifndef qh_NOtrace
+  if (qh->IStracing >= 2) {
+    FOREACHfacet_i_(qh, qh->hash_table) {
+      if (!facet)
+        numfree++;
+    }
+    qh_fprintf(qh, qh->ferr, 8089, "qh_matchnewfacets: %d new facets, %d unused hash entries .  hashsize %d\n",
+             numnew, numfree, qh_setsize(qh, qh->hash_table));
+  }
+#endif /* !qh_NOtrace */
+  qh_setfree(qh, &qh->hash_table);
+  if (qh->PREmerge || qh->MERGEexact) {
+    if (qh->IStracing >= 4)
+      qh_printfacetlist(qh, qh->newfacet_list, NULL, qh_ALL);
+    FORALLnew_facets {
+      if (newfacet->normal)
+        qh_checkflipped(qh, newfacet, NULL, qh_ALL);
+    }
+  }else if (qh->FORCEoutput)
+    qh_checkflipped_all(qh, qh->newfacet_list);  /* prints warnings for flipped */
+} /* matchnewfacets */
+
+
+/*-<a                             href="qh-poly_r.htm#TOC"
+  >-------------------------------</a><a name="matchvertices">-</a>
+
+  qh_matchvertices(qh, firstindex, verticesA, skipA, verticesB, skipB, same )
+    tests whether vertices match with a single skip
+    starts match at firstindex since all new facets have a common vertex
+
+  returns:
+    true if matched vertices
+    skip index for each set
+    sets same iff vertices have the same orientation
+
+  notes:
+    assumes skipA is in A and both sets are the same size
+
+  design:
+    set up pointers
+    scan both sets checking for a match
+    test orientation
+*/
+boolT qh_matchvertices(qhT *qh, int firstindex, setT *verticesA, int skipA,
+       setT *verticesB, int *skipB, boolT *same) {
+  vertexT **elemAp, **elemBp, **skipBp=NULL, **skipAp;
+
+  elemAp= SETelemaddr_(verticesA, firstindex, vertexT);
+  elemBp= SETelemaddr_(verticesB, firstindex, vertexT);
+  skipAp= SETelemaddr_(verticesA, skipA, vertexT);
+  do if (elemAp != skipAp) {
+    while (*elemAp != *elemBp++) {
+      if (skipBp)
+        return False;
+      skipBp= elemBp;  /* one extra like FOREACH */
+    }
+  }while (*(++elemAp));
+  if (!skipBp)
+    skipBp= ++elemBp;
+  *skipB= SETindex_(verticesB, skipB); /* i.e., skipBp - verticesB */
+  *same= !((skipA & 0x1) ^ (*skipB & 0x1)); /* result is 0 or 1 */
+  trace4((qh, qh->ferr, 4054, "qh_matchvertices: matched by skip %d(v%d) and skip %d(v%d) same? %d\n",
+          skipA, (*skipAp)->id, *skipB, (*(skipBp-1))->id, *same));
+  return(True);
+} /* matchvertices */
+
+/*-<a                             href="qh-poly_r.htm#TOC"
+  >-------------------------------</a><a name="newfacet">-</a>
+
+  qh_newfacet(qh)
+    return a new facet
+
+  returns:
+    all fields initialized or cleared   (NULL)
+    preallocates neighbors set
+*/
+facetT *qh_newfacet(qhT *qh) {
+  facetT *facet;
+  void **freelistp; /* used if !qh_NOmem by qh_memalloc_() */
+
+  qh_memalloc_(qh, (int)sizeof(facetT), freelistp, facet, facetT);
+  memset((char *)facet, (size_t)0, sizeof(facetT));
+  if (qh->facet_id == qh->tracefacet_id)
+    qh->tracefacet= facet;
+  facet->id= qh->facet_id++;
+  facet->neighbors= qh_setnew(qh, qh->hull_dim);
+#if !qh_COMPUTEfurthest
+  facet->furthestdist= 0.0;
+#endif
+#if qh_MAXoutside
+  if (qh->FORCEoutput && qh->APPROXhull)
+    facet->maxoutside= qh->MINoutside;
+  else
+    facet->maxoutside= qh->DISTround;
+#endif
+  facet->simplicial= True;
+  facet->good= True;
+  facet->newfacet= True;
+  trace4((qh, qh->ferr, 4055, "qh_newfacet: created facet f%d\n", facet->id));
+  return(facet);
+} /* newfacet */
+
+
+/*-<a                             href="qh-poly_r.htm#TOC"
+  >-------------------------------</a><a name="newridge">-</a>
+
+  qh_newridge()
+    return a new ridge
+*/
+ridgeT *qh_newridge(qhT *qh) {
+  ridgeT *ridge;
+  void **freelistp;   /* used if !qh_NOmem by qh_memalloc_() */
+
+  qh_memalloc_(qh, (int)sizeof(ridgeT), freelistp, ridge, ridgeT);
+  memset((char *)ridge, (size_t)0, sizeof(ridgeT));
+  zinc_(Ztotridges);
+  if (qh->ridge_id == UINT_MAX) {
+    qh_fprintf(qh, qh->ferr, 7074, "\
+qhull warning: more than 2^32 ridges.  Qhull results are OK.  Since the ridge ID wraps around to 0, two ridges may have the same identifier.\n");
+  }
+  ridge->id= qh->ridge_id++;
+  trace4((qh, qh->ferr, 4056, "qh_newridge: created ridge r%d\n", ridge->id));
+  return(ridge);
+} /* newridge */
+
+
+/*-<a                             href="qh-poly_r.htm#TOC"
+  >-------------------------------</a><a name="pointid">-</a>
+
+  qh_pointid(qh, point )
+    return id for a point,
+    returns qh_IDnone(-3) if null, qh_IDinterior(-2) if interior, or qh_IDunknown(-1) if not known
+
+  alternative code if point is in qh.first_point...
+    unsigned long id;
+    id= ((unsigned long)point - (unsigned long)qh.first_point)/qh.normal_size;
+
+  notes:
+    Valid points are non-negative
+    WARN64 -- id truncated to 32-bits, at most 2G points
+    NOerrors returned (QhullPoint::id)
+    if point not in point array
+      the code does a comparison of unrelated pointers.
+*/
+int qh_pointid(qhT *qh, pointT *point) {
+  ptr_intT offset, id;
+
+  if (!point || !qh)
+    return qh_IDnone;
+  else if (point == qh->interior_point)
+    return qh_IDinterior;
+  else if (point >= qh->first_point
+  && point < qh->first_point + qh->num_points * qh->hull_dim) {
+    offset= (ptr_intT)(point - qh->first_point);
+    id= offset / qh->hull_dim;
+  }else if ((id= qh_setindex(qh->other_points, point)) != -1)
+    id += qh->num_points;
+  else
+    return qh_IDunknown;
+  return (int)id;
+} /* pointid */
+
+/*-<a                             href="qh-poly_r.htm#TOC"
+  >-------------------------------</a><a name="removefacet">-</a>
+
+  qh_removefacet(qh, facet )
+    unlinks facet from qh.facet_list,
+
+  returns:
+    updates qh.facet_list .newfacet_list .facet_next visible_list
+    decrements qh.num_facets
+
+  see:
+    qh_appendfacet
+*/
+void qh_removefacet(qhT *qh, facetT *facet) {
+  facetT *next= facet->next, *previous= facet->previous;
+
+  if (facet == qh->newfacet_list)
+    qh->newfacet_list= next;
+  if (facet == qh->facet_next)
+    qh->facet_next= next;
+  if (facet == qh->visible_list)
+    qh->visible_list= next;
+  if (previous) {
+    previous->next= next;
+    next->previous= previous;
+  }else {  /* 1st facet in qh->facet_list */
+    qh->facet_list= next;
+    qh->facet_list->previous= NULL;
+  }
+  qh->num_facets--;
+  trace4((qh, qh->ferr, 4057, "qh_removefacet: remove f%d from facet_list\n", facet->id));
+} /* removefacet */
+
+
+/*-<a                             href="qh-poly_r.htm#TOC"
+  >-------------------------------</a><a name="removevertex">-</a>
+
+  qh_removevertex(qh, vertex )
+    unlinks vertex from qh.vertex_list,
+
+  returns:
+    updates qh.vertex_list .newvertex_list
+    decrements qh.num_vertices
+*/
+void qh_removevertex(qhT *qh, vertexT *vertex) {
+  vertexT *next= vertex->next, *previous= vertex->previous;
+
+  if (vertex == qh->newvertex_list)
+    qh->newvertex_list= next;
+  if (previous) {
+    previous->next= next;
+    next->previous= previous;
+  }else {  /* 1st vertex in qh->vertex_list */
+    qh->vertex_list= vertex->next;
+    qh->vertex_list->previous= NULL;
+  }
+  qh->num_vertices--;
+  trace4((qh, qh->ferr, 4058, "qh_removevertex: remove v%d from vertex_list\n", vertex->id));
+} /* removevertex */
+
+
+/*-<a                             href="qh-poly_r.htm#TOC"
+  >-------------------------------</a><a name="updatevertices">-</a>
+
+  qh_updatevertices()
+    update vertex neighbors and delete interior vertices
+
+  returns:
+    if qh.VERTEXneighbors, updates neighbors for each vertex
+      if qh.newvertex_list,
+         removes visible neighbors  from vertex neighbors
+      if qh.newfacet_list
+         adds new facets to vertex neighbors
+    if qh.visible_list
+       interior vertices added to qh.del_vertices for later partitioning
+
+  design:
+    if qh.VERTEXneighbors
+      deletes references to visible facets from vertex neighbors
+      appends new facets to the neighbor list for each vertex
+      checks all vertices of visible facets
+        removes visible facets from neighbor lists
+        marks unused vertices for deletion
+*/
+void qh_updatevertices(qhT *qh /*qh.newvertex_list, newfacet_list, visible_list*/) {
+  facetT *newfacet= NULL, *neighbor, **neighborp, *visible;
+  vertexT *vertex, **vertexp;
+
+  trace3((qh, qh->ferr, 3013, "qh_updatevertices: delete interior vertices and update vertex->neighbors\n"));
+  if (qh->VERTEXneighbors) {
+    FORALLvertex_(qh->newvertex_list) {
+      FOREACHneighbor_(vertex) {
+        if (neighbor->visible)
+          SETref_(neighbor)= NULL;
+      }
+      qh_setcompact(qh, vertex->neighbors);
+    }
+    FORALLnew_facets {
+      FOREACHvertex_(newfacet->vertices)
+        qh_setappend(qh, &vertex->neighbors, newfacet);
+    }
+    FORALLvisible_facets {
+      FOREACHvertex_(visible->vertices) {
+        if (!vertex->newlist && !vertex->deleted) {
+          FOREACHneighbor_(vertex) { /* this can happen under merging */
+            if (!neighbor->visible)
+              break;
+          }
+          if (neighbor)
+            qh_setdel(vertex->neighbors, visible);
+          else {
+            vertex->deleted= True;
+            qh_setappend(qh, &qh->del_vertices, vertex);
+            trace2((qh, qh->ferr, 2041, "qh_updatevertices: delete vertex p%d(v%d) in f%d\n",
+                  qh_pointid(qh, vertex->point), vertex->id, visible->id));
+          }
+        }
+      }
+    }
+  }else {  /* !VERTEXneighbors */
+    FORALLvisible_facets {
+      FOREACHvertex_(visible->vertices) {
+        if (!vertex->newlist && !vertex->deleted) {
+          vertex->deleted= True;
+          qh_setappend(qh, &qh->del_vertices, vertex);
+          trace2((qh, qh->ferr, 2042, "qh_updatevertices: delete vertex p%d(v%d) in f%d\n",
+                  qh_pointid(qh, vertex->point), vertex->id, visible->id));
+        }
+      }
+    }
+  }
+} /* updatevertices */
+
+
+
diff --git a/C/qset_r.c b/C/qset_r.c
new file mode 100644
--- /dev/null
+++ b/C/qset_r.c
@@ -0,0 +1,1340 @@
+/*<html><pre>  -<a                             href="qh-set_r.htm"
+  >-------------------------------</a><a name="TOP">-</a>
+
+   qset_r.c
+   implements set manipulations needed for quickhull
+
+   see qh-set_r.htm and qset_r.h
+
+   Be careful of strict aliasing (two pointers of different types
+   that reference the same location).  The last slot of a set is
+   either the actual size of the set plus 1, or the NULL terminator
+   of the set (i.e., setelemT).
+
+   Copyright (c) 1993-2015 The Geometry Center.
+   $Id: //main/2015/qhull/src/libqhull_r/qset_r.c#3 $$Change: 2062 $
+   $DateTime: 2016/01/17 13:13:18 $$Author: bbarber $
+*/
+
+#include "libqhull_r.h" /* for qhT and QHULL_CRTDBG */
+#include "qset_r.h"
+#include "mem_r.h"
+#include <stdio.h>
+#include <string.h>
+/*** uncomment here and qhull_ra.h
+     if string.h does not define memcpy()
+#include <memory.h>
+*/
+
+#ifndef qhDEFlibqhull
+typedef struct ridgeT ridgeT;
+typedef struct facetT facetT;
+void    qh_errexit(qhT *qh, int exitcode, facetT *, ridgeT *);
+void    qh_fprintf(qhT *qh, FILE *fp, int msgcode, const char *fmt, ... );
+#  ifdef _MSC_VER  /* Microsoft Visual C++ -- warning level 4 */
+#  pragma warning( disable : 4127)  /* conditional expression is constant */
+#  pragma warning( disable : 4706)  /* assignment within conditional function */
+#  endif
+#endif
+
+/*=============== internal macros ===========================*/
+
+/*============ functions in alphabetical order ===================*/
+
+/*-<a                             href="qh-set_r.htm#TOC"
+  >--------------------------------<a name="setaddnth">-</a>
+
+  qh_setaddnth(qh, setp, nth, newelem)
+    adds newelem as n'th element of sorted or unsorted *setp
+
+  notes:
+    *setp and newelem must be defined
+    *setp may be a temp set
+    nth=0 is first element
+    errors if nth is out of bounds
+
+  design:
+    expand *setp if empty or full
+    move tail of *setp up one
+    insert newelem
+*/
+void qh_setaddnth(qhT *qh, setT **setp, int nth, void *newelem) {
+  int oldsize, i;
+  setelemT *sizep;          /* avoid strict aliasing */
+  setelemT *oldp, *newp;
+
+  if (!*setp || (sizep= SETsizeaddr_(*setp))->i==0) {
+    qh_setlarger(qh, setp);
+    sizep= SETsizeaddr_(*setp);
+  }
+  oldsize= sizep->i - 1;
+  if (nth < 0 || nth > oldsize) {
+    qh_fprintf(qh, qh->qhmem.ferr, 6171, "qhull internal error (qh_setaddnth): nth %d is out-of-bounds for set:\n", nth);
+    qh_setprint(qh, qh->qhmem.ferr, "", *setp);
+    qh_errexit(qh, qhmem_ERRqhull, NULL, NULL);
+  }
+  sizep->i++;
+  oldp= (setelemT *)SETelemaddr_(*setp, oldsize, void);   /* NULL */
+  newp= oldp+1;
+  for (i=oldsize-nth+1; i--; )  /* move at least NULL  */
+    (newp--)->p= (oldp--)->p;       /* may overwrite *sizep */
+  newp->p= newelem;
+} /* setaddnth */
+
+
+/*-<a                              href="qh-set_r.htm#TOC"
+  >--------------------------------<a name="setaddsorted">-</a>
+
+  setaddsorted( setp, newelem )
+    adds an newelem into sorted *setp
+
+  notes:
+    *setp and newelem must be defined
+    *setp may be a temp set
+    nop if newelem already in set
+
+  design:
+    find newelem's position in *setp
+    insert newelem
+*/
+void qh_setaddsorted(qhT *qh, setT **setp, void *newelem) {
+  int newindex=0;
+  void *elem, **elemp;
+
+  FOREACHelem_(*setp) {          /* could use binary search instead */
+    if (elem < newelem)
+      newindex++;
+    else if (elem == newelem)
+      return;
+    else
+      break;
+  }
+  qh_setaddnth(qh, setp, newindex, newelem);
+} /* setaddsorted */
+
+
+/*-<a                             href="qh-set_r.htm#TOC"
+  >-------------------------------<a name="setappend">-</a>
+
+  qh_setappend(qh, setp, newelem)
+    append newelem to *setp
+
+  notes:
+    *setp may be a temp set
+    *setp and newelem may be NULL
+
+  design:
+    expand *setp if empty or full
+    append newelem to *setp
+
+*/
+void qh_setappend(qhT *qh, setT **setp, void *newelem) {
+  setelemT *sizep;  /* Avoid strict aliasing.  Writing to *endp may overwrite *sizep */
+  setelemT *endp;
+  int count;
+
+  if (!newelem)
+    return;
+  if (!*setp || (sizep= SETsizeaddr_(*setp))->i==0) {
+    qh_setlarger(qh, setp);
+    sizep= SETsizeaddr_(*setp);
+  }
+  count= (sizep->i)++ - 1;
+  endp= (setelemT *)SETelemaddr_(*setp, count, void);
+  (endp++)->p= newelem;
+  endp->p= NULL;
+} /* setappend */
+
+/*-<a                             href="qh-set_r.htm#TOC"
+  >-------------------------------<a name="setappend_set">-</a>
+
+  qh_setappend_set(qh, setp, setA)
+    appends setA to *setp
+
+  notes:
+    *setp can not be a temp set
+    *setp and setA may be NULL
+
+  design:
+    setup for copy
+    expand *setp if it is too small
+    append all elements of setA to *setp
+*/
+void qh_setappend_set(qhT *qh, setT **setp, setT *setA) {
+  int sizeA, size;
+  setT *oldset;
+  setelemT *sizep;
+
+  if (!setA)
+    return;
+  SETreturnsize_(setA, sizeA);
+  if (!*setp)
+    *setp= qh_setnew(qh, sizeA);
+  sizep= SETsizeaddr_(*setp);
+  if (!(size= sizep->i))
+    size= (*setp)->maxsize;
+  else
+    size--;
+  if (size + sizeA > (*setp)->maxsize) {
+    oldset= *setp;
+    *setp= qh_setcopy(qh, oldset, sizeA);
+    qh_setfree(qh, &oldset);
+    sizep= SETsizeaddr_(*setp);
+  }
+  if (sizeA > 0) {
+    sizep->i= size+sizeA+1;   /* memcpy may overwrite */
+    memcpy((char *)&((*setp)->e[size].p), (char *)&(setA->e[0].p), (size_t)(sizeA+1) * SETelemsize);
+  }
+} /* setappend_set */
+
+
+/*-<a                             href="qh-set_r.htm#TOC"
+  >-------------------------------<a name="setappend2ndlast">-</a>
+
+  qh_setappend2ndlast(qh, setp, newelem )
+    makes newelem the next to the last element in *setp
+
+  notes:
+    *setp must have at least one element
+    newelem must be defined
+    *setp may be a temp set
+
+  design:
+    expand *setp if empty or full
+    move last element of *setp up one
+    insert newelem
+*/
+void qh_setappend2ndlast(qhT *qh, setT **setp, void *newelem) {
+    setelemT *sizep;  /* Avoid strict aliasing.  Writing to *endp may overwrite *sizep */
+    setelemT *endp, *lastp;
+    int count;
+
+    if (!*setp || (sizep= SETsizeaddr_(*setp))->i==0) {
+        qh_setlarger(qh, setp);
+        sizep= SETsizeaddr_(*setp);
+    }
+    count= (sizep->i)++ - 1;
+    endp= (setelemT *)SETelemaddr_(*setp, count, void); /* NULL */
+    lastp= endp-1;
+    *(endp++)= *lastp;
+    endp->p= NULL;    /* may overwrite *sizep */
+    lastp->p= newelem;
+} /* setappend2ndlast */
+
+/*-<a                             href="qh-set_r.htm#TOC"
+  >-------------------------------<a name="setcheck">-</a>
+
+  qh_setcheck(qh, set, typename, id )
+    check set for validity
+    report errors with typename and id
+
+  design:
+    checks that maxsize, actual size, and NULL terminator agree
+*/
+void qh_setcheck(qhT *qh, setT *set, const char *tname, unsigned id) {
+  int maxsize, size;
+  int waserr= 0;
+
+  if (!set)
+    return;
+  SETreturnsize_(set, size);
+  maxsize= set->maxsize;
+  if (size > maxsize || !maxsize) {
+    qh_fprintf(qh, qh->qhmem.ferr, 6172, "qhull internal error (qh_setcheck): actual size %d of %s%d is greater than max size %d\n",
+             size, tname, id, maxsize);
+    waserr= 1;
+  }else if (set->e[size].p) {
+    qh_fprintf(qh, qh->qhmem.ferr, 6173, "qhull internal error (qh_setcheck): %s%d(size %d max %d) is not null terminated.\n",
+             tname, id, size-1, maxsize);
+    waserr= 1;
+  }
+  if (waserr) {
+    qh_setprint(qh, qh->qhmem.ferr, "ERRONEOUS", set);
+    qh_errexit(qh, qhmem_ERRqhull, NULL, NULL);
+  }
+} /* setcheck */
+
+
+/*-<a                             href="qh-set_r.htm#TOC"
+  >-------------------------------<a name="setcompact">-</a>
+
+  qh_setcompact(qh, set )
+    remove internal NULLs from an unsorted set
+
+  returns:
+    updated set
+
+  notes:
+    set may be NULL
+    it would be faster to swap tail of set into holes, like qh_setdel
+
+  design:
+    setup pointers into set
+    skip NULLs while copying elements to start of set
+    update the actual size
+*/
+void qh_setcompact(qhT *qh, setT *set) {
+  int size;
+  void **destp, **elemp, **endp, **firstp;
+
+  if (!set)
+    return;
+  SETreturnsize_(set, size);
+  destp= elemp= firstp= SETaddr_(set, void);
+  endp= destp + size;
+  while (1) {
+    if (!(*destp++ = *elemp++)) {
+      destp--;
+      if (elemp > endp)
+        break;
+    }
+  }
+  qh_settruncate(qh, set, (int)(destp-firstp));   /* WARN64 */
+} /* setcompact */
+
+
+/*-<a                             href="qh-set_r.htm#TOC"
+  >-------------------------------<a name="setcopy">-</a>
+
+  qh_setcopy(qh, set, extra )
+    make a copy of a sorted or unsorted set with extra slots
+
+  returns:
+    new set
+
+  design:
+    create a newset with extra slots
+    copy the elements to the newset
+
+*/
+setT *qh_setcopy(qhT *qh, setT *set, int extra) {
+  setT *newset;
+  int size;
+
+  if (extra < 0)
+    extra= 0;
+  SETreturnsize_(set, size);
+  newset= qh_setnew(qh, size+extra);
+  SETsizeaddr_(newset)->i= size+1;    /* memcpy may overwrite */
+  memcpy((char *)&(newset->e[0].p), (char *)&(set->e[0].p), (size_t)(size+1) * SETelemsize);
+  return(newset);
+} /* setcopy */
+
+
+/*-<a                             href="qh-set_r.htm#TOC"
+  >-------------------------------<a name="setdel">-</a>
+
+  qh_setdel(set, oldelem )
+    delete oldelem from an unsorted set
+
+  returns:
+    returns oldelem if found
+    returns NULL otherwise
+
+  notes:
+    set may be NULL
+    oldelem must not be NULL;
+    only deletes one copy of oldelem in set
+
+  design:
+    locate oldelem
+    update actual size if it was full
+    move the last element to the oldelem's location
+*/
+void *qh_setdel(setT *set, void *oldelem) {
+  setelemT *sizep;
+  setelemT *elemp;
+  setelemT *lastp;
+
+  if (!set)
+    return NULL;
+  elemp= (setelemT *)SETaddr_(set, void);
+  while (elemp->p != oldelem && elemp->p)
+    elemp++;
+  if (elemp->p) {
+    sizep= SETsizeaddr_(set);
+    if (!(sizep->i)--)         /*  if was a full set */
+      sizep->i= set->maxsize;  /*     *sizep= (maxsize-1)+ 1 */
+    lastp= (setelemT *)SETelemaddr_(set, sizep->i-1, void);
+    elemp->p= lastp->p;      /* may overwrite itself */
+    lastp->p= NULL;
+    return oldelem;
+  }
+  return NULL;
+} /* setdel */
+
+
+/*-<a                             href="qh-set_r.htm#TOC"
+  >-------------------------------<a name="setdellast">-</a>
+
+  qh_setdellast(set)
+    return last element of set or NULL
+
+  notes:
+    deletes element from set
+    set may be NULL
+
+  design:
+    return NULL if empty
+    if full set
+      delete last element and set actual size
+    else
+      delete last element and update actual size
+*/
+void *qh_setdellast(setT *set) {
+  int setsize;  /* actually, actual_size + 1 */
+  int maxsize;
+  setelemT *sizep;
+  void *returnvalue;
+
+  if (!set || !(set->e[0].p))
+    return NULL;
+  sizep= SETsizeaddr_(set);
+  if ((setsize= sizep->i)) {
+    returnvalue= set->e[setsize - 2].p;
+    set->e[setsize - 2].p= NULL;
+    sizep->i--;
+  }else {
+    maxsize= set->maxsize;
+    returnvalue= set->e[maxsize - 1].p;
+    set->e[maxsize - 1].p= NULL;
+    sizep->i= maxsize;
+  }
+  return returnvalue;
+} /* setdellast */
+
+
+/*-<a                             href="qh-set_r.htm#TOC"
+  >-------------------------------<a name="setdelnth">-</a>
+
+  qh_setdelnth(qh, set, nth )
+    deletes nth element from unsorted set
+    0 is first element
+
+  returns:
+    returns the element (needs type conversion)
+
+  notes:
+    errors if nth invalid
+
+  design:
+    setup points and check nth
+    delete nth element and overwrite with last element
+*/
+void *qh_setdelnth(qhT *qh, setT *set, int nth) {
+  void *elem;
+  setelemT *sizep;
+  setelemT *elemp, *lastp;
+
+  sizep= SETsizeaddr_(set);
+  if ((sizep->i--)==0)         /*  if was a full set */
+    sizep->i= set->maxsize;  /*     *sizep= (maxsize-1)+ 1 */
+  if (nth < 0 || nth >= sizep->i) {
+    qh_fprintf(qh, qh->qhmem.ferr, 6174, "qhull internal error (qh_setdelnth): nth %d is out-of-bounds for set:\n", nth);
+    qh_setprint(qh, qh->qhmem.ferr, "", set);
+    qh_errexit(qh, qhmem_ERRqhull, NULL, NULL);
+  }
+  elemp= (setelemT *)SETelemaddr_(set, nth, void); /* nth valid by QH6174 */
+  lastp= (setelemT *)SETelemaddr_(set, sizep->i-1, void);
+  elem= elemp->p;
+  elemp->p= lastp->p;      /* may overwrite itself */
+  lastp->p= NULL;
+  return elem;
+} /* setdelnth */
+
+/*-<a                             href="qh-set_r.htm#TOC"
+  >-------------------------------<a name="setdelnthsorted">-</a>
+
+  qh_setdelnthsorted(qh, set, nth )
+    deletes nth element from sorted set
+
+  returns:
+    returns the element (use type conversion)
+
+  notes:
+    errors if nth invalid
+
+  see also:
+    setnew_delnthsorted
+
+  design:
+    setup points and check nth
+    copy remaining elements down one
+    update actual size
+*/
+void *qh_setdelnthsorted(qhT *qh, setT *set, int nth) {
+  void *elem;
+  setelemT *sizep;
+  setelemT *newp, *oldp;
+
+  sizep= SETsizeaddr_(set);
+  if (nth < 0 || (sizep->i && nth >= sizep->i-1) || nth >= set->maxsize) {
+    qh_fprintf(qh, qh->qhmem.ferr, 6175, "qhull internal error (qh_setdelnthsorted): nth %d is out-of-bounds for set:\n", nth);
+    qh_setprint(qh, qh->qhmem.ferr, "", set);
+    qh_errexit(qh, qhmem_ERRqhull, NULL, NULL);
+  }
+  newp= (setelemT *)SETelemaddr_(set, nth, void);
+  elem= newp->p;
+  oldp= newp+1;
+  while (((newp++)->p= (oldp++)->p))
+    ; /* copy remaining elements and NULL */
+  if ((sizep->i--)==0)         /*  if was a full set */
+    sizep->i= set->maxsize;  /*     *sizep= (max size-1)+ 1 */
+  return elem;
+} /* setdelnthsorted */
+
+
+/*-<a                             href="qh-set_r.htm#TOC"
+  >-------------------------------<a name="setdelsorted">-</a>
+
+  qh_setdelsorted(set, oldelem )
+    deletes oldelem from sorted set
+
+  returns:
+    returns oldelem if it was deleted
+
+  notes:
+    set may be NULL
+
+  design:
+    locate oldelem in set
+    copy remaining elements down one
+    update actual size
+*/
+void *qh_setdelsorted(setT *set, void *oldelem) {
+  setelemT *sizep;
+  setelemT *newp, *oldp;
+
+  if (!set)
+    return NULL;
+  newp= (setelemT *)SETaddr_(set, void);
+  while(newp->p != oldelem && newp->p)
+    newp++;
+  if (newp->p) {
+    oldp= newp+1;
+    while (((newp++)->p= (oldp++)->p))
+      ; /* copy remaining elements */
+    sizep= SETsizeaddr_(set);
+    if ((sizep->i--)==0)    /*  if was a full set */
+      sizep->i= set->maxsize;  /*     *sizep= (max size-1)+ 1 */
+    return oldelem;
+  }
+  return NULL;
+} /* setdelsorted */
+
+
+/*-<a                             href="qh-set_r.htm#TOC"
+  >-------------------------------<a name="setduplicate">-</a>
+
+  qh_setduplicate(qh, set, elemsize )
+    duplicate a set of elemsize elements
+
+  notes:
+    use setcopy if retaining old elements
+
+  design:
+    create a new set
+    for each elem of the old set
+      create a newelem
+      append newelem to newset
+*/
+setT *qh_setduplicate(qhT *qh, setT *set, int elemsize) {
+  void          *elem, **elemp, *newElem;
+  setT          *newSet;
+  int           size;
+
+  if (!(size= qh_setsize(qh, set)))
+    return NULL;
+  newSet= qh_setnew(qh, size);
+  FOREACHelem_(set) {
+    newElem= qh_memalloc(qh, elemsize);
+    memcpy(newElem, elem, (size_t)elemsize);
+    qh_setappend(qh, &newSet, newElem);
+  }
+  return newSet;
+} /* setduplicate */
+
+
+/*-<a                             href="qh-set_r.htm#TOC"
+  >-------------------------------<a name="setendpointer">-</a>
+
+  qh_setendpointer( set )
+    Returns pointer to NULL terminator of a set's elements
+    set can not be NULL
+
+*/
+void **qh_setendpointer(setT *set) {
+
+  setelemT *sizep= SETsizeaddr_(set);
+  int n= sizep->i;
+  return (n ? &set->e[n-1].p : &sizep->p);
+} /* qh_setendpointer */
+
+/*-<a                             href="qh-set_r.htm#TOC"
+  >-------------------------------<a name="setequal">-</a>
+
+  qh_setequal( setA, setB )
+    returns 1 if two sorted sets are equal, otherwise returns 0
+
+  notes:
+    either set may be NULL
+
+  design:
+    check size of each set
+    setup pointers
+    compare elements of each set
+*/
+int qh_setequal(setT *setA, setT *setB) {
+  void **elemAp, **elemBp;
+  int sizeA= 0, sizeB= 0;
+
+  if (setA) {
+    SETreturnsize_(setA, sizeA);
+  }
+  if (setB) {
+    SETreturnsize_(setB, sizeB);
+  }
+  if (sizeA != sizeB)
+    return 0;
+  if (!sizeA)
+    return 1;
+  elemAp= SETaddr_(setA, void);
+  elemBp= SETaddr_(setB, void);
+  if (!memcmp((char *)elemAp, (char *)elemBp, sizeA*SETelemsize))
+    return 1;
+  return 0;
+} /* setequal */
+
+
+/*-<a                             href="qh-set_r.htm#TOC"
+  >-------------------------------<a name="setequal_except">-</a>
+
+  qh_setequal_except( setA, skipelemA, setB, skipelemB )
+    returns 1 if sorted setA and setB are equal except for skipelemA & B
+
+  returns:
+    false if either skipelemA or skipelemB are missing
+
+  notes:
+    neither set may be NULL
+
+    if skipelemB is NULL,
+      can skip any one element of setB
+
+  design:
+    setup pointers
+    search for skipelemA, skipelemB, and mismatches
+    check results
+*/
+int qh_setequal_except(setT *setA, void *skipelemA, setT *setB, void *skipelemB) {
+  void **elemA, **elemB;
+  int skip=0;
+
+  elemA= SETaddr_(setA, void);
+  elemB= SETaddr_(setB, void);
+  while (1) {
+    if (*elemA == skipelemA) {
+      skip++;
+      elemA++;
+    }
+    if (skipelemB) {
+      if (*elemB == skipelemB) {
+        skip++;
+        elemB++;
+      }
+    }else if (*elemA != *elemB) {
+      skip++;
+      if (!(skipelemB= *elemB++))
+        return 0;
+    }
+    if (!*elemA)
+      break;
+    if (*elemA++ != *elemB++)
+      return 0;
+  }
+  if (skip != 2 || *elemB)
+    return 0;
+  return 1;
+} /* setequal_except */
+
+
+/*-<a                             href="qh-set_r.htm#TOC"
+  >-------------------------------<a name="setequal_skip">-</a>
+
+  qh_setequal_skip( setA, skipA, setB, skipB )
+    returns 1 if sorted setA and setB are equal except for elements skipA & B
+
+  returns:
+    false if different size
+
+  notes:
+    neither set may be NULL
+
+  design:
+    setup pointers
+    search for mismatches while skipping skipA and skipB
+*/
+int qh_setequal_skip(setT *setA, int skipA, setT *setB, int skipB) {
+  void **elemA, **elemB, **skipAp, **skipBp;
+
+  elemA= SETaddr_(setA, void);
+  elemB= SETaddr_(setB, void);
+  skipAp= SETelemaddr_(setA, skipA, void);
+  skipBp= SETelemaddr_(setB, skipB, void);
+  while (1) {
+    if (elemA == skipAp)
+      elemA++;
+    if (elemB == skipBp)
+      elemB++;
+    if (!*elemA)
+      break;
+    if (*elemA++ != *elemB++)
+      return 0;
+  }
+  if (*elemB)
+    return 0;
+  return 1;
+} /* setequal_skip */
+
+
+/*-<a                             href="qh-set_r.htm#TOC"
+  >-------------------------------<a name="setfree">-</a>
+
+  qh_setfree(qh, setp )
+    frees the space occupied by a sorted or unsorted set
+
+  returns:
+    sets setp to NULL
+
+  notes:
+    set may be NULL
+
+  design:
+    free array
+    free set
+*/
+void qh_setfree(qhT *qh, setT **setp) {
+  int size;
+  void **freelistp;  /* used if !qh_NOmem by qh_memfree_() */
+
+  if (*setp) {
+    size= sizeof(setT) + ((*setp)->maxsize)*SETelemsize;
+    if (size <= qh->qhmem.LASTsize) {
+      qh_memfree_(qh, *setp, size, freelistp);
+    }else
+      qh_memfree(qh, *setp, size);
+    *setp= NULL;
+  }
+} /* setfree */
+
+
+/*-<a                             href="qh-set_r.htm#TOC"
+  >-------------------------------<a name="setfree2">-</a>
+
+  qh_setfree2(qh, setp, elemsize )
+    frees the space occupied by a set and its elements
+
+  notes:
+    set may be NULL
+
+  design:
+    free each element
+    free set
+*/
+void qh_setfree2(qhT *qh, setT **setp, int elemsize) {
+  void          *elem, **elemp;
+
+  FOREACHelem_(*setp)
+    qh_memfree(qh, elem, elemsize);
+  qh_setfree(qh, setp);
+} /* setfree2 */
+
+
+
+/*-<a                             href="qh-set_r.htm#TOC"
+  >-------------------------------<a name="setfreelong">-</a>
+
+  qh_setfreelong(qh, setp )
+    frees a set only if it's in long memory
+
+  returns:
+    sets setp to NULL if it is freed
+
+  notes:
+    set may be NULL
+
+  design:
+    if set is large
+      free it
+*/
+void qh_setfreelong(qhT *qh, setT **setp) {
+  int size;
+
+  if (*setp) {
+    size= sizeof(setT) + ((*setp)->maxsize)*SETelemsize;
+    if (size > qh->qhmem.LASTsize) {
+      qh_memfree(qh, *setp, size);
+      *setp= NULL;
+    }
+  }
+} /* setfreelong */
+
+
+/*-<a                             href="qh-set_r.htm#TOC"
+  >-------------------------------<a name="setin">-</a>
+
+  qh_setin(set, setelem )
+    returns 1 if setelem is in a set, 0 otherwise
+
+  notes:
+    set may be NULL or unsorted
+
+  design:
+    scans set for setelem
+*/
+int qh_setin(setT *set, void *setelem) {
+  void *elem, **elemp;
+
+  FOREACHelem_(set) {
+    if (elem == setelem)
+      return 1;
+  }
+  return 0;
+} /* setin */
+
+
+/*-<a                             href="qh-set_r.htm#TOC"
+  >-------------------------------<a name="setindex">-</a>
+
+  qh_setindex(set, atelem )
+    returns the index of atelem in set.
+    returns -1, if not in set or maxsize wrong
+
+  notes:
+    set may be NULL and may contain nulls.
+    NOerrors returned (qh_pointid, QhullPoint::id)
+
+  design:
+    checks maxsize
+    scans set for atelem
+*/
+int qh_setindex(setT *set, void *atelem) {
+  void **elem;
+  int size, i;
+
+  if (!set)
+    return -1;
+  SETreturnsize_(set, size);
+  if (size > set->maxsize)
+    return -1;
+  elem= SETaddr_(set, void);
+  for (i=0; i < size; i++) {
+    if (*elem++ == atelem)
+      return i;
+  }
+  return -1;
+} /* setindex */
+
+
+/*-<a                             href="qh-set_r.htm#TOC"
+  >-------------------------------<a name="setlarger">-</a>
+
+  qh_setlarger(qh, oldsetp )
+    returns a larger set that contains all elements of *oldsetp
+
+  notes:
+    the set is at least twice as large
+    if temp set, updates qh->qhmem.tempstack
+
+  design:
+    creates a new set
+    copies the old set to the new set
+    updates pointers in tempstack
+    deletes the old set
+*/
+void qh_setlarger(qhT *qh, setT **oldsetp) {
+  int size= 1;
+  setT *newset, *set, **setp, *oldset;
+  setelemT *sizep;
+  setelemT *newp, *oldp;
+
+  if (*oldsetp) {
+    oldset= *oldsetp;
+    SETreturnsize_(oldset, size);
+    qh->qhmem.cntlarger++;
+    qh->qhmem.totlarger += size+1;
+    newset= qh_setnew(qh, 2 * size);
+    oldp= (setelemT *)SETaddr_(oldset, void);
+    newp= (setelemT *)SETaddr_(newset, void);
+    memcpy((char *)newp, (char *)oldp, (size_t)(size+1) * SETelemsize);
+    sizep= SETsizeaddr_(newset);
+    sizep->i= size+1;
+    FOREACHset_((setT *)qh->qhmem.tempstack) {
+      if (set == oldset)
+        *(setp-1)= newset;
+    }
+    qh_setfree(qh, oldsetp);
+  }else
+    newset= qh_setnew(qh, 3);
+  *oldsetp= newset;
+} /* setlarger */
+
+
+/*-<a                             href="qh-set_r.htm#TOC"
+  >-------------------------------<a name="setlast">-</a>
+
+  qh_setlast( set )
+    return last element of set or NULL (use type conversion)
+
+  notes:
+    set may be NULL
+
+  design:
+    return last element
+*/
+void *qh_setlast(setT *set) {
+  int size;
+
+  if (set) {
+    size= SETsizeaddr_(set)->i;
+    if (!size)
+      return SETelem_(set, set->maxsize - 1);
+    else if (size > 1)
+      return SETelem_(set, size - 2);
+  }
+  return NULL;
+} /* setlast */
+
+
+/*-<a                             href="qh-set_r.htm#TOC"
+  >-------------------------------<a name="setnew">-</a>
+
+  qh_setnew(qh, setsize )
+    creates and allocates space for a set
+
+  notes:
+    setsize means the number of elements (!including the NULL terminator)
+    use qh_settemp/qh_setfreetemp if set is temporary
+
+  design:
+    allocate memory for set
+    roundup memory if small set
+    initialize as empty set
+*/
+setT *qh_setnew(qhT *qh, int setsize) {
+  setT *set;
+  int sizereceived; /* used if !qh_NOmem */
+  int size;
+  void **freelistp; /* used if !qh_NOmem by qh_memalloc_() */
+
+  if (!setsize)
+    setsize++;
+  size= sizeof(setT) + setsize * SETelemsize;
+  if (size>0 && size <= qh->qhmem.LASTsize) {
+    qh_memalloc_(qh, size, freelistp, set, setT);
+#ifndef qh_NOmem
+    sizereceived= qh->qhmem.sizetable[ qh->qhmem.indextable[size]];
+    if (sizereceived > size)
+      setsize += (sizereceived - size)/SETelemsize;
+#endif
+  }else
+    set= (setT*)qh_memalloc(qh, size);
+  set->maxsize= setsize;
+  set->e[setsize].i= 1;
+  set->e[0].p= NULL;
+  return(set);
+} /* setnew */
+
+
+/*-<a                             href="qh-set_r.htm#TOC"
+  >-------------------------------<a name="setnew_delnthsorted">-</a>
+
+  qh_setnew_delnthsorted(qh, set, size, nth, prepend )
+    creates a sorted set not containing nth element
+    if prepend, the first prepend elements are undefined
+
+  notes:
+    set must be defined
+    checks nth
+    see also: setdelnthsorted
+
+  design:
+    create new set
+    setup pointers and allocate room for prepend'ed entries
+    append head of old set to new set
+    append tail of old set to new set
+*/
+setT *qh_setnew_delnthsorted(qhT *qh, setT *set, int size, int nth, int prepend) {
+  setT *newset;
+  void **oldp, **newp;
+  int tailsize= size - nth -1, newsize;
+
+  if (tailsize < 0) {
+    qh_fprintf(qh, qh->qhmem.ferr, 6176, "qhull internal error (qh_setnew_delnthsorted): nth %d is out-of-bounds for set:\n", nth);
+    qh_setprint(qh, qh->qhmem.ferr, "", set);
+    qh_errexit(qh, qhmem_ERRqhull, NULL, NULL);
+  }
+  newsize= size-1 + prepend;
+  newset= qh_setnew(qh, newsize);
+  newset->e[newset->maxsize].i= newsize+1;  /* may be overwritten */
+  oldp= SETaddr_(set, void);
+  newp= SETaddr_(newset, void) + prepend;
+  switch (nth) {
+  case 0:
+    break;
+  case 1:
+    *(newp++)= *oldp++;
+    break;
+  case 2:
+    *(newp++)= *oldp++;
+    *(newp++)= *oldp++;
+    break;
+  case 3:
+    *(newp++)= *oldp++;
+    *(newp++)= *oldp++;
+    *(newp++)= *oldp++;
+    break;
+  case 4:
+    *(newp++)= *oldp++;
+    *(newp++)= *oldp++;
+    *(newp++)= *oldp++;
+    *(newp++)= *oldp++;
+    break;
+  default:
+    memcpy((char *)newp, (char *)oldp, (size_t)nth * SETelemsize);
+    newp += nth;
+    oldp += nth;
+    break;
+  }
+  oldp++;
+  switch (tailsize) {
+  case 0:
+    break;
+  case 1:
+    *(newp++)= *oldp++;
+    break;
+  case 2:
+    *(newp++)= *oldp++;
+    *(newp++)= *oldp++;
+    break;
+  case 3:
+    *(newp++)= *oldp++;
+    *(newp++)= *oldp++;
+    *(newp++)= *oldp++;
+    break;
+  case 4:
+    *(newp++)= *oldp++;
+    *(newp++)= *oldp++;
+    *(newp++)= *oldp++;
+    *(newp++)= *oldp++;
+    break;
+  default:
+    memcpy((char *)newp, (char *)oldp, (size_t)tailsize * SETelemsize);
+    newp += tailsize;
+  }
+  *newp= NULL;
+  return(newset);
+} /* setnew_delnthsorted */
+
+
+/*-<a                             href="qh-set_r.htm#TOC"
+  >-------------------------------<a name="setprint">-</a>
+
+  qh_setprint(qh, fp, string, set )
+    print set elements to fp with identifying string
+
+  notes:
+    never errors
+*/
+void qh_setprint(qhT *qh, FILE *fp, const char* string, setT *set) {
+  int size, k;
+
+  if (!set)
+    qh_fprintf(qh, fp, 9346, "%s set is null\n", string);
+  else {
+    SETreturnsize_(set, size);
+    qh_fprintf(qh, fp, 9347, "%s set=%p maxsize=%d size=%d elems=",
+             string, set, set->maxsize, size);
+    if (size > set->maxsize)
+      size= set->maxsize+1;
+    for (k=0; k < size; k++)
+      qh_fprintf(qh, fp, 9348, " %p", set->e[k].p);
+    qh_fprintf(qh, fp, 9349, "\n");
+  }
+} /* setprint */
+
+/*-<a                             href="qh-set_r.htm#TOC"
+  >-------------------------------<a name="setreplace">-</a>
+
+  qh_setreplace(qh, set, oldelem, newelem )
+    replaces oldelem in set with newelem
+
+  notes:
+    errors if oldelem not in the set
+    newelem may be NULL, but it turns the set into an indexed set (no FOREACH)
+
+  design:
+    find oldelem
+    replace with newelem
+*/
+void qh_setreplace(qhT *qh, setT *set, void *oldelem, void *newelem) {
+  void **elemp;
+
+  elemp= SETaddr_(set, void);
+  while (*elemp != oldelem && *elemp)
+    elemp++;
+  if (*elemp)
+    *elemp= newelem;
+  else {
+    qh_fprintf(qh, qh->qhmem.ferr, 6177, "qhull internal error (qh_setreplace): elem %p not found in set\n",
+       oldelem);
+    qh_setprint(qh, qh->qhmem.ferr, "", set);
+    qh_errexit(qh, qhmem_ERRqhull, NULL, NULL);
+  }
+} /* setreplace */
+
+
+/*-<a                             href="qh-set_r.htm#TOC"
+  >-------------------------------<a name="setsize">-</a>
+
+  qh_setsize(qh, set )
+    returns the size of a set
+
+  notes:
+    errors if set's maxsize is incorrect
+    same as SETreturnsize_(set)
+    same code for qh_setsize [qset_r.c] and QhullSetBase::count
+
+  design:
+    determine actual size of set from maxsize
+*/
+int qh_setsize(qhT *qh, setT *set) {
+  int size;
+  setelemT *sizep;
+
+  if (!set)
+    return(0);
+  sizep= SETsizeaddr_(set);
+  if ((size= sizep->i)) {
+    size--;
+    if (size > set->maxsize) {
+      qh_fprintf(qh, qh->qhmem.ferr, 6178, "qhull internal error (qh_setsize): current set size %d is greater than maximum size %d\n",
+               size, set->maxsize);
+      qh_setprint(qh, qh->qhmem.ferr, "set: ", set);
+      qh_errexit(qh, qhmem_ERRqhull, NULL, NULL);
+    }
+  }else
+    size= set->maxsize;
+  return size;
+} /* setsize */
+
+/*-<a                             href="qh-set_r.htm#TOC"
+  >-------------------------------<a name="settemp">-</a>
+
+  qh_settemp(qh, setsize )
+    return a stacked, temporary set of upto setsize elements
+
+  notes:
+    use settempfree or settempfree_all to release from qh->qhmem.tempstack
+    see also qh_setnew
+
+  design:
+    allocate set
+    append to qh->qhmem.tempstack
+
+*/
+setT *qh_settemp(qhT *qh, int setsize) {
+  setT *newset;
+
+  newset= qh_setnew(qh, setsize);
+  qh_setappend(qh, &qh->qhmem.tempstack, newset);
+  if (qh->qhmem.IStracing >= 5)
+    qh_fprintf(qh, qh->qhmem.ferr, 8123, "qh_settemp: temp set %p of %d elements, depth %d\n",
+       newset, newset->maxsize, qh_setsize(qh, qh->qhmem.tempstack));
+  return newset;
+} /* settemp */
+
+/*-<a                             href="qh-set_r.htm#TOC"
+  >-------------------------------<a name="settempfree">-</a>
+
+  qh_settempfree(qh, set )
+    free temporary set at top of qh->qhmem.tempstack
+
+  notes:
+    nop if set is NULL
+    errors if set not from previous   qh_settemp
+
+  to locate errors:
+    use 'T2' to find source and then find mis-matching qh_settemp
+
+  design:
+    check top of qh->qhmem.tempstack
+    free it
+*/
+void qh_settempfree(qhT *qh, setT **set) {
+  setT *stackedset;
+
+  if (!*set)
+    return;
+  stackedset= qh_settemppop(qh);
+  if (stackedset != *set) {
+    qh_settemppush(qh, stackedset);
+    qh_fprintf(qh, qh->qhmem.ferr, 6179, "qhull internal error (qh_settempfree): set %p(size %d) was not last temporary allocated(depth %d, set %p, size %d)\n",
+             *set, qh_setsize(qh, *set), qh_setsize(qh, qh->qhmem.tempstack)+1,
+             stackedset, qh_setsize(qh, stackedset));
+    qh_errexit(qh, qhmem_ERRqhull, NULL, NULL);
+  }
+  qh_setfree(qh, set);
+} /* settempfree */
+
+/*-<a                             href="qh-set_r.htm#TOC"
+  >-------------------------------<a name="settempfree_all">-</a>
+
+  qh_settempfree_all(qh)
+    free all temporary sets in qh->qhmem.tempstack
+
+  design:
+    for each set in tempstack
+      free set
+    free qh->qhmem.tempstack
+*/
+void qh_settempfree_all(qhT *qh) {
+  setT *set, **setp;
+
+  FOREACHset_(qh->qhmem.tempstack)
+    qh_setfree(qh, &set);
+  qh_setfree(qh, &qh->qhmem.tempstack);
+} /* settempfree_all */
+
+/*-<a                             href="qh-set_r.htm#TOC"
+  >-------------------------------<a name="settemppop">-</a>
+
+  qh_settemppop(qh)
+    pop and return temporary set from qh->qhmem.tempstack
+
+  notes:
+    the returned set is permanent
+
+  design:
+    pop and check top of qh->qhmem.tempstack
+*/
+setT *qh_settemppop(qhT *qh) {
+  setT *stackedset;
+
+  stackedset= (setT*)qh_setdellast(qh->qhmem.tempstack);
+  if (!stackedset) {
+    qh_fprintf(qh, qh->qhmem.ferr, 6180, "qhull internal error (qh_settemppop): pop from empty temporary stack\n");
+    qh_errexit(qh, qhmem_ERRqhull, NULL, NULL);
+  }
+  if (qh->qhmem.IStracing >= 5)
+    qh_fprintf(qh, qh->qhmem.ferr, 8124, "qh_settemppop: depth %d temp set %p of %d elements\n",
+       qh_setsize(qh, qh->qhmem.tempstack)+1, stackedset, qh_setsize(qh, stackedset));
+  return stackedset;
+} /* settemppop */
+
+/*-<a                             href="qh-set_r.htm#TOC"
+  >-------------------------------<a name="settemppush">-</a>
+
+  qh_settemppush(qh, set )
+    push temporary set unto qh->qhmem.tempstack (makes it temporary)
+
+  notes:
+    duplicates settemp() for tracing
+
+  design:
+    append set to tempstack
+*/
+void qh_settemppush(qhT *qh, setT *set) {
+  if (!set) {
+    qh_fprintf(qh, qh->qhmem.ferr, 6267, "qhull error (qh_settemppush): can not push a NULL temp\n");
+    qh_errexit(qh, qhmem_ERRqhull, NULL, NULL);
+  }
+  qh_setappend(qh, &qh->qhmem.tempstack, set);
+  if (qh->qhmem.IStracing >= 5)
+    qh_fprintf(qh, qh->qhmem.ferr, 8125, "qh_settemppush: depth %d temp set %p of %d elements\n",
+      qh_setsize(qh, qh->qhmem.tempstack), set, qh_setsize(qh, set));
+} /* settemppush */
+
+
+/*-<a                             href="qh-set_r.htm#TOC"
+  >-------------------------------<a name="settruncate">-</a>
+
+  qh_settruncate(qh, set, size )
+    truncate set to size elements
+
+  notes:
+    set must be defined
+
+  see:
+    SETtruncate_
+
+  design:
+    check size
+    update actual size of set
+*/
+void qh_settruncate(qhT *qh, setT *set, int size) {
+
+  if (size < 0 || size > set->maxsize) {
+    qh_fprintf(qh, qh->qhmem.ferr, 6181, "qhull internal error (qh_settruncate): size %d out of bounds for set:\n", size);
+    qh_setprint(qh, qh->qhmem.ferr, "", set);
+    qh_errexit(qh, qhmem_ERRqhull, NULL, NULL);
+  }
+  set->e[set->maxsize].i= size+1;   /* maybe overwritten */
+  set->e[size].p= NULL;
+} /* settruncate */
+
+/*-<a                             href="qh-set_r.htm#TOC"
+  >-------------------------------<a name="setunique">-</a>
+
+  qh_setunique(qh, set, elem )
+    add elem to unsorted set unless it is already in set
+
+  notes:
+    returns 1 if it is appended
+
+  design:
+    if elem not in set
+      append elem to set
+*/
+int qh_setunique(qhT *qh, setT **set, void *elem) {
+
+  if (!qh_setin(*set, elem)) {
+    qh_setappend(qh, set, elem);
+    return 1;
+  }
+  return 0;
+} /* setunique */
+
+/*-<a                             href="qh-set_r.htm#TOC"
+  >-------------------------------<a name="setzero">-</a>
+
+  qh_setzero(qh, set, index, size )
+    zero elements from index on
+    set actual size of set to size
+
+  notes:
+    set must be defined
+    the set becomes an indexed set (can not use FOREACH...)
+
+  see also:
+    qh_settruncate
+
+  design:
+    check index and size
+    update actual size
+    zero elements starting at e[index]
+*/
+void qh_setzero(qhT *qh, setT *set, int idx, int size) {
+  int count;
+
+  if (idx < 0 || idx >= size || size > set->maxsize) {
+    qh_fprintf(qh, qh->qhmem.ferr, 6182, "qhull internal error (qh_setzero): index %d or size %d out of bounds for set:\n", idx, size);
+    qh_setprint(qh, qh->qhmem.ferr, "", set);
+    qh_errexit(qh, qhmem_ERRqhull, NULL, NULL);
+  }
+  set->e[set->maxsize].i=  size+1;  /* may be overwritten */
+  count= size - idx + 1;   /* +1 for NULL terminator */
+  memset((char *)SETelemaddr_(set, idx, void), 0, (size_t)count * SETelemsize);
+} /* setzero */
+
+
diff --git a/C/random_r.c b/C/random_r.c
new file mode 100644
--- /dev/null
+++ b/C/random_r.c
@@ -0,0 +1,247 @@
+/*<html><pre>  -<a                             href="index_r.htm#TOC"
+  >-------------------------------</a><a name="TOP">-</a>
+
+   random_r.c and utilities
+     Park & Miller's minimimal standard random number generator
+     argc/argv conversion
+
+     Used by rbox.  Do not use 'qh' 
+*/
+
+#include "libqhull_r.h"
+#include "random_r.h"
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#ifdef _MSC_VER  /* Microsoft Visual C++ -- warning level 4 */
+#pragma warning( disable : 4706)  /* assignment within conditional function */
+#pragma warning( disable : 4996)  /* function was declared deprecated(strcpy, localtime, etc.) */
+#endif
+
+/*-<a                             href="qh-globa_r.htm#TOC"
+ >-------------------------------</a><a name="argv_to_command">-</a>
+
+ qh_argv_to_command(argc, argv, command, max_size )
+
+    build command from argc/argv
+    max_size is at least
+
+ returns:
+    a space-delimited string of options (just as typed)
+    returns false if max_size is too short
+
+ notes:
+    silently removes
+    makes option string easy to input and output
+    matches qh_argv_to_command_size()
+
+    argc may be 0
+*/
+int qh_argv_to_command(int argc, char *argv[], char* command, int max_size) {
+  int i, remaining;
+  char *s;
+  *command= '\0';  /* max_size > 0 */
+
+  if (argc) {
+    if ((s= strrchr( argv[0], '\\')) /* get filename w/o .exe extension */
+    || (s= strrchr( argv[0], '/')))
+        s++;
+    else
+        s= argv[0];
+    if ((int)strlen(s) < max_size)   /* WARN64 */
+        strcpy(command, s);
+    else
+        goto error_argv;
+    if ((s= strstr(command, ".EXE"))
+    ||  (s= strstr(command, ".exe")))
+        *s= '\0';
+  }
+  for (i=1; i < argc; i++) {
+    s= argv[i];
+    remaining= max_size - (int)strlen(command) - (int)strlen(s) - 2;   /* WARN64 */
+    if (!*s || strchr(s, ' ')) {
+      char *t= command + strlen(command);
+      remaining -= 2;
+      if (remaining < 0) {
+        goto error_argv;
+      }
+      *t++= ' ';
+      *t++= '"';
+      while (*s) {
+        if (*s == '"') {
+          if (--remaining < 0)
+            goto error_argv;
+          *t++= '\\';
+        }
+        *t++= *s++;
+      }
+      *t++= '"';
+      *t= '\0';
+    }else if (remaining < 0) {
+      goto error_argv;
+    }else
+      strcat(command, " ");
+      strcat(command, s);
+  }
+  return 1;
+
+error_argv:
+  return 0;
+} /* argv_to_command */
+
+/*-<a                             href="qh-globa_r.htm#TOC"
+>-------------------------------</a><a name="argv_to_command_size">-</a>
+
+qh_argv_to_command_size(argc, argv )
+
+    return size to allocate for qh_argv_to_command()
+
+notes:
+    argc may be 0
+    actual size is usually shorter
+*/
+int qh_argv_to_command_size(int argc, char *argv[]) {
+    unsigned int count= 1; /* null-terminator if argc==0 */
+    int i;
+    char *s;
+
+    for (i=0; i<argc; i++){
+      count += (int)strlen(argv[i]) + 1;   /* WARN64 */
+      if (i>0 && strchr(argv[i], ' ')) {
+        count += 2;  /* quote delimiters */
+        for (s=argv[i]; *s; s++) {
+          if (*s == '"') {
+            count++;
+          }
+        }
+      }
+    }
+    return count;
+} /* argv_to_command_size */
+
+/*-<a                             href="qh-geom_r.htm#TOC"
+  >-------------------------------</a><a name="rand">-</a>
+
+  qh_rand()
+  qh_srand(qh, seed )
+    generate pseudo-random number between 1 and 2^31 -2
+
+  notes:
+    For qhull and rbox, called from qh_RANDOMint(),etc. [user.h]
+
+    From Park & Miller's minimal standard random number generator
+      Communications of the ACM, 31:1192-1201, 1988.
+    Does not use 0 or 2^31 -1
+      this is silently enforced by qh_srand()
+    Can make 'Rn' much faster by moving qh_rand to qh_distplane
+*/
+
+/* Global variables and constants */
+
+#define qh_rand_a 16807
+#define qh_rand_m 2147483647
+#define qh_rand_q 127773  /* m div a */
+#define qh_rand_r 2836    /* m mod a */
+
+int qh_rand(qhT *qh) {
+    int lo, hi, test;
+    int seed = qh->last_random;
+
+    hi = seed / qh_rand_q;  /* seed div q */
+    lo = seed % qh_rand_q;  /* seed mod q */
+    test = qh_rand_a * lo - qh_rand_r * hi;
+    if (test > 0)
+        seed= test;
+    else
+        seed= test + qh_rand_m;
+    qh->last_random= seed;
+    /* seed = seed < qh_RANDOMmax/2 ? 0 : qh_RANDOMmax;  for testing */
+    /* seed = qh_RANDOMmax;  for testing */
+    return seed;
+} /* rand */
+
+void qh_srand(qhT *qh, int seed) {
+    if (seed < 1)
+        qh->last_random= 1;
+    else if (seed >= qh_rand_m)
+        qh->last_random= qh_rand_m - 1;
+    else
+        qh->last_random= seed;
+} /* qh_srand */
+
+/*-<a                             href="qh-geom_r.htm#TOC"
+>-------------------------------</a><a name="randomfactor">-</a>
+
+qh_randomfactor(qh, scale, offset )
+  return a random factor r * scale + offset
+
+notes:
+  qh.RANDOMa/b are defined in global_r.c
+  qh_RANDOMint requires 'qh'
+*/
+realT qh_randomfactor(qhT *qh, realT scale, realT offset) {
+    realT randr;
+
+    randr= qh_RANDOMint;
+    return randr * scale + offset;
+} /* randomfactor */
+
+/*-<a                             href="qh-geom_r.htm#TOC"
+>-------------------------------</a><a name="randommatrix">-</a>
+
+qh_randommatrix(qh, buffer, dim, rows )
+  generate a random dim X dim matrix in range [-1,1]
+  assumes buffer is [dim+1, dim]
+
+  returns:
+    sets buffer to random numbers
+    sets rows to rows of buffer
+    sets row[dim] as scratch row
+
+  notes:
+    qh_RANDOMint requires 'qh'
+*/
+void qh_randommatrix(qhT *qh, realT *buffer, int dim, realT **rows) {
+    int i, k;
+    realT **rowi, *coord, realr;
+
+    coord= buffer;
+    rowi= rows;
+    for (i=0; i < dim; i++) {
+        *(rowi++)= coord;
+        for (k=0; k < dim; k++) {
+            realr= qh_RANDOMint;
+            *(coord++)= 2.0 * realr/(qh_RANDOMmax+1) - 1.0;
+        }
+    }
+    *rowi= coord;
+} /* randommatrix */
+
+/*-<a                             href="qh-globa_r.htm#TOC"
+  >-------------------------------</a><a name="strtol">-</a>
+
+  qh_strtol( s, endp) qh_strtod( s, endp)
+    internal versions of strtol() and strtod()
+    does not skip trailing spaces
+  notes:
+    some implementations of strtol()/strtod() skip trailing spaces
+*/
+double qh_strtod(const char *s, char **endp) {
+  double result;
+
+  result= strtod(s, endp);
+  if (s < (*endp) && (*endp)[-1] == ' ')
+    (*endp)--;
+  return result;
+} /* strtod */
+
+int qh_strtol(const char *s, char **endp) {
+  int result;
+
+  result= (int) strtol(s, endp, 10);     /* WARN64 */
+  if (s< (*endp) && (*endp)[-1] == ' ')
+    (*endp)--;
+  return result;
+} /* strtol */
diff --git a/C/stat_r.c b/C/stat_r.c
new file mode 100644
--- /dev/null
+++ b/C/stat_r.c
@@ -0,0 +1,682 @@
+/*<html><pre>  -<a                             href="qh-stat_r.htm"
+  >-------------------------------</a><a name="TOP">-</a>
+
+   stat_r.c
+   contains all statistics that are collected for qhull
+
+   see qh-stat_r.htm and stat_r.h
+
+   Copyright (c) 1993-2015 The Geometry Center.
+   $Id: //main/2015/qhull/src/libqhull_r/stat_r.c#5 $$Change: 2062 $
+   $DateTime: 2016/01/17 13:13:18 $$Author: bbarber $
+*/
+
+#include "qhull_ra.h"
+
+/*========== functions in alphabetic order ================*/
+
+/*-<a                             href="qh-stat_r.htm#TOC"
+  >-------------------------------</a><a name="allstatA">-</a>
+
+  qh_allstatA()
+    define statistics in groups of 20
+
+  notes:
+    (otherwise, 'gcc -O2' uses too much memory)
+    uses qhstat.next
+*/
+void qh_allstatA(qhT *qh) {
+
+   /* zdef_(type,name,doc,average) */
+  zzdef_(zdoc, Zdoc2, "precision statistics", -1);
+  zdef_(zinc, Znewvertex, NULL, -1);
+  zdef_(wadd, Wnewvertex, "ave. distance of a new vertex to a facet(!0s)", Znewvertex);
+  zzdef_(wmax, Wnewvertexmax, "max. distance of a new vertex to a facet", -1);
+  zdef_(wmax, Wvertexmax, "max. distance of an output vertex to a facet", -1);
+  zdef_(wmin, Wvertexmin, "min. distance of an output vertex to a facet", -1);
+  zdef_(wmin, Wmindenom, "min. denominator in hyperplane computation", -1);
+
+  qh->qhstat.precision= qh->qhstat.next;  /* call qh_precision for each of these */
+  zzdef_(zdoc, Zdoc3, "precision problems (corrected unless 'Q0' or an error)", -1);
+  zzdef_(zinc, Zcoplanarridges, "coplanar half ridges in output", -1);
+  zzdef_(zinc, Zconcaveridges, "concave half ridges in output", -1);
+  zzdef_(zinc, Zflippedfacets, "flipped facets", -1);
+  zzdef_(zinc, Zcoplanarhorizon, "coplanar horizon facets for new vertices", -1);
+  zzdef_(zinc, Zcoplanarpart, "coplanar points during partitioning", -1);
+  zzdef_(zinc, Zminnorm, "degenerate hyperplanes recomputed with gaussian elimination", -1);
+  zzdef_(zinc, Znearlysingular, "nearly singular or axis-parallel hyperplanes", -1);
+  zzdef_(zinc, Zback0, "zero divisors during back substitute", -1);
+  zzdef_(zinc, Zgauss0, "zero divisors during gaussian elimination", -1);
+  zzdef_(zinc, Zmultiridge, "ridges with multiple neighbors", -1);
+}
+void qh_allstatB(qhT *qh) {
+  zzdef_(zdoc, Zdoc1, "summary information", -1);
+  zdef_(zinc, Zvertices, "number of vertices in output", -1);
+  zdef_(zinc, Znumfacets, "number of facets in output", -1);
+  zdef_(zinc, Znonsimplicial, "number of non-simplicial facets in output", -1);
+  zdef_(zinc, Znowsimplicial, "number of simplicial facets that were merged", -1);
+  zdef_(zinc, Znumridges, "number of ridges in output", -1);
+  zdef_(zadd, Znumridges, "average number of ridges per facet", Znumfacets);
+  zdef_(zmax, Zmaxridges, "maximum number of ridges", -1);
+  zdef_(zadd, Znumneighbors, "average number of neighbors per facet", Znumfacets);
+  zdef_(zmax, Zmaxneighbors, "maximum number of neighbors", -1);
+  zdef_(zadd, Znumvertices, "average number of vertices per facet", Znumfacets);
+  zdef_(zmax, Zmaxvertices, "maximum number of vertices", -1);
+  zdef_(zadd, Znumvneighbors, "average number of neighbors per vertex", Zvertices);
+  zdef_(zmax, Zmaxvneighbors, "maximum number of neighbors", -1);
+  zdef_(wadd, Wcpu, "cpu seconds for qhull after input", -1);
+  zdef_(zinc, Ztotvertices, "vertices created altogether", -1);
+  zzdef_(zinc, Zsetplane, "facets created altogether", -1);
+  zdef_(zinc, Ztotridges, "ridges created altogether", -1);
+  zdef_(zinc, Zpostfacets, "facets before post merge", -1);
+  zdef_(zadd, Znummergetot, "average merges per facet(at most 511)", Znumfacets);
+  zdef_(zmax, Znummergemax, "  maximum merges for a facet(at most 511)", -1);
+  zdef_(zinc, Zangle, NULL, -1);
+  zdef_(wadd, Wangle, "average angle(cosine) of facet normals for all ridges", Zangle);
+  zdef_(wmax, Wanglemax, "  maximum angle(cosine) of facet normals across a ridge", -1);
+  zdef_(wmin, Wanglemin, "  minimum angle(cosine) of facet normals across a ridge", -1);
+  zdef_(wadd, Wareatot, "total area of facets", -1);
+  zdef_(wmax, Wareamax, "  maximum facet area", -1);
+  zdef_(wmin, Wareamin, "  minimum facet area", -1);
+}
+void qh_allstatC(qhT *qh) {
+  zdef_(zdoc, Zdoc9, "build hull statistics", -1);
+  zzdef_(zinc, Zprocessed, "points processed", -1);
+  zzdef_(zinc, Zretry, "retries due to precision problems", -1);
+  zdef_(wmax, Wretrymax, "  max. random joggle", -1);
+  zdef_(zmax, Zmaxvertex, "max. vertices at any one time", -1);
+  zdef_(zinc, Ztotvisible, "ave. visible facets per iteration", Zprocessed);
+  zdef_(zinc, Zinsidevisible, "  ave. visible facets without an horizon neighbor", Zprocessed);
+  zdef_(zadd, Zvisfacettot,  "  ave. facets deleted per iteration", Zprocessed);
+  zdef_(zmax, Zvisfacetmax,  "    maximum", -1);
+  zdef_(zadd, Zvisvertextot, "ave. visible vertices per iteration", Zprocessed);
+  zdef_(zmax, Zvisvertexmax, "    maximum", -1);
+  zdef_(zinc, Ztothorizon, "ave. horizon facets per iteration", Zprocessed);
+  zdef_(zadd, Znewfacettot,  "ave. new or merged facets per iteration", Zprocessed);
+  zdef_(zmax, Znewfacetmax,  "    maximum(includes initial simplex)", -1);
+  zdef_(wadd, Wnewbalance, "average new facet balance", Zprocessed);
+  zdef_(wadd, Wnewbalance2, "  standard deviation", -1);
+  zdef_(wadd, Wpbalance, "average partition balance", Zpbalance);
+  zdef_(wadd, Wpbalance2, "  standard deviation", -1);
+  zdef_(zinc, Zpbalance, "  number of trials", -1);
+  zdef_(zinc, Zsearchpoints, "searches of all points for initial simplex", -1);
+  zdef_(zinc, Zdetsimplex, "determinants computed(area & initial hull)", -1);
+  zdef_(zinc, Znoarea, "determinants not computed because vertex too low", -1);
+  zdef_(zinc, Znotmax, "points ignored(!above max_outside)", -1);
+  zdef_(zinc, Znotgood, "points ignored(!above a good facet)", -1);
+  zdef_(zinc, Znotgoodnew, "points ignored(didn't create a good new facet)", -1);
+  zdef_(zinc, Zgoodfacet, "good facets found", -1);
+  zzdef_(zinc, Znumvisibility, "distance tests for facet visibility", -1);
+  zdef_(zinc, Zdistvertex, "distance tests to report minimum vertex", -1);
+  zzdef_(zinc, Ztotcheck, "points checked for facets' outer planes", -1);
+  zzdef_(zinc, Zcheckpart, "  ave. distance tests per check", Ztotcheck);
+}
+void qh_allstatD(qhT *qh) {
+  zdef_(zinc, Zvisit, "resets of visit_id", -1);
+  zdef_(zinc, Zvvisit, "  resets of vertex_visit", -1);
+  zdef_(zmax, Zvisit2max, "  max visit_id/2", -1);
+  zdef_(zmax, Zvvisit2max, "  max vertex_visit/2", -1);
+
+  zdef_(zdoc, Zdoc4, "partitioning statistics(see previous for outer planes)", -1);
+  zzdef_(zadd, Zdelvertextot, "total vertices deleted", -1);
+  zdef_(zmax, Zdelvertexmax, "    maximum vertices deleted per iteration", -1);
+  zdef_(zinc, Zfindbest, "calls to findbest", -1);
+  zdef_(zadd, Zfindbesttot, " ave. facets tested", Zfindbest);
+  zdef_(zmax, Zfindbestmax, " max. facets tested", -1);
+  zdef_(zadd, Zfindcoplanar, " ave. coplanar search", Zfindbest);
+  zdef_(zinc, Zfindnew, "calls to findbestnew", -1);
+  zdef_(zadd, Zfindnewtot, " ave. facets tested", Zfindnew);
+  zdef_(zmax, Zfindnewmax, " max. facets tested", -1);
+  zdef_(zinc, Zfindnewjump, " ave. clearly better", Zfindnew);
+  zdef_(zinc, Zfindnewsharp, " calls due to qh_sharpnewfacets", -1);
+  zdef_(zinc, Zfindhorizon, "calls to findhorizon", -1);
+  zdef_(zadd, Zfindhorizontot, " ave. facets tested", Zfindhorizon);
+  zdef_(zmax, Zfindhorizonmax, " max. facets tested", -1);
+  zdef_(zinc, Zfindjump,       " ave. clearly better", Zfindhorizon);
+  zdef_(zinc, Zparthorizon, " horizon facets better than bestfacet", -1);
+  zdef_(zinc, Zpartangle, "angle tests for repartitioned coplanar points", -1);
+  zdef_(zinc, Zpartflip, "  repartitioned coplanar points for flipped orientation", -1);
+}
+void qh_allstatE(qhT *qh) {
+  zdef_(zinc, Zpartinside, "inside points", -1);
+  zdef_(zinc, Zpartnear, "  inside points kept with a facet", -1);
+  zdef_(zinc, Zcoplanarinside, "  inside points that were coplanar with a facet", -1);
+  zdef_(zinc, Zbestlower, "calls to findbestlower", -1);
+  zdef_(zinc, Zbestlowerv, "  with search of vertex neighbors", -1);
+  zdef_(zinc, Zbestlowerall, "  with rare search of all facets", -1);
+  zdef_(zmax, Zbestloweralln, "  facets per search of all facets", -1);
+  zdef_(wadd, Wmaxout, "difference in max_outside at final check", -1);
+  zzdef_(zinc, Zpartitionall, "distance tests for initial partition", -1);
+  zdef_(zinc, Ztotpartition, "partitions of a point", -1);
+  zzdef_(zinc, Zpartition, "distance tests for partitioning", -1);
+  zzdef_(zinc, Zdistcheck, "distance tests for checking flipped facets", -1);
+  zzdef_(zinc, Zdistconvex, "distance tests for checking convexity", -1);
+  zdef_(zinc, Zdistgood, "distance tests for checking good point", -1);
+  zdef_(zinc, Zdistio, "distance tests for output", -1);
+  zdef_(zinc, Zdiststat, "distance tests for statistics", -1);
+  zdef_(zinc, Zdistplane, "total number of distance tests", -1);
+  zdef_(zinc, Ztotpartcoplanar, "partitions of coplanar points or deleted vertices", -1);
+  zzdef_(zinc, Zpartcoplanar, "   distance tests for these partitions", -1);
+  zdef_(zinc, Zcomputefurthest, "distance tests for computing furthest", -1);
+}
+void qh_allstatE2(qhT *qh) {
+  zdef_(zdoc, Zdoc5, "statistics for matching ridges", -1);
+  zdef_(zinc, Zhashlookup, "total lookups for matching ridges of new facets", -1);
+  zdef_(zinc, Zhashtests, "average number of tests to match a ridge", Zhashlookup);
+  zdef_(zinc, Zhashridge, "total lookups of subridges(duplicates and boundary)", -1);
+  zdef_(zinc, Zhashridgetest, "average number of tests per subridge", Zhashridge);
+  zdef_(zinc, Zdupsame, "duplicated ridges in same merge cycle", -1);
+  zdef_(zinc, Zdupflip, "duplicated ridges with flipped facets", -1);
+
+  zdef_(zdoc, Zdoc6, "statistics for determining merges", -1);
+  zdef_(zinc, Zangletests, "angles computed for ridge convexity", -1);
+  zdef_(zinc, Zbestcentrum, "best merges used centrum instead of vertices",-1);
+  zzdef_(zinc, Zbestdist, "distance tests for best merge", -1);
+  zzdef_(zinc, Zcentrumtests, "distance tests for centrum convexity", -1);
+  zzdef_(zinc, Zdistzero, "distance tests for checking simplicial convexity", -1);
+  zdef_(zinc, Zcoplanarangle, "coplanar angles in getmergeset", -1);
+  zdef_(zinc, Zcoplanarcentrum, "coplanar centrums in getmergeset", -1);
+  zdef_(zinc, Zconcaveridge, "concave ridges in getmergeset", -1);
+}
+void qh_allstatF(qhT *qh) {
+  zdef_(zdoc, Zdoc7, "statistics for merging", -1);
+  zdef_(zinc, Zpremergetot, "merge iterations", -1);
+  zdef_(zadd, Zmergeinittot, "ave. initial non-convex ridges per iteration", Zpremergetot);
+  zdef_(zadd, Zmergeinitmax, "  maximum", -1);
+  zdef_(zadd, Zmergesettot, "  ave. additional non-convex ridges per iteration", Zpremergetot);
+  zdef_(zadd, Zmergesetmax, "  maximum additional in one pass", -1);
+  zdef_(zadd, Zmergeinittot2, "initial non-convex ridges for post merging", -1);
+  zdef_(zadd, Zmergesettot2, "  additional non-convex ridges", -1);
+  zdef_(wmax, Wmaxoutside, "max distance of vertex or coplanar point above facet(w/roundoff)", -1);
+  zdef_(wmin, Wminvertex, "max distance of merged vertex below facet(or roundoff)", -1);
+  zdef_(zinc, Zwidefacet, "centrums frozen due to a wide merge", -1);
+  zdef_(zinc, Zwidevertices, "centrums frozen due to extra vertices", -1);
+  zzdef_(zinc, Ztotmerge, "total number of facets or cycles of facets merged", -1);
+  zdef_(zinc, Zmergesimplex, "merged a simplex", -1);
+  zdef_(zinc, Zonehorizon, "simplices merged into coplanar horizon", -1);
+  zzdef_(zinc, Zcyclehorizon, "cycles of facets merged into coplanar horizon", -1);
+  zzdef_(zadd, Zcyclefacettot, "  ave. facets per cycle", Zcyclehorizon);
+  zdef_(zmax, Zcyclefacetmax, "  max. facets", -1);
+  zdef_(zinc, Zmergeintohorizon, "new facets merged into horizon", -1);
+  zdef_(zinc, Zmergenew, "new facets merged", -1);
+  zdef_(zinc, Zmergehorizon, "horizon facets merged into new facets", -1);
+  zdef_(zinc, Zmergevertex, "vertices deleted by merging", -1);
+  zdef_(zinc, Zcyclevertex, "vertices deleted by merging into coplanar horizon", -1);
+  zdef_(zinc, Zdegenvertex, "vertices deleted by degenerate facet", -1);
+  zdef_(zinc, Zmergeflipdup, "merges due to flipped facets in duplicated ridge", -1);
+  zdef_(zinc, Zneighbor, "merges due to redundant neighbors", -1);
+  zdef_(zadd, Ztestvneighbor, "non-convex vertex neighbors", -1);
+}
+void qh_allstatG(qhT *qh) {
+  zdef_(zinc, Zacoplanar, "merges due to angle coplanar facets", -1);
+  zdef_(wadd, Wacoplanartot, "  average merge distance", Zacoplanar);
+  zdef_(wmax, Wacoplanarmax, "  maximum merge distance", -1);
+  zdef_(zinc, Zcoplanar, "merges due to coplanar facets", -1);
+  zdef_(wadd, Wcoplanartot, "  average merge distance", Zcoplanar);
+  zdef_(wmax, Wcoplanarmax, "  maximum merge distance", -1);
+  zdef_(zinc, Zconcave, "merges due to concave facets", -1);
+  zdef_(wadd, Wconcavetot, "  average merge distance", Zconcave);
+  zdef_(wmax, Wconcavemax, "  maximum merge distance", -1);
+  zdef_(zinc, Zavoidold, "coplanar/concave merges due to avoiding old merge", -1);
+  zdef_(wadd, Wavoidoldtot, "  average merge distance", Zavoidold);
+  zdef_(wmax, Wavoidoldmax, "  maximum merge distance", -1);
+  zdef_(zinc, Zdegen, "merges due to degenerate facets", -1);
+  zdef_(wadd, Wdegentot, "  average merge distance", Zdegen);
+  zdef_(wmax, Wdegenmax, "  maximum merge distance", -1);
+  zdef_(zinc, Zflipped, "merges due to removing flipped facets", -1);
+  zdef_(wadd, Wflippedtot, "  average merge distance", Zflipped);
+  zdef_(wmax, Wflippedmax, "  maximum merge distance", -1);
+  zdef_(zinc, Zduplicate, "merges due to duplicated ridges", -1);
+  zdef_(wadd, Wduplicatetot, "  average merge distance", Zduplicate);
+  zdef_(wmax, Wduplicatemax, "  maximum merge distance", -1);
+}
+void qh_allstatH(qhT *qh) {
+  zdef_(zdoc, Zdoc8, "renamed vertex statistics", -1);
+  zdef_(zinc, Zrenameshare, "renamed vertices shared by two facets", -1);
+  zdef_(zinc, Zrenamepinch, "renamed vertices in a pinched facet", -1);
+  zdef_(zinc, Zrenameall, "renamed vertices shared by multiple facets", -1);
+  zdef_(zinc, Zfindfail, "rename failures due to duplicated ridges", -1);
+  zdef_(zinc, Zdupridge, "  duplicate ridges detected", -1);
+  zdef_(zinc, Zdelridge, "deleted ridges due to renamed vertices", -1);
+  zdef_(zinc, Zdropneighbor, "dropped neighbors due to renamed vertices", -1);
+  zdef_(zinc, Zdropdegen, "degenerate facets due to dropped neighbors", -1);
+  zdef_(zinc, Zdelfacetdup, "  facets deleted because of no neighbors", -1);
+  zdef_(zinc, Zremvertex, "vertices removed from facets due to no ridges", -1);
+  zdef_(zinc, Zremvertexdel, "  deleted", -1);
+  zdef_(zinc, Zintersectnum, "vertex intersections for locating redundant vertices", -1);
+  zdef_(zinc, Zintersectfail, "intersections failed to find a redundant vertex", -1);
+  zdef_(zinc, Zintersect, "intersections found redundant vertices", -1);
+  zdef_(zadd, Zintersecttot, "   ave. number found per vertex", Zintersect);
+  zdef_(zmax, Zintersectmax, "   max. found for a vertex", -1);
+  zdef_(zinc, Zvertexridge, NULL, -1);
+  zdef_(zadd, Zvertexridgetot, "  ave. number of ridges per tested vertex", Zvertexridge);
+  zdef_(zmax, Zvertexridgemax, "  max. number of ridges per tested vertex", -1);
+
+  zdef_(zdoc, Zdoc10, "memory usage statistics(in bytes)", -1);
+  zdef_(zadd, Zmemfacets, "for facets and their normals, neighbor and vertex sets", -1);
+  zdef_(zadd, Zmemvertices, "for vertices and their neighbor sets", -1);
+  zdef_(zadd, Zmempoints, "for input points, outside and coplanar sets, and qhT",-1);
+  zdef_(zadd, Zmemridges, "for ridges and their vertex sets", -1);
+} /* allstat */
+
+void qh_allstatI(qhT *qh) {
+  qh->qhstat.vridges= qh->qhstat.next;
+  zzdef_(zdoc, Zdoc11, "Voronoi ridge statistics", -1);
+  zzdef_(zinc, Zridge, "non-simplicial Voronoi vertices for all ridges", -1);
+  zzdef_(wadd, Wridge, "  ave. distance to ridge", Zridge);
+  zzdef_(wmax, Wridgemax, "  max. distance to ridge", -1);
+  zzdef_(zinc, Zridgemid, "bounded ridges", -1);
+  zzdef_(wadd, Wridgemid, "  ave. distance of midpoint to ridge", Zridgemid);
+  zzdef_(wmax, Wridgemidmax, "  max. distance of midpoint to ridge", -1);
+  zzdef_(zinc, Zridgeok, "bounded ridges with ok normal", -1);
+  zzdef_(wadd, Wridgeok, "  ave. angle to ridge", Zridgeok);
+  zzdef_(wmax, Wridgeokmax, "  max. angle to ridge", -1);
+  zzdef_(zinc, Zridge0, "bounded ridges with near-zero normal", -1);
+  zzdef_(wadd, Wridge0, "  ave. angle to ridge", Zridge0);
+  zzdef_(wmax, Wridge0max, "  max. angle to ridge", -1);
+
+  zdef_(zdoc, Zdoc12, "Triangulation statistics(Qt)", -1);
+  zdef_(zinc, Ztricoplanar, "non-simplicial facets triangulated", -1);
+  zdef_(zadd, Ztricoplanartot, "  ave. new facets created(may be deleted)", Ztricoplanar);
+  zdef_(zmax, Ztricoplanarmax, "  max. new facets created", -1);
+  zdef_(zinc, Ztrinull, "null new facets deleted(duplicated vertex)", -1);
+  zdef_(zinc, Ztrimirror, "mirrored pairs of new facets deleted(same vertices)", -1);
+  zdef_(zinc, Ztridegen, "degenerate new facets in output(same ridge)", -1);
+} /* allstat */
+
+/*-<a                             href="qh-stat_r.htm#TOC"
+  >-------------------------------</a><a name="allstatistics">-</a>
+
+  qh_allstatistics()
+    reset printed flag for all statistics
+*/
+void qh_allstatistics(qhT *qh) {
+  int i;
+
+  for(i=ZEND; i--; )
+    qh->qhstat.printed[i]= False;
+} /* allstatistics */
+
+#if qh_KEEPstatistics
+/*-<a                             href="qh-stat_r.htm#TOC"
+  >-------------------------------</a><a name="collectstatistics">-</a>
+
+  qh_collectstatistics()
+    collect statistics for qh.facet_list
+
+*/
+void qh_collectstatistics(qhT *qh) {
+  facetT *facet, *neighbor, **neighborp;
+  vertexT *vertex, **vertexp;
+  realT dotproduct, dist;
+  int sizneighbors, sizridges, sizvertices, i;
+
+  qh->old_randomdist= qh->RANDOMdist;
+  qh->RANDOMdist= False;
+  zval_(Zmempoints)= qh->num_points * qh->normal_size + sizeof(qhT);
+  zval_(Zmemfacets)= 0;
+  zval_(Zmemridges)= 0;
+  zval_(Zmemvertices)= 0;
+  zval_(Zangle)= 0;
+  wval_(Wangle)= 0.0;
+  zval_(Znumridges)= 0;
+  zval_(Znumfacets)= 0;
+  zval_(Znumneighbors)= 0;
+  zval_(Znumvertices)= 0;
+  zval_(Znumvneighbors)= 0;
+  zval_(Znummergetot)= 0;
+  zval_(Znummergemax)= 0;
+  zval_(Zvertices)= qh->num_vertices - qh_setsize(qh, qh->del_vertices);
+  if (qh->MERGING || qh->APPROXhull || qh->JOGGLEmax < REALmax/2)
+    wmax_(Wmaxoutside, qh->max_outside);
+  if (qh->MERGING)
+    wmin_(Wminvertex, qh->min_vertex);
+  FORALLfacets
+    facet->seen= False;
+  if (qh->DELAUNAY) {
+    FORALLfacets {
+      if (facet->upperdelaunay != qh->UPPERdelaunay)
+        facet->seen= True; /* remove from angle statistics */
+    }
+  }
+  FORALLfacets {
+    if (facet->visible && qh->NEWfacets)
+      continue;
+    sizvertices= qh_setsize(qh, facet->vertices);
+    sizneighbors= qh_setsize(qh, facet->neighbors);
+    sizridges= qh_setsize(qh, facet->ridges);
+    zinc_(Znumfacets);
+    zadd_(Znumvertices, sizvertices);
+    zmax_(Zmaxvertices, sizvertices);
+    zadd_(Znumneighbors, sizneighbors);
+    zmax_(Zmaxneighbors, sizneighbors);
+    zadd_(Znummergetot, facet->nummerge);
+    i= facet->nummerge; /* avoid warnings */
+    zmax_(Znummergemax, i);
+    if (!facet->simplicial) {
+      if (sizvertices == qh->hull_dim) {
+        zinc_(Znowsimplicial);
+      }else {
+        zinc_(Znonsimplicial);
+      }
+    }
+    if (sizridges) {
+      zadd_(Znumridges, sizridges);
+      zmax_(Zmaxridges, sizridges);
+    }
+    zadd_(Zmemfacets, sizeof(facetT) + qh->normal_size + 2*sizeof(setT)
+       + SETelemsize * (sizneighbors + sizvertices));
+    if (facet->ridges) {
+      zadd_(Zmemridges,
+         sizeof(setT) + SETelemsize * sizridges + sizridges *
+         (sizeof(ridgeT) + sizeof(setT) + SETelemsize * (qh->hull_dim-1))/2);
+    }
+    if (facet->outsideset)
+      zadd_(Zmempoints, sizeof(setT) + SETelemsize * qh_setsize(qh, facet->outsideset));
+    if (facet->coplanarset)
+      zadd_(Zmempoints, sizeof(setT) + SETelemsize * qh_setsize(qh, facet->coplanarset));
+    if (facet->seen) /* Delaunay upper envelope */
+      continue;
+    facet->seen= True;
+    FOREACHneighbor_(facet) {
+      if (neighbor == qh_DUPLICATEridge || neighbor == qh_MERGEridge
+          || neighbor->seen || !facet->normal || !neighbor->normal)
+        continue;
+      dotproduct= qh_getangle(qh, facet->normal, neighbor->normal);
+      zinc_(Zangle);
+      wadd_(Wangle, dotproduct);
+      wmax_(Wanglemax, dotproduct)
+      wmin_(Wanglemin, dotproduct)
+    }
+    if (facet->normal) {
+      FOREACHvertex_(facet->vertices) {
+        zinc_(Zdiststat);
+        qh_distplane(qh, vertex->point, facet, &dist);
+        wmax_(Wvertexmax, dist);
+        wmin_(Wvertexmin, dist);
+      }
+    }
+  }
+  FORALLvertices {
+    if (vertex->deleted)
+      continue;
+    zadd_(Zmemvertices, sizeof(vertexT));
+    if (vertex->neighbors) {
+      sizneighbors= qh_setsize(qh, vertex->neighbors);
+      zadd_(Znumvneighbors, sizneighbors);
+      zmax_(Zmaxvneighbors, sizneighbors);
+      zadd_(Zmemvertices, sizeof(vertexT) + SETelemsize * sizneighbors);
+    }
+  }
+  qh->RANDOMdist= qh->old_randomdist;
+} /* collectstatistics */
+#endif /* qh_KEEPstatistics */
+
+/*-<a                             href="qh-stat_r.htm#TOC"
+  >-------------------------------</a><a name="initstatistics">-</a>
+
+  qh_initstatistics(qh)
+    initialize statistics
+
+  notes:
+  NOerrors -- qh_initstatistics can not use qh_errexit(), qh_fprintf, or qh.ferr
+  On first call, only qhmem.ferr is defined.  qh_memalloc is not setup.
+  Also invoked by QhullQh().
+*/
+void qh_initstatistics(qhT *qh) {
+  int i;
+  realT realx;
+  int intx;
+
+  qh->qhstat.next= 0;
+  qh_allstatA(qh);
+  qh_allstatB(qh);
+  qh_allstatC(qh);
+  qh_allstatD(qh);
+  qh_allstatE(qh);
+  qh_allstatE2(qh);
+  qh_allstatF(qh);
+  qh_allstatG(qh);
+  qh_allstatH(qh);
+  qh_allstatI(qh);
+  if (qh->qhstat.next > (int)sizeof(qh->qhstat.id)) {
+    qh_fprintf(qh, qh->qhmem.ferr, 6184, "qhull error (qh_initstatistics): increase size of qhstat.id[].\n\
+      qhstat.next %d should be <= sizeof(qh->qhstat.id) %d\n", qh->qhstat.next, (int)sizeof(qh->qhstat.id));
+#if 0 /* for locating error, Znumridges should be duplicated */
+    for(i=0; i < ZEND; i++) {
+      int j;
+      for(j=i+1; j < ZEND; j++) {
+        if (qh->qhstat.id[i] == qh->qhstat.id[j]) {
+          qh_fprintf(qh, qh->qhmem.ferr, 6185, "qhull error (qh_initstatistics): duplicated statistic %d at indices %d and %d\n",
+              qh->qhstat.id[i], i, j);
+        }
+      }
+    }
+#endif
+    qh_exit(qh_ERRqhull);  /* can not use qh_errexit() */
+  }
+  qh->qhstat.init[zinc].i= 0;
+  qh->qhstat.init[zadd].i= 0;
+  qh->qhstat.init[zmin].i= INT_MAX;
+  qh->qhstat.init[zmax].i= INT_MIN;
+  qh->qhstat.init[wadd].r= 0;
+  qh->qhstat.init[wmin].r= REALmax;
+  qh->qhstat.init[wmax].r= -REALmax;
+  for(i=0; i < ZEND; i++) {
+    if (qh->qhstat.type[i] > ZTYPEreal) {
+      realx= qh->qhstat.init[(unsigned char)(qh->qhstat.type[i])].r;
+      qh->qhstat.stats[i].r= realx;
+    }else if (qh->qhstat.type[i] != zdoc) {
+      intx= qh->qhstat.init[(unsigned char)(qh->qhstat.type[i])].i;
+      qh->qhstat.stats[i].i= intx;
+    }
+  }
+} /* initstatistics */
+
+/*-<a                             href="qh-stat_r.htm#TOC"
+  >-------------------------------</a><a name="newstats">-</a>
+
+  qh_newstats(qh, )
+    returns True if statistics for zdoc
+
+  returns:
+    next zdoc
+*/
+boolT qh_newstats(qhT *qh, int idx, int *nextindex) {
+  boolT isnew= False;
+  int start, i;
+
+  if (qh->qhstat.type[qh->qhstat.id[idx]] == zdoc)
+    start= idx+1;
+  else
+    start= idx;
+  for(i= start; i < qh->qhstat.next && qh->qhstat.type[qh->qhstat.id[i]] != zdoc; i++) {
+    if (!qh_nostatistic(qh, qh->qhstat.id[i]) && !qh->qhstat.printed[qh->qhstat.id[i]])
+        isnew= True;
+  }
+  *nextindex= i;
+  return isnew;
+} /* newstats */
+
+/*-<a                             href="qh-stat_r.htm#TOC"
+  >-------------------------------</a><a name="nostatistic">-</a>
+
+  qh_nostatistic(qh, index )
+    true if no statistic to print
+*/
+boolT qh_nostatistic(qhT *qh, int i) {
+
+  if ((qh->qhstat.type[i] > ZTYPEreal
+       &&qh->qhstat.stats[i].r == qh->qhstat.init[(unsigned char)(qh->qhstat.type[i])].r)
+      || (qh->qhstat.type[i] < ZTYPEreal
+          &&qh->qhstat.stats[i].i == qh->qhstat.init[(unsigned char)(qh->qhstat.type[i])].i))
+    return True;
+  return False;
+} /* nostatistic */
+
+#if qh_KEEPstatistics
+/*-<a                             href="qh-stat_r.htm#TOC"
+  >-------------------------------</a><a name="printallstatistics">-</a>
+
+  qh_printallstatistics(qh, fp, string )
+    print all statistics with header 'string'
+*/
+void qh_printallstatistics(qhT *qh, FILE *fp, const char *string) {
+
+  qh_allstatistics(qh);
+  qh_collectstatistics(qh);
+  qh_printstatistics(qh, fp, string);
+  qh_memstatistics(qh, fp);
+}
+
+
+/*-<a                             href="qh-stat_r.htm#TOC"
+  >-------------------------------</a><a name="printstatistics">-</a>
+
+  qh_printstatistics(qh, fp, string )
+    print statistics to a file with header 'string'
+    skips statistics with qhstat.printed[] (reset with qh_allstatistics)
+
+  see:
+    qh_printallstatistics()
+*/
+void qh_printstatistics(qhT *qh, FILE *fp, const char *string) {
+  int i, k;
+  realT ave;
+
+  if (qh->num_points != qh->num_vertices) {
+    wval_(Wpbalance)= 0;
+    wval_(Wpbalance2)= 0;
+  }else
+    wval_(Wpbalance2)= qh_stddev(zval_(Zpbalance), wval_(Wpbalance),
+                                 wval_(Wpbalance2), &ave);
+  wval_(Wnewbalance2)= qh_stddev(zval_(Zprocessed), wval_(Wnewbalance),
+                                 wval_(Wnewbalance2), &ave);
+  qh_fprintf(qh, fp, 9350, "\n\
+%s\n\
+ qhull invoked by: %s | %s\n%s with options:\n%s\n", string, qh->rbox_command,
+     qh->qhull_command, qh_version, qh->qhull_options);
+  qh_fprintf(qh, fp, 9351, "\nprecision constants:\n\
+ %6.2g max. abs. coordinate in the (transformed) input('Qbd:n')\n\
+ %6.2g max. roundoff error for distance computation('En')\n\
+ %6.2g max. roundoff error for angle computations\n\
+ %6.2g min. distance for outside points ('Wn')\n\
+ %6.2g min. distance for visible facets ('Vn')\n\
+ %6.2g max. distance for coplanar facets ('Un')\n\
+ %6.2g max. facet width for recomputing centrum and area\n\
+",
+  qh->MAXabs_coord, qh->DISTround, qh->ANGLEround, qh->MINoutside,
+        qh->MINvisible, qh->MAXcoplanar, qh->WIDEfacet);
+  if (qh->KEEPnearinside)
+    qh_fprintf(qh, fp, 9352, "\
+ %6.2g max. distance for near-inside points\n", qh->NEARinside);
+  if (qh->premerge_cos < REALmax/2) qh_fprintf(qh, fp, 9353, "\
+ %6.2g max. cosine for pre-merge angle\n", qh->premerge_cos);
+  if (qh->PREmerge) qh_fprintf(qh, fp, 9354, "\
+ %6.2g radius of pre-merge centrum\n", qh->premerge_centrum);
+  if (qh->postmerge_cos < REALmax/2) qh_fprintf(qh, fp, 9355, "\
+ %6.2g max. cosine for post-merge angle\n", qh->postmerge_cos);
+  if (qh->POSTmerge) qh_fprintf(qh, fp, 9356, "\
+ %6.2g radius of post-merge centrum\n", qh->postmerge_centrum);
+  qh_fprintf(qh, fp, 9357, "\
+ %6.2g max. distance for merging two simplicial facets\n\
+ %6.2g max. roundoff error for arithmetic operations\n\
+ %6.2g min. denominator for divisions\n\
+  zero diagonal for Gauss: ", qh->ONEmerge, REALepsilon, qh->MINdenom);
+  for(k=0; k < qh->hull_dim; k++)
+    qh_fprintf(qh, fp, 9358, "%6.2e ", qh->NEARzero[k]);
+  qh_fprintf(qh, fp, 9359, "\n\n");
+  for(i=0 ; i < qh->qhstat.next; )
+    qh_printstats(qh, fp, i, &i);
+} /* printstatistics */
+#endif /* qh_KEEPstatistics */
+
+/*-<a                             href="qh-stat_r.htm#TOC"
+  >-------------------------------</a><a name="printstatlevel">-</a>
+
+  qh_printstatlevel(qh, fp, id )
+    print level information for a statistic
+
+  notes:
+    nop if id >= ZEND, printed, or same as initial value
+*/
+void qh_printstatlevel(qhT *qh, FILE *fp, int id) {
+#define NULLfield "       "
+
+  if (id >= ZEND || qh->qhstat.printed[id])
+    return;
+  if (qh->qhstat.type[id] == zdoc) {
+    qh_fprintf(qh, fp, 9360, "%s\n", qh->qhstat.doc[id]);
+    return;
+  }
+  if (qh_nostatistic(qh, id) || !qh->qhstat.doc[id])
+    return;
+  qh->qhstat.printed[id]= True;
+  if (qh->qhstat.count[id] != -1
+      && qh->qhstat.stats[(unsigned char)(qh->qhstat.count[id])].i == 0)
+    qh_fprintf(qh, fp, 9361, " *0 cnt*");
+  else if (qh->qhstat.type[id] >= ZTYPEreal && qh->qhstat.count[id] == -1)
+    qh_fprintf(qh, fp, 9362, "%7.2g", qh->qhstat.stats[id].r);
+  else if (qh->qhstat.type[id] >= ZTYPEreal && qh->qhstat.count[id] != -1)
+    qh_fprintf(qh, fp, 9363, "%7.2g", qh->qhstat.stats[id].r/ qh->qhstat.stats[(unsigned char)(qh->qhstat.count[id])].i);
+  else if (qh->qhstat.type[id] < ZTYPEreal && qh->qhstat.count[id] == -1)
+    qh_fprintf(qh, fp, 9364, "%7d", qh->qhstat.stats[id].i);
+  else if (qh->qhstat.type[id] < ZTYPEreal && qh->qhstat.count[id] != -1)
+    qh_fprintf(qh, fp, 9365, "%7.3g", (realT) qh->qhstat.stats[id].i / qh->qhstat.stats[(unsigned char)(qh->qhstat.count[id])].i);
+  qh_fprintf(qh, fp, 9366, " %s\n", qh->qhstat.doc[id]);
+} /* printstatlevel */
+
+
+/*-<a                             href="qh-stat_r.htm#TOC"
+  >-------------------------------</a><a name="printstats">-</a>
+
+  qh_printstats(qh, fp, index, nextindex )
+    print statistics for a zdoc group
+
+  returns:
+    next zdoc if non-null
+*/
+void qh_printstats(qhT *qh, FILE *fp, int idx, int *nextindex) {
+  int j, nexti;
+
+  if (qh_newstats(qh, idx, &nexti)) {
+    qh_fprintf(qh, fp, 9367, "\n");
+    for (j=idx; j<nexti; j++)
+      qh_printstatlevel(qh, fp, qh->qhstat.id[j]);
+  }
+  if (nextindex)
+    *nextindex= nexti;
+} /* printstats */
+
+#if qh_KEEPstatistics
+
+/*-<a                             href="qh-stat_r.htm#TOC"
+  >-------------------------------</a><a name="stddev">-</a>
+
+  qh_stddev(num, tot, tot2, ave )
+    compute the standard deviation and average from statistics
+
+    tot2 is the sum of the squares
+  notes:
+    computes r.m.s.:
+      (x-ave)^2
+      == x^2 - 2x tot/num +   (tot/num)^2
+      == tot2 - 2 tot tot/num + tot tot/num
+      == tot2 - tot ave
+*/
+realT qh_stddev(int num, realT tot, realT tot2, realT *ave) {
+  realT stddev;
+
+  *ave= tot/num;
+  stddev= sqrt(tot2/num - *ave * *ave);
+  return stddev;
+} /* stddev */
+
+#endif /* qh_KEEPstatistics */
+
+#if !qh_KEEPstatistics
+void    qh_collectstatistics(qhT *qh) {}
+void    qh_printallstatistics(qhT *qh, FILE *fp, char *string) {};
+void    qh_printstatistics(qhT *qh, FILE *fp, char *string) {}
+#endif
+
diff --git a/C/user_r.c b/C/user_r.c
new file mode 100644
--- /dev/null
+++ b/C/user_r.c
@@ -0,0 +1,527 @@
+/*<html><pre>  -<a                             href="qh-user_r.htm"
+  >-------------------------------</a><a name="TOP">-</a>
+
+   user.c
+   user redefinable functions
+
+   see user2_r.c for qh_fprintf, qh_malloc, qh_free
+
+   see README.txt  see COPYING.txt for copyright information.
+
+   see libqhull_r.h for data structures, macros, and user-callable functions.
+
+   see user_eg.c, user_eg2.c, and unix.c for examples.
+
+   see user.h for user-definable constants
+
+      use qh_NOmem in mem_r.h to turn off memory management
+      use qh_NOmerge in user.h to turn off facet merging
+      set qh_KEEPstatistics in user.h to 0 to turn off statistics
+
+   This is unsupported software.  You're welcome to make changes,
+   but you're on your own if something goes wrong.  Use 'Tc' to
+   check frequently.  Usually qhull will report an error if
+   a data structure becomes inconsistent.  If so, it also reports
+   the last point added to the hull, e.g., 102.  You can then trace
+   the execution of qhull with "T4P102".
+
+   Please report any errors that you fix to qhull@qhull.org
+
+   Qhull-template is a template for calling qhull from within your application
+
+   if you recompile and load this module, then user.o will not be loaded
+   from qhull.a
+
+   you can add additional quick allocation sizes in qh_user_memsizes
+
+   if the other functions here are redefined to not use qh_print...,
+   then io.o will not be loaded from qhull.a.  See user_eg_r.c for an
+   example.  We recommend keeping io.o for the extra debugging
+   information it supplies.
+*/
+
+#include "qhull_ra.h"
+
+#include <stdarg.h>
+
+/*-<a                             href="qh-user_r.htm#TOC"
+  >-------------------------------</a><a name="qhull_template">-</a>
+
+  Qhull-template
+    Template for calling qhull from inside your program
+
+  returns:
+    exit code(see qh_ERR... in libqhull_r.h)
+    all memory freed
+
+  notes:
+    This can be called any number of times.
+
+*/
+#if 0
+{
+  int dim;                  /* dimension of points */
+  int numpoints;            /* number of points */
+  coordT *points;           /* array of coordinates for each point */
+  boolT ismalloc;           /* True if qhull should free points in qh_freeqhull() or reallocation */
+  char flags[]= "qhull Tv"; /* option flags for qhull, see qh_opt.htm */
+  FILE *outfile= stdout;    /* output from qh_produce_output(qh)
+                               use NULL to skip qh_produce_output(qh) */
+  FILE *errfile= stderr;    /* error messages from qhull code */
+  int exitcode;             /* 0 if no error from qhull */
+  facetT *facet;            /* set by FORALLfacets */
+  int curlong, totlong;     /* memory remaining after qh_memfreeshort */
+
+  qhT qh_qh;                /* Qhull's data structure.  First argument of most calls */
+  qhT *qh= &qh_qh;          /* Alternatively -- qhT *qh= (qhT*)malloc(sizeof(qhT)) */
+
+  QHULL_LIB_CHECK /* Check for compatible library */
+
+  qh_zero(qh, errfile);
+
+  /* initialize dim, numpoints, points[], ismalloc here */
+  exitcode= qh_new_qhull(qh, dim, numpoints, points, ismalloc,
+                      flags, outfile, errfile);
+  if (!exitcode) {                  /* if no error */
+    /* 'qh->facet_list' contains the convex hull */
+    FORALLfacets {
+       /* ... your code ... */
+    }
+  }
+  qh_freeqhull(qh, !qh_ALL);
+  qh_memfreeshort(qh, &curlong, &totlong);
+  if (curlong || totlong)
+    qh_fprintf(qh, errfile, 7068, "qhull internal warning (main): did not free %d bytes of long memory(%d pieces)\n", totlong, curlong);
+}
+#endif
+
+/*-<a                             href="qh-user_r.htm#TOC"
+  >-------------------------------</a><a name="new_qhull">-</a>
+
+  qh_new_qhull(qh, dim, numpoints, points, ismalloc, qhull_cmd, outfile, errfile )
+    Run qhull and return results in qh.
+    Returns exitcode (0 if no errors).
+    Before first call, either call qh_zero(qh, errfile), or set qh to all zero.
+
+  notes:
+    do not modify points until finished with results.
+      The qhull data structure contains pointers into the points array.
+    do not call qhull functions before qh_new_qhull().
+      The qhull data structure is not initialized until qh_new_qhull().
+    do not call qh_init_A (global_r.c)
+
+    Default errfile is stderr, outfile may be null
+    qhull_cmd must start with "qhull "
+    projects points to a new point array for Delaunay triangulations ('d' and 'v')
+    transforms points into a new point array for halfspace intersection ('H')
+
+  see:
+    Qhull-template at the beginning of this file.
+    An example of using qh_new_qhull is user_eg_r.c
+*/
+int qh_new_qhull(qhT *qh, int dim, int numpoints, coordT *points, boolT ismalloc,
+                char *qhull_cmd, FILE *outfile, FILE *errfile) {
+  /* gcc may issue a "might be clobbered" warning for dim, points, and ismalloc [-Wclobbered].
+     These parameters are not referenced after a longjmp() and hence not clobbered.
+     See http://stackoverflow.com/questions/7721854/what-sense-do-these-clobbered-variable-warnings-make */
+  int exitcode, hulldim;
+  boolT new_ismalloc;
+  coordT *new_points;
+
+  if(!errfile){
+    errfile= stderr;
+  }
+  if (!qh->qhmem.ferr) {
+    qh_meminit(qh, errfile);
+  } else {
+    qh_memcheck(qh);
+  }
+  if (strncmp(qhull_cmd, "qhull ", (size_t)6)) {
+    qh_fprintf(qh, errfile, 6186, "qhull error (qh_new_qhull): start qhull_cmd argument with \"qhull \"\n");
+    return qh_ERRinput;
+  }
+  qh_initqhull_start(qh, NULL, outfile, errfile);
+  trace1((qh, qh->ferr, 1044, "qh_new_qhull: build new Qhull for %d %d-d points with %s\n", numpoints, dim, qhull_cmd));
+  exitcode = setjmp(qh->errexit);
+  if (!exitcode)
+  {
+    qh->NOerrexit = False;
+    qh_initflags(qh, qhull_cmd);
+    if (qh->DELAUNAY)
+      qh->PROJECTdelaunay= True;
+    if (qh->HALFspace) {
+      /* points is an array of halfspaces,
+         the last coordinate of each halfspace is its offset */
+      hulldim= dim-1;
+      qh_setfeasible(qh, hulldim);
+      new_points= qh_sethalfspace_all(qh, dim, numpoints, points, qh->feasible_point);
+      new_ismalloc= True;
+      if (ismalloc)
+        qh_free(points);
+    }else {
+      hulldim= dim;
+      new_points= points;
+      new_ismalloc= ismalloc;
+    }
+    qh_init_B(qh, new_points, numpoints, hulldim, new_ismalloc);
+    qh_qhull(qh);
+    qh_check_output(qh);
+    if (outfile) {
+      qh_produce_output(qh);
+    }else {
+      qh_prepare_output(qh);
+    }
+    if (qh->VERIFYoutput && !qh->STOPpoint && !qh->STOPcone)
+      qh_check_points(qh);
+  }
+  qh->NOerrexit = True;
+  return exitcode;
+} /* new_qhull */
+
+/*-<a                             href="qh-user_r.htm#TOC"
+  >-------------------------------</a><a name="errexit">-</a>
+
+  qh_errexit(qh, exitcode, facet, ridge )
+    report and exit from an error
+    report facet and ridge if non-NULL
+    reports useful information such as last point processed
+    set qh.FORCEoutput to print neighborhood of facet
+
+  see:
+    qh_errexit2() in libqhull_r.c for printing 2 facets
+
+  design:
+    check for error within error processing
+    compute qh.hulltime
+    print facet and ridge (if any)
+    report commandString, options, qh.furthest_id
+    print summary and statistics (including precision statistics)
+    if qh_ERRsingular
+      print help text for singular data set
+    exit program via long jump (if defined) or exit()
+*/
+void qh_errexit(qhT *qh, int exitcode, facetT *facet, ridgeT *ridge) {
+
+  if (qh->ERREXITcalled) {
+    qh_fprintf(qh, qh->ferr, 8126, "\nqhull error while processing previous error.  Exit program\n");
+    qh_exit(qh_ERRqhull);
+  }
+  qh->ERREXITcalled= True;
+  if (!qh->QHULLfinished)
+    qh->hulltime= qh_CPUclock - qh->hulltime;
+  qh_errprint(qh, "ERRONEOUS", facet, NULL, ridge, NULL);
+  qh_fprintf(qh, qh->ferr, 8127, "\nWhile executing: %s | %s\n", qh->rbox_command, qh->qhull_command);
+  qh_fprintf(qh, qh->ferr, 8128, "Options selected for Qhull %s:\n%s\n", qh_version, qh->qhull_options);
+  if (qh->furthest_id >= 0) {
+    qh_fprintf(qh, qh->ferr, 8129, "Last point added to hull was p%d.", qh->furthest_id);
+    if (zzval_(Ztotmerge))
+      qh_fprintf(qh, qh->ferr, 8130, "  Last merge was #%d.", zzval_(Ztotmerge));
+    if (qh->QHULLfinished)
+      qh_fprintf(qh, qh->ferr, 8131, "\nQhull has finished constructing the hull.");
+    else if (qh->POSTmerging)
+      qh_fprintf(qh, qh->ferr, 8132, "\nQhull has started post-merging.");
+    qh_fprintf(qh, qh->ferr, 8133, "\n");
+  }
+  if (qh->FORCEoutput && (qh->QHULLfinished || (!facet && !ridge)))
+    qh_produce_output(qh);
+  else if (exitcode != qh_ERRinput) {
+    if (exitcode != qh_ERRsingular && zzval_(Zsetplane) > qh->hull_dim+1) {
+      qh_fprintf(qh, qh->ferr, 8134, "\nAt error exit:\n");
+      qh_printsummary(qh, qh->ferr);
+      if (qh->PRINTstatistics) {
+        qh_collectstatistics(qh);
+        qh_printstatistics(qh, qh->ferr, "at error exit");
+        qh_memstatistics(qh, qh->ferr);
+      }
+    }
+    if (qh->PRINTprecision)
+      qh_printstats(qh, qh->ferr, qh->qhstat.precision, NULL);
+  }
+  if (!exitcode)
+    exitcode= qh_ERRqhull;
+  else if (exitcode == qh_ERRsingular)
+    qh_printhelp_singular(qh, qh->ferr);
+  else if (exitcode == qh_ERRprec && !qh->PREmerge)
+    qh_printhelp_degenerate(qh, qh->ferr);
+  if (qh->NOerrexit) {
+    qh_fprintf(qh, qh->ferr, 6187, "qhull error while ending program, or qh->NOerrexit not cleared after setjmp(). Exit program with error.\n");
+    qh_exit(qh_ERRqhull);
+  }
+  qh->ERREXITcalled= False;
+  qh->NOerrexit= True;
+  qh->ALLOWrestart= False;  /* longjmp will undo qh_build_withrestart */
+  longjmp(qh->errexit, exitcode);
+} /* errexit */
+
+
+/*-<a                             href="qh-user_r.htm#TOC"
+  >-------------------------------</a><a name="errprint">-</a>
+
+  qh_errprint(qh, fp, string, atfacet, otherfacet, atridge, atvertex )
+    prints out the information of facets and ridges to fp
+    also prints neighbors and geomview output
+
+  notes:
+    except for string, any parameter may be NULL
+*/
+void qh_errprint(qhT *qh, const char *string, facetT *atfacet, facetT *otherfacet, ridgeT *atridge, vertexT *atvertex) {
+  int i;
+
+  if (atfacet) {
+    qh_fprintf(qh, qh->ferr, 8135, "%s FACET:\n", string);
+    qh_printfacet(qh, qh->ferr, atfacet);
+  }
+  if (otherfacet) {
+    qh_fprintf(qh, qh->ferr, 8136, "%s OTHER FACET:\n", string);
+    qh_printfacet(qh, qh->ferr, otherfacet);
+  }
+  if (atridge) {
+    qh_fprintf(qh, qh->ferr, 8137, "%s RIDGE:\n", string);
+    qh_printridge(qh, qh->ferr, atridge);
+    if (atridge->top && atridge->top != atfacet && atridge->top != otherfacet)
+      qh_printfacet(qh, qh->ferr, atridge->top);
+    if (atridge->bottom
+        && atridge->bottom != atfacet && atridge->bottom != otherfacet)
+      qh_printfacet(qh, qh->ferr, atridge->bottom);
+    if (!atfacet)
+      atfacet= atridge->top;
+    if (!otherfacet)
+      otherfacet= otherfacet_(atridge, atfacet);
+  }
+  if (atvertex) {
+    qh_fprintf(qh, qh->ferr, 8138, "%s VERTEX:\n", string);
+    qh_printvertex(qh, qh->ferr, atvertex);
+  }
+  if (qh->fout && qh->FORCEoutput && atfacet && !qh->QHULLfinished && !qh->IStracing) {
+    qh_fprintf(qh, qh->ferr, 8139, "ERRONEOUS and NEIGHBORING FACETS to output\n");
+    for (i=0; i < qh_PRINTEND; i++)  /* use fout for geomview output */
+      qh_printneighborhood(qh, qh->fout, qh->PRINTout[i], atfacet, otherfacet,
+                            !qh_ALL);
+  }
+} /* errprint */
+
+
+/*-<a                             href="qh-user_r.htm#TOC"
+  >-------------------------------</a><a name="printfacetlist">-</a>
+
+  qh_printfacetlist(qh, fp, facetlist, facets, printall )
+    print all fields for a facet list and/or set of facets to fp
+    if !printall,
+      only prints good facets
+
+  notes:
+    also prints all vertices
+*/
+void qh_printfacetlist(qhT *qh, facetT *facetlist, setT *facets, boolT printall) {
+  facetT *facet, **facetp;
+
+  qh_printbegin(qh, qh->ferr, qh_PRINTfacets, facetlist, facets, printall);
+  FORALLfacet_(facetlist)
+    qh_printafacet(qh, qh->ferr, qh_PRINTfacets, facet, printall);
+  FOREACHfacet_(facets)
+    qh_printafacet(qh, qh->ferr, qh_PRINTfacets, facet, printall);
+  qh_printend(qh, qh->ferr, qh_PRINTfacets, facetlist, facets, printall);
+} /* printfacetlist */
+
+
+/*-<a                             href="qh-io_r.htm#TOC"
+  >-------------------------------</a><a name="printhelp_degenerate">-</a>
+
+  qh_printhelp_degenerate(qh, fp )
+    prints descriptive message for precision error
+
+  notes:
+    no message if qh_QUICKhelp
+*/
+void qh_printhelp_degenerate(qhT *qh, FILE *fp) {
+
+  if (qh->MERGEexact || qh->PREmerge || qh->JOGGLEmax < REALmax/2)
+    qh_fprintf(qh, fp, 9368, "\n\
+A Qhull error has occurred.  Qhull should have corrected the above\n\
+precision error.  Please send the input and all of the output to\n\
+qhull_bug@qhull.org\n");
+  else if (!qh_QUICKhelp) {
+    qh_fprintf(qh, fp, 9369, "\n\
+Precision problems were detected during construction of the convex hull.\n\
+This occurs because convex hull algorithms assume that calculations are\n\
+exact, but floating-point arithmetic has roundoff errors.\n\
+\n\
+To correct for precision problems, do not use 'Q0'.  By default, Qhull\n\
+selects 'C-0' or 'Qx' and merges non-convex facets.  With option 'QJ',\n\
+Qhull joggles the input to prevent precision problems.  See \"Imprecision\n\
+in Qhull\" (qh-impre.htm).\n\
+\n\
+If you use 'Q0', the output may include\n\
+coplanar ridges, concave ridges, and flipped facets.  In 4-d and higher,\n\
+Qhull may produce a ridge with four neighbors or two facets with the same \n\
+vertices.  Qhull reports these events when they occur.  It stops when a\n\
+concave ridge, flipped facet, or duplicate facet occurs.\n");
+#if REALfloat
+    qh_fprintf(qh, fp, 9370, "\
+\n\
+Qhull is currently using single precision arithmetic.  The following\n\
+will probably remove the precision problems:\n\
+  - recompile qhull for realT precision(#define REALfloat 0 in user.h).\n");
+#endif
+    if (qh->DELAUNAY && !qh->SCALElast && qh->MAXabs_coord > 1e4)
+      qh_fprintf(qh, fp, 9371, "\
+\n\
+When computing the Delaunay triangulation of coordinates > 1.0,\n\
+  - use 'Qbb' to scale the last coordinate to [0,m] (max previous coordinate)\n");
+    if (qh->DELAUNAY && !qh->ATinfinity)
+      qh_fprintf(qh, fp, 9372, "\
+When computing the Delaunay triangulation:\n\
+  - use 'Qz' to add a point at-infinity.  This reduces precision problems.\n");
+
+    qh_fprintf(qh, fp, 9373, "\
+\n\
+If you need triangular output:\n\
+  - use option 'Qt' to triangulate the output\n\
+  - use option 'QJ' to joggle the input points and remove precision errors\n\
+  - use option 'Ft'.  It triangulates non-simplicial facets with added points.\n\
+\n\
+If you must use 'Q0',\n\
+try one or more of the following options.  They can not guarantee an output.\n\
+  - use 'QbB' to scale the input to a cube.\n\
+  - use 'Po' to produce output and prevent partitioning for flipped facets\n\
+  - use 'V0' to set min. distance to visible facet as 0 instead of roundoff\n\
+  - use 'En' to specify a maximum roundoff error less than %2.2g.\n\
+  - options 'Qf', 'Qbb', and 'QR0' may also help\n",
+               qh->DISTround);
+    qh_fprintf(qh, fp, 9374, "\
+\n\
+To guarantee simplicial output:\n\
+  - use option 'Qt' to triangulate the output\n\
+  - use option 'QJ' to joggle the input points and remove precision errors\n\
+  - use option 'Ft' to triangulate the output by adding points\n\
+  - use exact arithmetic (see \"Imprecision in Qhull\", qh-impre.htm)\n\
+");
+  }
+} /* printhelp_degenerate */
+
+
+/*-<a                             href="qh-globa_r.htm#TOC"
+  >-------------------------------</a><a name="printhelp_narrowhull">-</a>
+
+  qh_printhelp_narrowhull(qh, minangle )
+    Warn about a narrow hull
+
+  notes:
+    Alternatively, reduce qh_WARNnarrow in user.h
+
+*/
+void qh_printhelp_narrowhull(qhT *qh, FILE *fp, realT minangle) {
+
+    qh_fprintf(qh, fp, 9375, "qhull precision warning: \n\
+The initial hull is narrow (cosine of min. angle is %.16f).\n\
+Is the input lower dimensional (e.g., on a plane in 3-d)?  Qhull may\n\
+produce a wide facet.  Options 'QbB' (scale to unit box) or 'Qbb' (scale\n\
+last coordinate) may remove this warning.  Use 'Pp' to skip this warning.\n\
+See 'Limitations' in qh-impre.htm.\n",
+          -minangle);   /* convert from angle between normals to angle between facets */
+} /* printhelp_narrowhull */
+
+/*-<a                             href="qh-io_r.htm#TOC"
+  >-------------------------------</a><a name="printhelp_singular">-</a>
+
+  qh_printhelp_singular(qh, fp )
+    prints descriptive message for singular input
+*/
+void qh_printhelp_singular(qhT *qh, FILE *fp) {
+  facetT *facet;
+  vertexT *vertex, **vertexp;
+  realT min, max, *coord, dist;
+  int i,k;
+
+  qh_fprintf(qh, fp, 9376, "\n\
+The input to qhull appears to be less than %d dimensional, or a\n\
+computation has overflowed.\n\n\
+Qhull could not construct a clearly convex simplex from points:\n",
+           qh->hull_dim);
+  qh_printvertexlist(qh, fp, "", qh->facet_list, NULL, qh_ALL);
+  if (!qh_QUICKhelp)
+    qh_fprintf(qh, fp, 9377, "\n\
+The center point is coplanar with a facet, or a vertex is coplanar\n\
+with a neighboring facet.  The maximum round off error for\n\
+computing distances is %2.2g.  The center point, facets and distances\n\
+to the center point are as follows:\n\n", qh->DISTround);
+  qh_printpointid(qh, fp, "center point", qh->hull_dim, qh->interior_point, qh_IDunknown);
+  qh_fprintf(qh, fp, 9378, "\n");
+  FORALLfacets {
+    qh_fprintf(qh, fp, 9379, "facet");
+    FOREACHvertex_(facet->vertices)
+      qh_fprintf(qh, fp, 9380, " p%d", qh_pointid(qh, vertex->point));
+    zinc_(Zdistio);
+    qh_distplane(qh, qh->interior_point, facet, &dist);
+    qh_fprintf(qh, fp, 9381, " distance= %4.2g\n", dist);
+  }
+  if (!qh_QUICKhelp) {
+    if (qh->HALFspace)
+      qh_fprintf(qh, fp, 9382, "\n\
+These points are the dual of the given halfspaces.  They indicate that\n\
+the intersection is degenerate.\n");
+    qh_fprintf(qh, fp, 9383,"\n\
+These points either have a maximum or minimum x-coordinate, or\n\
+they maximize the determinant for k coordinates.  Trial points\n\
+are first selected from points that maximize a coordinate.\n");
+    if (qh->hull_dim >= qh_INITIALmax)
+      qh_fprintf(qh, fp, 9384, "\n\
+Because of the high dimension, the min x-coordinate and max-coordinate\n\
+points are used if the determinant is non-zero.  Option 'Qs' will\n\
+do a better, though much slower, job.  Instead of 'Qs', you can change\n\
+the points by randomly rotating the input with 'QR0'.\n");
+  }
+  qh_fprintf(qh, fp, 9385, "\nThe min and max coordinates for each dimension are:\n");
+  for (k=0; k < qh->hull_dim; k++) {
+    min= REALmax;
+    max= -REALmin;
+    for (i=qh->num_points, coord= qh->first_point+k; i--; coord += qh->hull_dim) {
+      maximize_(max, *coord);
+      minimize_(min, *coord);
+    }
+    qh_fprintf(qh, fp, 9386, "  %d:  %8.4g  %8.4g  difference= %4.4g\n", k, min, max, max-min);
+  }
+  if (!qh_QUICKhelp) {
+    qh_fprintf(qh, fp, 9387, "\n\
+If the input should be full dimensional, you have several options that\n\
+may determine an initial simplex:\n\
+  - use 'QJ'  to joggle the input and make it full dimensional\n\
+  - use 'QbB' to scale the points to the unit cube\n\
+  - use 'QR0' to randomly rotate the input for different maximum points\n\
+  - use 'Qs'  to search all points for the initial simplex\n\
+  - use 'En'  to specify a maximum roundoff error less than %2.2g.\n\
+  - trace execution with 'T3' to see the determinant for each point.\n",
+                     qh->DISTround);
+#if REALfloat
+    qh_fprintf(qh, fp, 9388, "\
+  - recompile qhull for realT precision(#define REALfloat 0 in libqhull_r.h).\n");
+#endif
+    qh_fprintf(qh, fp, 9389, "\n\
+If the input is lower dimensional:\n\
+  - use 'QJ' to joggle the input and make it full dimensional\n\
+  - use 'Qbk:0Bk:0' to delete coordinate k from the input.  You should\n\
+    pick the coordinate with the least range.  The hull will have the\n\
+    correct topology.\n\
+  - determine the flat containing the points, rotate the points\n\
+    into a coordinate plane, and delete the other coordinates.\n\
+  - add one or more points to make the input full dimensional.\n\
+");
+  }
+} /* printhelp_singular */
+
+/*-<a                             href="qh-globa_r.htm#TOC"
+  >-------------------------------</a><a name="user_memsizes">-</a>
+
+  qh_user_memsizes(qh)
+    allocate up to 10 additional, quick allocation sizes
+
+  notes:
+    increase maximum number of allocations in qh_initqhull_mem()
+*/
+void qh_user_memsizes(qhT *qh) {
+
+  QHULL_UNUSED(qh)
+  /* qh_memsize(qh, size); */
+} /* user_memsizes */
+
+
diff --git a/C/usermem_r.c b/C/usermem_r.c
new file mode 100644
--- /dev/null
+++ b/C/usermem_r.c
@@ -0,0 +1,94 @@
+/*<html><pre>  -<a                             href="qh-user_r.htm"
+  >-------------------------------</a><a name="TOP">-</a>
+
+   usermem_r.c
+   qh_exit(), qh_free(), and qh_malloc()
+
+   See README.txt.
+
+   If you redefine one of these functions you must redefine all of them.
+   If you recompile and load this file, then usermem.o will not be loaded
+   from qhull.a or qhull.lib
+
+   See libqhull_r.h for data structures, macros, and user-callable functions.
+   See user_r.c for qhull-related, redefinable functions
+   see user_r.h for user-definable constants
+   See userprintf_r.c for qh_fprintf and userprintf_rbox_r.c for qh_fprintf_rbox
+
+   Please report any errors that you fix to qhull@qhull.org
+*/
+
+#include "libqhull_r.h"
+
+#include <stdarg.h>
+#include <stdlib.h>
+
+/*-<a                             href="qh-user_r.htm#TOC"
+  >-------------------------------</a><a name="qh_exit">-</a>
+
+  qh_exit( exitcode )
+    exit program
+
+  notes:
+    qh_exit() is called when qh_errexit() and longjmp() are not available.
+
+    This is the only use of exit() in Qhull
+    To replace qh_exit with 'throw', see libqhullcpp/usermem_r-cpp.cpp
+*/
+void qh_exit(int exitcode) {
+    exit(exitcode);
+} /* exit */
+
+/*-<a                             href="qh-user_r.htm#TOC"
+  >-------------------------------</a><a name="qh_fprintf_stderr">-</a>
+
+  qh_fprintf_stderr( msgcode, format, list of args )
+    fprintf to stderr with msgcode (non-zero)
+
+  notes:
+    qh_fprintf_stderr() is called when qh->ferr is not defined, usually due to an initialization error
+    
+    It is typically followed by qh_errexit().
+
+    Redefine this function to avoid using stderr
+
+    Use qh_fprintf [userprintf_r.c] for normal printing
+*/
+void qh_fprintf_stderr(int msgcode, const char *fmt, ... ) {
+    va_list args;
+
+    va_start(args, fmt);
+    if(msgcode)
+      fprintf(stderr, "QH%.4d ", msgcode);
+    vfprintf(stderr, fmt, args);
+    va_end(args);
+} /* fprintf_stderr */
+
+/*-<a                             href="qh-user_r.htm#TOC"
+>-------------------------------</a><a name="qh_free">-</a>
+
+  qh_free(qhT *qh, mem )
+    free memory
+
+  notes:
+    same as free()
+    No calls to qh_errexit() 
+*/
+void qh_free(void *mem) {
+    free(mem);
+} /* free */
+
+/*-<a                             href="qh-user_r.htm#TOC"
+    >-------------------------------</a><a name="qh_malloc">-</a>
+
+    qh_malloc( mem )
+      allocate memory
+
+    notes:
+      same as malloc()
+*/
+void *qh_malloc(size_t size) {
+    return malloc(size);
+} /* malloc */
+
+
diff --git a/C/userprintf_r.c b/C/userprintf_r.c
new file mode 100644
--- /dev/null
+++ b/C/userprintf_r.c
@@ -0,0 +1,65 @@
+/*<html><pre>  -<a                             href="qh-user_r.htm"
+  >-------------------------------</a><a name="TOP">-</a>
+
+   userprintf_r.c
+   qh_fprintf()
+
+   see README.txt  see COPYING.txt for copyright information.
+
+   If you recompile and load this file, then userprintf_r.o will not be loaded
+   from qhull.a or qhull.lib
+
+   See libqhull_r.h for data structures, macros, and user-callable functions.
+   See user_r.c for qhull-related, redefinable functions
+   see user_r.h for user-definable constants
+   See usermem_r.c for qh_exit(), qh_free(), and qh_malloc()
+   see Qhull.cpp and RboxPoints.cpp for examples.
+
+   Please report any errors that you fix to qhull@qhull.org
+*/
+
+#include "libqhull_r.h"
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+/*-<a                             href="qh-user_r.htm#TOC"
+   >-------------------------------</a><a name="qh_fprintf">-</a>
+
+   qh_fprintf(qh, fp, msgcode, format, list of args )
+     print arguments to *fp according to format
+     Use qh_fprintf_rbox() for rboxlib_r.c
+
+   notes:
+     same as fprintf()
+     fgets() is not trapped like fprintf()
+     exit qh_fprintf via qh_errexit()
+     may be called for errors in qh_initstatistics and qh_meminit
+*/
+
+void qh_fprintf(qhT *qh, FILE *fp, int msgcode, const char *fmt, ... ) {
+    va_list args;
+
+    if (!fp) {
+        if(!qh){
+            qh_fprintf_stderr(6241, "userprintf_r.c: fp and qh not defined for qh_fprintf '%s'", fmt);
+            qh_exit(qhmem_ERRqhull);  /* can not use qh_errexit() */
+        }
+        /* could use qh->qhmem.ferr, but probably better to be cautious */
+        qh_fprintf_stderr(6232, "Qhull internal error (userprintf_r.c): fp is 0.  Wrong qh_fprintf called.\n");
+        qh_errexit(qh, 6232, NULL, NULL);
+    }
+    va_start(args, fmt);
+    if (qh && qh->ANNOTATEoutput) {
+      fprintf(fp, "[QH%.4d]", msgcode);
+    }else if (msgcode >= MSG_ERROR && msgcode < MSG_STDERR ) {
+      fprintf(fp, "QH%.4d ", msgcode);
+    }
+    vfprintf(fp, fmt, args);
+    va_end(args);
+
+    /* Place debugging traps here. Use with option 'Tn' */
+
+} /* qh_fprintf */
+
diff --git a/C/utils.c b/C/utils.c
new file mode 100644
--- /dev/null
+++ b/C/utils.c
@@ -0,0 +1,91 @@
+#include <stdlib.h> // to use realloc
+#include <math.h> // to use NAN
+#include <stdio.h> // to use printf
+
+double* getpoint(double* points, unsigned dim, unsigned id){
+  double* out = malloc(dim * sizeof(double));
+  for(unsigned i=0; i < dim; i++){
+    out[i] = points[id*dim+i];
+  }
+  return out;
+}
+
+/* dot product of two vectors */
+double dotproduct(double* p1, double* p2, unsigned dim){
+  double out = 0;
+  for(unsigned i=0; i < dim; i++){
+    out += p1[i] * p2[i];
+  }
+  return out;
+}
+
+/* middle of segment [p1,p2] */
+double* middle(double* p1, double* p2, unsigned dim){
+  double* out = malloc(dim * sizeof(double));
+  for(unsigned i=0; i<dim; i++){
+    out[i] = (p1[i] + p2[i])/2;
+  }
+  return out;
+}
+
+/* vector of NANs */
+double* nanvector(int dim){
+  double* out = malloc(dim * sizeof(double));
+  for(unsigned i=0; i < dim; i++){
+    out[i] = NAN;
+  }
+  return out;
+}
+
+// to use the qsort function
+int cmpfunc (const void * a, const void * b) {
+   return ( *(int*)a - *(int*)b );
+}
+int cmpfuncdbl (const void * a, const void * b) {
+   return ( *(double*)a - *(double*)b > 0 ? 1 : -1);
+}
+void qsortu(unsigned* vector, unsigned length){
+  qsort(vector, length, sizeof(unsigned), cmpfunc);
+}
+
+
+double square(double x){
+  return x*x;
+}
+
+/* append to a vector of unsigned */
+void appendu(unsigned x, unsigned** array, unsigned length, unsigned* flag){
+  *flag = 1;
+  for(unsigned i=0; i<length; i++){
+    if(x==*(*array + i)){
+      *flag = 0;
+      break;
+    }
+  }
+  if(*flag==1){
+    *array = realloc(*array, (length+1)*sizeof(unsigned));
+    if(*array == NULL){
+      printf("realloc failure - exiting");
+      exit(1);
+    }
+    *(*array + length) = x;
+  }
+}
+
+/* make a vector of zeros */
+unsigned* uzeros(unsigned length){
+  unsigned* out = malloc(length * sizeof(unsigned));
+  for(unsigned i=0; i < length; i++){
+    out[i] = 0;
+  }
+  return out;
+}
+
+/* squared distance between two points */
+double squaredDistance(double* p1, double* p2, unsigned dim){
+  double out = 0;
+  for(unsigned i=0; i < dim; i++){
+    out += square(p1[i] - p2[i]);
+  }
+  return out;
+}
diff --git a/GPL_3 b/GPL_3
new file mode 100644
--- /dev/null
+++ b/GPL_3
@@ -0,0 +1,674 @@
+                    GNU GENERAL PUBLIC LICENSE
+                       Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                            Preamble
+
+  The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+  The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works.  By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users.  We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors.  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+  To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights.  Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received.  You must make sure that they, too, receive
+or can get the source code.  And you must show them these terms so they
+know their rights.
+
+  Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+  For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software.  For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+  Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so.  This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software.  The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable.  Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products.  If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+  Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary.  To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                       TERMS AND CONDITIONS
+
+  0. Definitions.
+
+  "This License" refers to version 3 of the GNU General Public License.
+
+  "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+  "The Program" refers to any copyrightable work licensed under this
+License.  Each licensee is addressed as "you".  "Licensees" and
+"recipients" may be individuals or organizations.
+
+  To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy.  The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+  A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+  To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy.  Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+  To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies.  Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+  An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License.  If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+  1. Source Code.
+
+  The "source code" for a work means the preferred form of the work
+for making modifications to it.  "Object code" means any non-source
+form of a work.
+
+  A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+  The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form.  A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+  The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities.  However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work.  For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+  The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+  The Corresponding Source for a work in source code form is that
+same work.
+
+  2. Basic Permissions.
+
+  All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met.  This License explicitly affirms your unlimited
+permission to run the unmodified Program.  The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work.  This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+  You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force.  You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright.  Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+  Conveying under any other circumstances is permitted solely under
+the conditions stated below.  Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+  3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+  No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+  When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+  4. Conveying Verbatim Copies.
+
+  You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+  You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+  5. Conveying Modified Source Versions.
+
+  You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+    a) The work must carry prominent notices stating that you modified
+    it, and giving a relevant date.
+
+    b) The work must carry prominent notices stating that it is
+    released under this License and any conditions added under section
+    7.  This requirement modifies the requirement in section 4 to
+    "keep intact all notices".
+
+    c) You must license the entire work, as a whole, under this
+    License to anyone who comes into possession of a copy.  This
+    License will therefore apply, along with any applicable section 7
+    additional terms, to the whole of the work, and all its parts,
+    regardless of how they are packaged.  This License gives no
+    permission to license the work in any other way, but it does not
+    invalidate such permission if you have separately received it.
+
+    d) If the work has interactive user interfaces, each must display
+    Appropriate Legal Notices; however, if the Program has interactive
+    interfaces that do not display Appropriate Legal Notices, your
+    work need not make them do so.
+
+  A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit.  Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+  6. Conveying Non-Source Forms.
+
+  You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+    a) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by the
+    Corresponding Source fixed on a durable physical medium
+    customarily used for software interchange.
+
+    b) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by a
+    written offer, valid for at least three years and valid for as
+    long as you offer spare parts or customer support for that product
+    model, to give anyone who possesses the object code either (1) a
+    copy of the Corresponding Source for all the software in the
+    product that is covered by this License, on a durable physical
+    medium customarily used for software interchange, for a price no
+    more than your reasonable cost of physically performing this
+    conveying of source, or (2) access to copy the
+    Corresponding Source from a network server at no charge.
+
+    c) Convey individual copies of the object code with a copy of the
+    written offer to provide the Corresponding Source.  This
+    alternative is allowed only occasionally and noncommercially, and
+    only if you received the object code with such an offer, in accord
+    with subsection 6b.
+
+    d) Convey the object code by offering access from a designated
+    place (gratis or for a charge), and offer equivalent access to the
+    Corresponding Source in the same way through the same place at no
+    further charge.  You need not require recipients to copy the
+    Corresponding Source along with the object code.  If the place to
+    copy the object code is a network server, the Corresponding Source
+    may be on a different server (operated by you or a third party)
+    that supports equivalent copying facilities, provided you maintain
+    clear directions next to the object code saying where to find the
+    Corresponding Source.  Regardless of what server hosts the
+    Corresponding Source, you remain obligated to ensure that it is
+    available for as long as needed to satisfy these requirements.
+
+    e) Convey the object code using peer-to-peer transmission, provided
+    you inform other peers where the object code and Corresponding
+    Source of the work are being offered to the general public at no
+    charge under subsection 6d.
+
+  A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+  A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling.  In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage.  For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product.  A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+  "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source.  The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+  If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information.  But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+  The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed.  Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+  Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+  7. Additional Terms.
+
+  "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law.  If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+  When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it.  (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.)  You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+  Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+    a) Disclaiming warranty or limiting liability differently from the
+    terms of sections 15 and 16 of this License; or
+
+    b) Requiring preservation of specified reasonable legal notices or
+    author attributions in that material or in the Appropriate Legal
+    Notices displayed by works containing it; or
+
+    c) Prohibiting misrepresentation of the origin of that material, or
+    requiring that modified versions of such material be marked in
+    reasonable ways as different from the original version; or
+
+    d) Limiting the use for publicity purposes of names of licensors or
+    authors of the material; or
+
+    e) Declining to grant rights under trademark law for use of some
+    trade names, trademarks, or service marks; or
+
+    f) Requiring indemnification of licensors and authors of that
+    material by anyone who conveys the material (or modified versions of
+    it) with contractual assumptions of liability to the recipient, for
+    any liability that these contractual assumptions directly impose on
+    those licensors and authors.
+
+  All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10.  If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term.  If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+  If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+  Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+  8. Termination.
+
+  You may not propagate or modify a covered work except as expressly
+provided under this License.  Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+  However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+  Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+  Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License.  If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+  9. Acceptance Not Required for Having Copies.
+
+  You are not required to accept this License in order to receive or
+run a copy of the Program.  Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance.  However,
+nothing other than this License grants you permission to propagate or
+modify any covered work.  These actions infringe copyright if you do
+not accept this License.  Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+  10. Automatic Licensing of Downstream Recipients.
+
+  Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License.  You are not responsible
+for enforcing compliance by third parties with this License.
+
+  An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations.  If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+  You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License.  For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+  11. Patents.
+
+  A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based.  The
+work thus licensed is called the contributor's "contributor version".
+
+  A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version.  For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+  Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+  In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement).  To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+  If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients.  "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+  If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+  A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License.  You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+  Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+  12. No Surrender of Others' Freedom.
+
+  If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all.  For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+  13. Use with the GNU Affero General Public License.
+
+  Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work.  The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+  14. Revised Versions of this License.
+
+  The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+  Each version is given a distinguishing version number.  If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation.  If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+  If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+  Later license versions may give you additional or different
+permissions.  However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+  15. Disclaimer of Warranty.
+
+  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. Limitation of Liability.
+
+  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+  17. Interpretation of Sections 15 and 16.
+
+  If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+                     END OF TERMS AND CONDITIONS
+
+            How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+  If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+    <program>  Copyright (C) <year>  <name of author>
+    This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+  You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+<http://www.gnu.org/licenses/>.
+
+  The GNU General Public License does not permit incorporating your program
+into proprietary programs.  If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library.  If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.  But first, please read
+<http://www.gnu.org/philosophy/why-not-lgpl.html>.
diff --git a/README.md b/README.md
new file mode 100644
--- /dev/null
+++ b/README.md
@@ -0,0 +1,476 @@
+# qhull
+
+Delaunay triangulation, Voronoi diagrams and convex hulls.
+Based on the `qhull` C library.
+
+## Delaunay tesselation
+
+Consider this list of vertices (actually these are the vertices of a
+polyhedron):
+
+```haskell
+vertices = [
+            [ -5, -5,  16 ]  -- 0
+          , [ -5,  8,   3 ]  -- 1
+          , [  4, -1,   3 ]  -- 2
+          , [  4, -5,   7 ]  -- 3
+          , [  4, -1, -10 ]  -- 4
+          , [  4, -5, -10 ]  -- 5
+          , [ -5,  8, -10 ]  -- 6
+          , [ -5, -5, -10 ]  -- 7
+                           ]
+```
+
+The `delaunay` function splits the polyhedron into simplices, the tiles of the
+tesselation:
+
+```haskell
+> import Delaunay
+> d <- delaunay vertices False False
+> _tiles d
+fromList
+  [ ( 0
+    , Tile
+        { _simplex =
+            Simplex
+              { _points =
+                  fromList
+                    [ ( 2 , [ 4.0 , -1.0 , 3.0 ] )
+                    , ( 4 , [ 4.0 , -1.0 , -10.0 ] )
+                    , ( 5 , [ 4.0 , -5.0 , -10.0 ] )
+                    , ( 7 , [ -5.0 , -5.0 , -10.0 ] )
+                    ]
+              , _circumcenter =
+                  [ -0.5000000000000009 , -3.0 , -3.499999999999999 ]
+              , _circumradius = 8.154753215150047
+              , _volume = 78.0
+              }
+        , _neighborsIds = fromList [ 1 , 3 ]
+        , _facetsIds = fromList [ 0 , 1 , 2 , 3 ]
+        , _family = Nothing
+        , _toporiented = False
+        }
+    )
+  , ( 1
+    , Tile
+        { _simplex =
+  ......
+```
+
+The field `_tiles` is a map of `Tile` objects. The keys of the map are
+the tiles identifiers. A `Tile` object has five fields:
+
+-   `_simplex`, a `Simplex` object;
+
+-   `_neighborsIds`, a set of tiles identifiers, the neighbors of the tile;
+
+-   `facetsIds`, a set of facets identifiers, the facets of the tile;
+
+-   `family`, two tiles of the same family share the same circumcenter;
+
+-   `toporiented`, Boolean, whether the tile is top-oriented.
+
+A `Simplex` object has four fields:
+
+-   `_points`, the vertices of the simplex, actually a map of the vertices
+identifiers to their coordinates
+
+-   `_circumcenter`, the coordinates of the circumcenter of the simplex;
+
+-   `_circumradius`, the circumradius;
+
+-   `_volume`, the volume of the simplex (the area in dimension 2, the
+  length in dimension 1).
+
+Another field of the output of `delaunay` is `_tilefacets`:
+
+```haskell
+> _tilefacets d
+fromList
+  [ ( 0
+    , TileFacet
+        { _subsimplex =
+            Simplex
+              { _points =
+                  fromList
+                    [ ( 4 , [ 4.0 , -1.0 , -10.0 ] )
+                    , ( 5 , [ 4.0 , -5.0 , -10.0 ] )
+                    , ( 7 , [ -5.0 , -5.0 , -10.0 ] )
+                    ]
+              , _circumcenter = [ -0.5000000000000009 , -3.0 , -10.0 ]
+              , _circumradius = 4.924428900898053
+              , _volume = 36.0
+              }
+        , _facetOf = fromList [ 0 ]
+        , _normal = [ 0.0 , 0.0 , -1.0 ]
+        , _offset = -10.0
+        }
+    )
+  , ( 1
+    , TileFacet
+        { _subsimplex =
+  ......
+```
+
+This is a map of `TileFacet` objects. A tile facet is a subsimplex. The keys of
+the map are the identifiers of the facets.
+A `TileFacet` object has four fields: `_subsimplex`, a `Simplex` object,
+`_facetOf`, the identifiers of the tiles this facet belongs to (a set of one
+or two integers), `_normal`, the normal of the facet, and `offset`, the offset
+of the facet.
+
+Finally, the output of `delaunay` has a `_sites` field, the vertices with
+additional information:
+
+```haskell
+> _sites d
+fromList
+  [ ( 0
+    , Site
+        { _point = [ -5.0 , -5.0 , 16.0 ]
+        , _neighsitesIds = fromList [ 1 , 3 , 7 ]
+        , _neighfacetsIds = fromList [ 15 , 16 , 17 ]
+        , _neightilesIds = fromList [ 5 ]
+        }
+    )
+  , ( 1
+    , Site
+  ......
+```
+
+This is a map of `Site` objects. The keys of the map are the identifiers of
+the vertices. A `Site` object has four fields:
+
+-   `_point`, the coordinates of the vertex;
+
+-   `_neighsitesIds`, the identifiers of the connected vertices;
+
+-   `_neighfacetsIds`, a set of integers, the identifiers of the facets the
+vertex belongs to;
+
+-   `_neightilesIds`, the set of the identifiers of the tiles the vertex belongs
+to.
+
+[![gfycat](https://thumbs.gfycat.com/FreeFaithfulArgali-size_restricted.gif)](https://gfycat.com/FreeFaithfulArgali)
+
+
+## Voronoi diagrams
+
+The library allows to get the Voronoi diagram of a list of sites (vertices)
+from the Delaunay tesselation. Here is a 3D example.
+
+```haskell
+centricCuboctahedron :: [[Double]]
+centricCuboctahedron = [[i,j,0] | i <- [-1,1], j <- [-1,1]] ++
+                       [[i,0,j] | i <- [-1,1], j <- [-1,1]] ++
+                       [[0,i,j] | i <- [-1,1], j <- [-1,1]] ++
+                       [[0,0,0]]
+import Delaunay
+import Voronoi3D
+d <- delaunay centricCuboctahedron False False
+v = voronoi3 d
+```
+
+In some circumstances, one has to run the Delaunay tesselation including the
+degenerate tiles in order to get the correct Voronoi diagram, that is to say
+`delaunay vertices False True`.
+
+The output of `voronoi3` is a list of Voronoi cells given as pairs, each pair
+consisting of a site and a list of edges.
+This is the cell of the center `[0, 0, 0]`:
+
+```haskell
+> last v
+( [ 0.0 , 0.0 , 0.0 ]
+, [ Edge3 ( ( -0.5 , -0.5 , 0.5 ) , ( 0.0 , 0.0 , 1.0 ) )
+  , Edge3 ( ( -0.5 , -0.5 , 0.5 ) , ( 0.0 , -1.0 , 0.0 ) )
+  , Edge3 ( ( -0.5 , -0.5 , 0.5 ) , ( -1.0 , 0.0 , 0.0 ) )
+  , Edge3 ( ( -0.5 , 0.5 , 0.5 ) , ( 0.0 , 0.0 , 1.0 ) )
+  , Edge3 ( ( -0.5 , 0.5 , 0.5 ) , ( 0.0 , 1.0 , 0.0 ) )
+  , Edge3 ( ( -0.5 , 0.5 , 0.5 ) , ( -1.0 , 0.0 , 0.0 ) )
+  , Edge3 ( ( 0.5 , -0.5 , 0.5 ) , ( 0.0 , 0.0 , 1.0 ) )
+  , Edge3 ( ( 0.5 , -0.5 , 0.5 ) , ( 0.0 , -1.0 , 0.0 ) )
+  , Edge3 ( ( 0.5 , -0.5 , 0.5 ) , ( 1.0 , 0.0 , 0.0 ) )
+  , Edge3 ( ( 0.5 , 0.5 , 0.5 ) , ( 0.0 , 0.0 , 1.0 ) )
+  , Edge3 ( ( 0.5 , 0.5 , 0.5 ) , ( 0.0 , 1.0 , 0.0 ) )
+  , Edge3 ( ( 0.5 , 0.5 , 0.5 ) , ( 1.0 , 0.0 , 0.0 ) )
+  , Edge3 ( ( -0.5 , -0.5 , -0.5 ) , ( 0.0 , 0.0 , -1.0 ) )
+  , Edge3 ( ( -0.5 , -0.5 , -0.5 ) , ( 0.0 , -1.0 , 0.0 ) )
+  , Edge3 ( ( -0.5 , -0.5 , -0.5 ) , ( -1.0 , 0.0 , 0.0 ) )
+  , Edge3 ( ( -0.5 , 0.5 , -0.5 ) , ( 0.0 , 0.0 , -1.0 ) )
+  , Edge3 ( ( -0.5 , 0.5 , -0.5 ) , ( 0.0 , 1.0 , 0.0 ) )
+  , Edge3 ( ( -0.5 , 0.5 , -0.5 ) , ( -1.0 , 0.0 , 0.0 ) )
+  , Edge3 ( ( 0.5 , -0.5 , -0.5 ) , ( 0.0 , 0.0 , -1.0 ) )
+  , Edge3 ( ( 0.5 , -0.5 , -0.5 ) , ( 0.0 , -1.0 , 0.0 ) )
+  , Edge3 ( ( 0.5 , -0.5 , -0.5 ) , ( 1.0 , 0.0 , 0.0 ) )
+  , Edge3 ( ( 0.5 , 0.5 , -0.5 ) , ( 0.0 , 0.0 , -1.0 ) )
+  , Edge3 ( ( 0.5 , 0.5 , -0.5 ) , ( 0.0 , 1.0 , 0.0 ) )
+  , Edge3 ( ( 0.5 , 0.5 , -0.5 ) , ( 1.0 , 0.0 , 0.0 ) )
+  ]
+)
+```
+
+This is a bounded cell: it has finite edges only. The other ones are not
+bounded, they have infinite edges:
+
+```haskell
+> head v
+( [ -1.0 , -1.0 , 0.0 ]
+, [ Edge3 ( ( -0.5 , -0.5 , 0.5 ) , ( 0.0 , -1.0 , 0.0 ) )
+  , Edge3 ( ( -0.5 , -0.5 , 0.5 ) , ( -1.0 , 0.0 , 0.0 ) )
+  , IEdge3
+      ( ( -0.5 , -0.5 , 0.5 )
+      , ( -0.5773502691896258 , -0.5773502691896258 , 0.5773502691896258 )
+      )
+  , Edge3 ( ( -0.5 , -0.5 , -0.5 ) , ( 0.0 , -1.0 , 0.0 ) )
+  , Edge3 ( ( -0.5 , -0.5 , -0.5 ) , ( -1.0 , 0.0 , 0.0 ) )
+  , IEdge3
+      ( ( -0.5 , -0.5 , -0.5 )
+      , ( -0.5773502691896258 , -0.5773502691896258 , -0.5773502691896258 )
+      )
+  , IEdge3 ( ( -1.0 , 0.0 , 0.0 ) , ( 1.0 , 0.0 , 0.0 ) )
+  , IEdge3 ( ( 0.0 , -1.0 , 0.0 ) , ( 0.0 , -1.0 , 0.0 ) )
+  ]
+)
+```
+
+[![gfycat](https://thumbs.gfycat.com/HarmoniousHighlevelBushbaby-size_restricted.gif)](https://gfycat.com/HarmoniousHighlevelBushbaby)
+
+
+## Convex hull
+
+The `convexHull` function of the `ConvexHull` module generates the convex hull
+of a list of points.
+
+```haskell
+import ConvexHull
+import ConvexHull.Examples -- for the function randomInCube
+points <- randomInCube 100 -- 100 random points in a cube
+hull <- convexHull points False False Nothing
+```
+
+The vertices of the convex hull are stored in the field `_hvertices`:
+
+```haskell
+> _hvertices hull
+fromList
+  [ ( 3
+    , Vertex
+        { _point =
+            [ 0.7872072051657094 , 0.450772463858757 , 1.9900427529711773e-2 ]
+        , _neighfacets = fromList [ 42 , 43 , 47 , 48 ]
+        , _neighvertices = fromList [ 1 , 11 , 64 , 88 ]
+        , _neighridges = fromList [ 70 , 71 , 72 , 77 ]
+        }
+    )
+  , ( 6
+    , Vertex
+  ......
+```
+
+The edges in the field `_hedges`:
+
+```haskell
+> _hedges hull
+fromList
+  [ ( Pair 14 70
+    , ( [ 0.9215432980174852 , 0.8554065771602318 , 0.9842902519648512 ]
+      , [ 0.9497713758656887 , 0.998006476041318 , 0.7243639875028591 ]
+      )
+    )
+  , ( Pair 84 99
+  ......
+```
+
+The facets in the field `_hfacets`:
+
+```haskell
+> _hfacets hull
+fromList
+  [ ( 0
+    , Facet
+        { _fvertices =
+            fromList
+              [ ( 4
+                , [ 1.5757133629105136e-3
+                  , 0.6442797662244039
+                  , 0.7058559215899725
+                  ]
+                )
+              , ( 67
+                , [ 2.7500520534961326e-2
+                  , 0.37516259577251554
+                  , 0.7331611715042575
+                  ]
+                )
+              , ( 77
+                , [ 3.46399386146774e-2
+                  , 5.575911794526589e-2
+                  , 0.46787034305814157
+                  ]
+                )
+              ]
+        , _fridges =
+            fromList
+              [ ( 0
+                , Ridge
+                    { _rvertices =
+                        fromList
+                          [ ( 4
+                            , [ 1.5757133629105136e-3
+                              , 0.6442797662244039
+                              , 0.7058559215899725
+                              ]
+                            )
+                          , ( 77
+                            , [ 3.46399386146774e-2
+                              , 5.575911794526589e-2
+                              , 0.46787034305814157
+                              ]
+                            )
+                          ]
+                    , _ridgeOf = fromList [ 0 , 4 ]
+                    }
+                )
+              , ( 1
+                , Ridge
+                    { _rvertices =
+                        fromList
+                          [ ( 4
+                            , [ 1.5757133629105136e-3
+                              , 0.6442797662244039
+                              , 0.7058559215899725
+                              ]
+                            )
+                          , ( 67
+                            , [ 2.7500520534961326e-2
+                              , 0.37516259577251554
+                              , 0.7331611715042575
+                              ]
+                            )
+                          ]
+                    , _ridgeOf = fromList [ 0 , 2 ]
+                    }
+                )
+              , ( 2
+                , Ridge
+                    { _rvertices =
+                        fromList
+                          [ ( 67
+                            , [ 2.7500520534961326e-2
+                              , 0.37516259577251554
+                              , 0.7331611715042575
+                              ]
+                            )
+                          , ( 77
+                            , [ 3.46399386146774e-2
+                              , 5.575911794526589e-2
+                              , 0.46787034305814157
+                              ]
+                            )
+                          ]
+                    , _ridgeOf = fromList [ 0 , 1 ]
+                    }
+                )
+              ]
+        , _centroid =
+            [ 2.1238724170849748e-2 , 0.3584004933140618 , 0.6356291453841239 ]
+        , _normal =
+            [ -0.9930268604214181
+            , -8.766369712550202e-2
+            , 7.882087723357102e-2
+            ]
+        , _offset = 2.40848904384814e-3
+        , _area = 4.0339144929987907e-2
+        , _neighbors = fromList [ 1 , 2 , 4 ]
+        , _family = None
+        , _fedges =
+            fromList
+              [ ( Pair 4 67
+                , ( [ 1.5757133629105136e-3
+                    , 0.6442797662244039
+                    , 0.7058559215899725
+                    ]
+                  , [ 2.7500520534961326e-2
+                    , 0.37516259577251554
+                    , 0.7331611715042575
+                    ]
+                  )
+                )
+              , ( Pair 67 77
+                , ( [ 2.7500520534961326e-2
+                    , 0.37516259577251554
+                    , 0.7331611715042575
+                    ]
+                  , [ 3.46399386146774e-2
+                    , 5.575911794526589e-2
+                    , 0.46787034305814157
+                    ]
+                  )
+                )
+              , ( Pair 4 77
+                , ( [ 1.5757133629105136e-3
+                    , 0.6442797662244039
+                    , 0.7058559215899725
+                    ]
+                  , [ 3.46399386146774e-2
+                    , 5.575911794526589e-2
+                    , 0.46787034305814157
+                    ]
+                  )
+                )
+              ]
+        }
+    )
+  , ( 1
+    , Facet
+  ......
+```
+
+[![gfycat](https://thumbs.gfycat.com/QuaintUnrulyBlackpanther-size_restricted.gif)](https://gfycat.com/QuaintUnrulyBlackpanther)
+
+
+## Halfspaces intersections
+
+![equation](http://latex.codecogs.com/gif.latex?0%5Cleq%20x%5Cleq%203,%5Cquad%20%200%5Cleq%20y%5Cleq%202-%5Cfrac%7B2%7D%7B3%7Dx,%5Cquad%200%5Cleq%20z%5Cleq%206-2x-3y)
+
+```haskell
+import HalfSpaces
+import Data.Ratio ((%))
+x = newVar 1
+y = newVar 2
+z = newVar 3
+constraints =
+  [ x .>=  0 -- shortcut for x .>=. cst 0
+  , x .<=  3
+  , y .>=  0
+  , y .<=. cst 2 ^-^ (2%3)*^x
+  , z .>=  0
+  , z .<=. cst 6 ^-^ 2*^x ^-^ 3*^y ]
+```
+
+```haskell
+> hsintersections constraints False
+[ [ -1.1102230246251565e-16 , -1.1102230246251565e-16 , 6.0 ]
+, [ 0.0 , 2.0 , 0.0 ]
+, [ 0.0 , 0.0 , 0.0 ]
+, [ 3.0 , 0.0 , 0.0 ] ]
+```
+
+## Gallery
+
+The convex hull of a curve on the sphere:
+
+![Imgur](https://i.imgur.com/kaS78HG.png)
+
+The Voronoi cell of a point inside the Utah teapot:
+
+![Imgur](https://i.imgur.com/gmgKDrE.png)
+
+The Voronoi diagram of a projection of the truncated tesseract:
+
+![Imgur](https://i.imgur.com/mocwfy6.png)
+
+The Voronoi diagram of a cube surrounded by three perpendicular circles:
+
+![Imgur](https://i.imgur.com/tK4rjhL.png)
diff --git a/Setup.hs b/Setup.hs
new file mode 100644
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,2 @@
+import Distribution.Simple
+main = defaultMain
diff --git a/qhull.cabal b/qhull.cabal
new file mode 100644
--- /dev/null
+++ b/qhull.cabal
@@ -0,0 +1,190 @@
+name:                qhull
+version:             0.1.0.1
+synopsis:       Delaunay triangulation, Voronoi diagrams and convex hulls. 
+description:    Based on the qhull C library. 
+
+                Maintenance version to bring it up to current ghc-version (ghc-8.10.7, lts-18.28).  
+homepage:            https://github.com/stla/qhull#readme
+license:             GPL-3
+license-file:        GPL_3
+author:              Stéphane Laurent
+maintainer:          Andrew U. Frank
+copyright:           2018 Stéphane Laurent
+category:            Math
+build-type:          Simple
+extra-source-files:  README.md
+cabal-version:       >=1.10
+
+flag exe
+  description:
+    Build the executables.
+  default: True
+flag exe-delaunay
+  default: True
+flag exe-voronoi
+  default: False
+flag exe-hull
+  default: False
+flag exe-hs
+  default: False
+flag exe-am
+  default: False
+
+library
+  hs-source-dirs:      src
+  exposed-modules:     Delaunay
+                     , Delaunay.Delaunay
+                     , Delaunay.CDelaunay
+                     , Delaunay.Types
+                     , Delaunay.R
+                     , Delaunay.Examples
+                     , Voronoi2D
+                     , Voronoi3D
+                     , Voronoi.Voronoi
+                     , Voronoi.R
+                     , Voronoi.Shared
+                     , ConvexHull
+                     , ConvexHull.ConvexHull
+                     , ConvexHull.CConvexHull
+                     , ConvexHull.Types
+                     , ConvexHull.R
+                     , ConvexHull.Examples
+                     , Qhull.Types
+                     , Qhull.Shared
+                     , HalfSpaces.LinearCombination
+                     , HalfSpaces.Constraint
+                     , HalfSpaces.Internal
+                     , HalfSpaces.ToySolver
+                     , HalfSpaces.CHalfSpaces
+                     , HalfSpaces.HalfSpaces
+                     , HalfSpaces.Examples
+                     , HalfSpaces
+                     , ConvexHull.Truncated120Cell3
+                     , ConvexHull.CantiTrunc600Cell.Data
+                     , ConvexHull.BiTruncatedTesseract
+                     , ConvexHull.SnubDodecahedron.SnubDodecahedron
+                     , ConvexHull.OmniTruncated120Cell
+                     , Delaunay.Adjacency
+  build-depends:       base >= 4.7 && < 5
+                     , split
+                     , containers
+                     , pretty-show
+                     , extra
+                     , ilist
+                     , hashable
+                     , insert-ordered-containers
+                     , random
+                     , Unique
+                     , vector-space 
+                     , vector-algorithms ==0.8.0.3
+                     , toysolver
+                     , data-default-class
+                     , combinat
+                    --  , permutation
+                     , regex-compat
+                     , regex-base
+                     , regex-posix
+  other-extensions:    ForeignFunctionInterface
+                     , TypeFamilies
+  default-language:    Haskell2010
+  include-dirs:        C
+  C-sources:           ./C/libqhull_r.c
+                     , ./C/geom_r.c
+                     , ./C/geom2_r.c
+                     , ./C/global_r.c
+                     , ./C/io_r.c
+                     , ./C/mem_r.c
+                     , ./C/merge_r.c
+                     , ./C/poly_r.c
+                     , ./C/poly2_r.c
+                     , ./C/qset_r.c
+                     , ./C/random_r.c
+                     , ./C/usermem_r.c
+                     , ./C/userprintf_r.c
+                     , ./C/user_r.c
+                     , ./C/stat_r.c
+                     , ./C/delaunay.c
+                     , ./C/convexhull.c
+                     , ./C/utils.c
+                     , ./C/halfspaces.c
+  ghc-options:         -O0 -Wall
+
+executable test_delaunay
+  if flag(exe) || flag(exe-delaunay)
+    buildable:         True
+  else
+    buildable:         False
+  hs-source-dirs:      src-exe/Delaunay
+  main-is:             Main.hs
+  default-language:    Haskell2010
+  build-depends:       base >= 4.7 && < 5
+                     , qhull
+                     , pretty-show
+                     , containers
+                     , insert-ordered-containers
+
+executable test_voronoi
+  if flag(exe) || flag(exe-voronoi)
+    buildable:         True
+  else
+    buildable:         False
+  hs-source-dirs:      src-exe/Voronoi
+  main-is:             Main.hs
+  default-language:    Haskell2010
+  build-depends:       base >= 4.7 && < 5
+                     , qhull
+                     , pretty-show
+                     , containers
+                     , ilist
+
+executable test_convexhull
+  if flag(exe) || flag(exe-hull)
+    buildable:         True
+  else
+    buildable:         False
+  hs-source-dirs:      src-exe/ConvexHull
+  main-is:             Main.hs
+  default-language:    Haskell2010
+  build-depends:       base >= 4.7 && < 5
+                     , qhull
+                     , pretty-show
+                     , containers
+                     , insert-ordered-containers
+                     , combinat
+                    --  , permutation
+                     , ilist
+                     , extra
+                     , regex-compat
+                     , regex-base
+                     , regex-posix
+  ghc-options:         -O0
+
+executable test_halfspaces
+  if flag(exe) || flag(exe-hs)
+    buildable:         True
+  else
+    buildable:         False
+  hs-source-dirs:      src-exe/HalfSpaces
+  main-is:             Main.hs
+  default-language:    Haskell2010
+  build-depends:       base >= 4.7 && < 5
+                     , qhull
+                     , pretty-show
+
+executable adjacencymatrix
+  if flag(exe) || flag(exe-am)
+    buildable:         True
+  else
+    buildable:         False
+  hs-source-dirs:      src-exe/Adjacency
+  main-is:             AdjacencyMatrix.hs
+  default-language:    Haskell2010
+  build-depends:       base >= 4.7 && < 5
+                     , qhull
+                     , optparse-applicative
+                     , containers
+  ghc-options:         -main-is Adjacency.AdjacencyMatrix
+
+source-repository head
+  type:     git
+  location: https://github.com/stla/qhull
diff --git a/src-exe/Adjacency/AdjacencyMatrix.hs b/src-exe/Adjacency/AdjacencyMatrix.hs
new file mode 100644
--- /dev/null
+++ b/src-exe/Adjacency/AdjacencyMatrix.hs
@@ -0,0 +1,53 @@
+module Adjacency.AdjacencyMatrix
+  where
+import qualified Data.IntMap.Strict as IM
+import qualified Data.IntSet        as IS
+import           Data.List
+import           Data.Monoid         ((<>))
+import           Delaunay.Delaunay
+import           Delaunay.Types
+import           Options.Applicative
+
+delaunayTess :: [[Double]] -> IO Tesselation
+delaunayTess vertices = delaunay vertices False False Nothing
+
+delaunayVertices :: FilePath -> IO [[Double]]
+delaunayVertices verts = do
+  v <- readFile verts
+  return $ read v :: IO [[Double]]
+
+adjacency :: Tesselation -> Int -> [Int]
+adjacency tess i =  map (fromEnum . ((i `IS.member`) . _neighsitesIds) . snd) $ IM.toList $ _sites tess
+
+adjacency' :: Tesselation -> [[Int]]
+adjacency' tess = map (adjacency tess) (IM.keys ( _sites tess ))
+
+data Arguments = Arguments { infile :: FilePath, outfile :: FilePath }
+
+run :: Parser Arguments
+run = Arguments
+     <$> argument str
+           ( metavar "INFILE"
+          <> help "File of vertices" )
+      <*> argument str
+           ( metavar "OUTFILE"
+          <> help "adjacency matrix" )
+
+writeMatrix :: [[Int]] -> FilePath -> IO ()
+writeMatrix matrix outfile = writeFile outfile (intercalate "\n" (map show matrix))
+
+doMatrix :: Arguments -> IO ()
+doMatrix (Arguments infile outfile) =
+  do
+    v <- delaunayVertices infile
+    tess <- delaunayTess v
+    let mat = adjacency' tess
+    writeMatrix mat outfile
+
+main :: IO ()
+main = execParser opts >>= doMatrix
+  where
+    opts = info (helper <*> run)
+      ( fullDesc
+     <> progDesc "Adjacency matrix of a Delaunay tesselation"
+     <> header "adjacencymatrix -- based on qhull" )
diff --git a/src-exe/ConvexHull/Main.hs b/src-exe/ConvexHull/Main.hs
new file mode 100644
--- /dev/null
+++ b/src-exe/ConvexHull/Main.hs
@@ -0,0 +1,847 @@
+module Main
+  where
+import           ConvexHull
+import           ConvexHull.BiTruncatedTesseract
+import           ConvexHull.CantiTrunc600Cell.Data
+import           ConvexHull.Examples                          hiding
+                                                               (regularSphere,
+                                                               regularTetrahedron)
+import           ConvexHull.OmniTruncated120Cell
+import           ConvexHull.R
+import           ConvexHull.SnubDodecahedron.SnubDodecahedron
+import           ConvexHull.Truncated120Cell3
+import           Data.Function                                (on)
+import qualified Data.HashMap.Strict.InsOrd                   as H
+import qualified Data.IntMap.Strict                           as IM
+import           Data.List
+import           Data.List.Index
+-- import           Data.Permute                                 (elems, rank)
+import qualified Data.Set                                     as S
+import           Data.Tuple.Extra
+import           System.IO
+import           Text.Printf
+import           Text.Regex
+import           Text.Show.Pretty
+
+approx :: RealFrac a => Int -> a -> a
+approx n x = fromInteger (round $ x * (10^n)) / (10.0^^n)
+
+roundedVertices :: Int -> [[Double]] -> [[Double]]
+roundedVertices n = map (map (approx n))
+
+
+-- fixIndices :: [[Double]] -> [[Int]] -> ([[Double]], [[Int]])
+-- fixIndices allVertices faces = (newvertices, newfaces)
+--   where
+--   faceselems = nub $ foldr union [] faces
+--   l = length faceselems
+--   permute = elems $ rank l faceselems
+--   mapper = IM.fromList $ zip permute faceselems
+--   mapper' = IM.fromList $ zip faceselems permute
+--   newfaces = map (map ((IM.!) mapper')) faces
+--   --newvertices =
+-- --    (fromJust <$> (filter isJust $ (map atMay ([allVertices !! (mapper IM.! i) | i <- IM.keys mapper])))) `intersect` IM.keys mapper
+--   -- newvertices = [allVertices !! (mapper IM.! i) | i <- IM.keys mapper]
+--   newvertices = [allVertices !! i | i <- [0 .. length allVertices-1] `intersect` IM.keys mapper]
+-- --
+-- -- regularTetrahedron' :: [[Double]]
+-- -- regularTetrahedron' =
+-- --   [ [0.5 / sqrt 3, -0.5, 0.5 / sqrt 6]
+-- --   , [sqrt 3 / 3, 0, -0.5 / sqrt 6]
+-- --   , [0.5 / sqrt 3, 0.5, -0.5 / sqrt 6]
+-- --   , [0, 0, 0.5 * sqrt 3 / sqrt 2] ]
+-- --
+-- regularSphere :: Int -> [Double] -> Double -> ([[Double]], [[Int]])
+-- regularSphere n center rho =
+--   (zipWith (s2c rho) theta phi, [[i,j] | i <- [0 .. n-1], j <- [1 .. n-1]])
+--   where
+--   gridtheta = [frac i n | i <- [0 .. n-1]]
+--   theta = map (*(2*pi)) gridtheta
+--   gridphi = [frac i n | i <- [1 .. n-1]]
+--   phi = map (*pi) gridphi
+--   frac :: Int -> Int -> Double
+--   frac p q = realToFrac p / realToFrac q
+--   s2c :: Double -> Double -> Double -> [Double]
+--   s2c r th ph = [r * cos th * sin ph + center!!0, r * sin th * sin ph + center!!1, r * cos ph + center!!2]
+--
+-- sphere1,sphere2,sphere3,sphere4 :: ([[Double]], [[Int]])
+-- sphere1 = regularSphere 40 (regularTetrahedron !! 0) (sqrt 6 / 4)
+-- sphere2 = regularSphere 40 (regularTetrahedron !! 1) (sqrt 6 / 4)
+-- sphere3 = regularSphere 40 (regularTetrahedron !! 2) (sqrt 6 / 4)
+-- sphere4 = regularSphere 40 (regularTetrahedron !! 3) (sqrt 6 / 4)
+--
+--
+-- -- regular tetrahederon -- --
+-- regularTetrahedron :: [[Double]]
+-- regularTetrahedron =
+--     -- [[i, 0, -1/sqrt 2] | i <- pm] ++ [[0, i , 1/sqrt 2] | i <- pm]
+--     -- where pm = [-1,1]
+--     [ [ -1.0 , 0.0 , -0.7071067811865475 ]
+--     , [ 0.0 , 1.0 , -0.7071067811865475 ]
+--     , [ 0.0 , -1.0 , 0.7071067811865475 ]
+--     , [ 1.0, 0.0 ,  0.7071067811865475 ]
+--     ]
+--
+
+-- stringify :: Show a => String -> [a] -> String
+-- stringify sep = intercalate sep . map show
+
+
+main :: IO ()
+main = do
+
+  h <- convexHull sixhundredCell False False Nothing
+  pPrint $ hullSummary h
+  putStrLn "vertices:"
+  pPrint $ roundedVertices 4 $ verticesCoordinates h
+  putStrLn "edges:"
+  pPrint $ edgesIds' h
+  putStrLn "\nTETRAHEDRAL FACETS:"
+  let tetras = IM.filter (\f -> length (verticesIds f) == 4) (_hfacets h)
+  pPrint $ IM.toList (IM.map verticesIds tetras)
+
+  -- h <- convexHull hexadecachoron False False Nothing
+  -- pPrint $ hullSummary h
+  -- putStrLn "edges:"
+  -- pPrint $ edgesIds' h
+  -- putStrLn "\nTETRAHEDRAL FACETS:"
+  -- let tetras = IM.filter (\f -> length (verticesIds f) == 4) (_hfacets h)
+  -- pPrint $ IM.toList (IM.map verticesIds tetras)
+
+  -- h <- convexHull runcitruncated5cell False False Nothing
+  -- pPrint $ hullSummary h
+  -- putStrLn "edges:"
+  -- pPrint $ edgesIds' h
+
+  -- h <- convexHull rectified5cell False False Nothing
+  -- pPrint $ hullSummary h
+  -- putStrLn "edges:"
+  -- pPrint $ edgesIds' h
+  -- putStrLn "\nTETRAHEDRAL FACETS:"
+  -- let tetras = IM.filter (\f -> length (verticesIds f) == 4) (_hfacets h)
+  -- pPrint $ IM.map verticesIds tetras
+
+  -- h <- convexHull sircope False False Nothing
+  -- pPrint $ hullSummary h
+  -- putStrLn "edges:"
+  -- pPrint $ edgesIds' h
+  -- putStrLn "\nTRIANGLE PRISM FACETS:"
+  -- let tprisms = IM.filter (\f -> length (verticesIds f) == 6) (_hfacets h)
+  -- pPrint $ IM.elems (IM.map verticesIds tprisms)
+  -- putStrLn "ridges:"
+  -- let ridges = map (IM.elems . facetRidges h) (IM.elems tprisms)
+  -- pPrint $ map (map (map fst . ridgeToPolygon)) ridges
+
+  -- h <- convexHull tutcup False False Nothing
+  -- pPrint $ hullSummary h
+  -- putStrLn "edges:"
+  -- pPrint $ edgesIds' h
+  -- putStrLn "\nTETRAHEDRAL FACETS:"
+  -- let tetras = IM.filter (\f -> length (verticesIds f) == 4) (_hfacets h)
+  -- pPrint $ IM.toList (IM.map verticesIds tetras)
+
+  -- h <- convexHull runcinatedTesseract False False Nothing
+  -- pPrint $ hullSummary h
+  -- putStrLn "edges:"
+  -- pPrint $ edgesIds' h
+  -- putStrLn "\nTETRAHEDRAL FACETS:"
+  -- let tetras = IM.filter (\f -> length (verticesIds f) == 4) (_hfacets h)
+  -- pPrint $ IM.map verticesIds tetras
+
+  -- h <- convexHull runcinated5cells False False Nothing
+  -- pPrint $ hullSummary h
+  -- putStrLn "edges:"
+  -- pPrint $ edgesIds' h
+  -- putStrLn "\nTETRAHEDRAL FACETS:"
+  -- let tetras = IM.filter (\f -> length (verticesIds f) == 4) (_hfacets h)
+  -- putStrLn "ridges:"
+  -- let ridges = map (IM.elems . facetRidges h) (IM.elems tetras)
+  -- pPrint $ map (map (map fst . ridgeToPolygon)) ridges
+
+  -- h <- convexHull duoprism330 False False Nothing
+  -- putStrLn $ hullSummary h
+  -- putStrLn "edges:"
+  -- pPrint $ edgesIds' h
+  -- putStrLn "same vertices:"
+  -- print $ verticesCoordinates h == duoprism330
+  -- putStrLn "\nALL ROUNDED VERTICES:"
+  -- pPrint $ roundedVertices 3 $ verticesCoordinates h
+
+  -- let curve3D = map (\x -> [ sin (pi*x) * cos (2*pi*x)
+  --                          , sin (pi*x) * sin (2*pi*x)
+  --                          , cos (pi*x)]) [i/100 | i <- [0 .. 100]]
+  -- h <- convexHull curve3D True False Nothing
+  -- pPrint $ hullSummary h
+  -- putStrLn "vertices:"
+  -- pPrint $ roundedVertices 10 $ verticesCoordinates h
+  -- putStrLn "facets:"
+  -- pPrint $ map (IM.keys . _vertices) (IM.elems $ _hfacets h) -- TODO FAIRE UNE FONCTION FACETIDS
+  -- c'est fait : verticesIds = IM.keys . _vertices
+  -- direct : facetsVerticesIds h
+
+  -- h <- convexHull truncated5cells False False Nothing
+  -- pPrint $ hullSummary h
+  -- putStrLn "vertices:"
+  -- pPrint $ roundedVertices 10 $ verticesCoordinates h
+  -- putStrLn "edges:"
+  -- pPrint $ edgesIds' h
+  -- putStrLn "\nTETRAHEDRAL FACETS:"
+  -- let tetras = IM.filter (\f -> length (verticesIds f) == 4) (_hfacets h)
+  -- putStrLn "ridges:"
+  -- let ridges = map (IM.elems . facetRidges h) (IM.elems tetras)
+  -- pPrint $ map (map (map fst . ridgeToPolygon)) ridges
+
+  -- h <- convexHull truncated24cells False False Nothing
+  -- pPrint $ hullSummary h
+  -- putStrLn "vertices:"
+  -- pPrint $ verticesCoordinates h
+  -- putStrLn "edges:"
+  -- pPrint $ edgesIds' h
+  -- putStrLn "\nCUBICAL FACETS:"
+  -- let cubes = IM.filter (\f -> length (verticesIds f) == 8) (_hfacets h)
+  -- putStrLn "ridges:"
+  -- let ridges = map (IM.elems . facetRidges h) (IM.elems cubes)
+  -- pPrint $ map (map (map fst . ridgeToPolygon)) ridges
+
+  -- h <- convexHull biTruncatedTesseract False False Nothing
+  -- pPrint $ hullSummary h
+  -- putStrLn "vertices:"
+  -- pPrint $ roundedVertices 3 $ verticesCoordinates h
+  -- putStrLn "edges:"
+  -- pPrint $ edgesIds' h
+  -- putStrLn "\nTRUNCATED OCTAHEDRAL FACETS:"
+  -- let octahedra = IM.filter (\f -> length (verticesIds f) == 24) (_hfacets h)
+  -- putStrLn "\nTRUNCATED TETRAHEDRAL FACETS:"
+  -- let tetrahedra = IM.filter (\f -> length (verticesIds f) == 12) (_hfacets h)
+  -- putStrLn "ridges:"
+  -- let ridges = map (IM.elems . facetRidges h) (IM.elems tetrahedra)
+  -- pPrint $ map (map (map fst . ridgeToPolygon)) ridges
+
+  -- h <- convexHull truncatedTetrahedron False False Nothing
+  -- putStrLn "same vertices:"
+  -- pPrint $ roundedVertices 3 truncatedTetrahedron == roundedVertices 3 (verticesCoordinates h)
+  -- putStrLn "edges:"
+  -- pPrint $ edgesIds' h
+  -- putStrLn "facets:"
+  -- let facets = IM.elems (_hfacets h)
+  -- let polygons = map (map fst . facetToPolygon') facets
+  -- pPrint polygons
+
+  -- h <- convexHull duoprism1616 False False Nothing
+  -- pPrint $ hullSummary h
+  -- putStrLn "edges:"
+  -- pPrint $ edgesIds' h
+
+  -- h <- convexHull thex False False Nothing
+  -- pPrint $ hullSummary h
+  -- putStrLn "vertices:"
+  -- pPrint $ roundedVertices 3 $ verticesCoordinates h
+  -- putStrLn "edges:"
+  -- pPrint $ edgesIds' h
+  -- putStrLn "\nOCTAHEDRAL FACETS:"
+  -- let octahedra = IM.filter (\f -> length (verticesIds f) == 6) (_hfacets h)
+  -- pPrint $ IM.elems $ IM.map verticesIds octahedra
+  -- pPrint octahedra
+  -- putStrLn "ridges:"
+  -- let ridges = map (IM.elems . facetRidges h) (IM.elems octahedra)
+  -- pPrint $ map (map (map fst . ridgeToPolygon)) ridges
+
+--  h <- convexHull cuboctahedron4d False False Nothing
+--  pPrint $ hullSummary h
+--  putStrLn "vertices:"
+---  pPrint $ verticesCoordinates h
+--  putStrLn "facets:"
+--  let facets = IM.elems (_hfacets h)
+--  let polygons = map (map fst . facetToPolygon') facets
+--  pPrint polygons
+--  putStrLn "edges:"
+--  pPrint $ edgesIds' h
+---  putStrLn "ridges:"
+--  let ridges = map (IM.elems . facetRidges h) facets
+--  pPrint $ map (map (map fst . ridgeToPolygon)) ridges
+
+  -- h <- convexHull daVinci False False Nothing
+  -- pPrint $ hullSummary h
+  -- code <- convexHull3DrglCode daVinci False (Just "rgl/daVinci.R")
+  -- putStrLn "done"
+
+  -- h <- convexHull twocircles False False Nothing
+  -- pPrint $ hullSummary h
+  -- code <- convexHull3DrglCode twocircles False (Just "rgl/oloid.R")
+  -- putStrLn "done"
+
+  -- h <- convexHull vs120omnitrunc False False Nothing
+  -- pPrint $ hullSummary h
+  -- putStrLn "vertices:"
+  -- pPrint $ verticesCoordinates h
+  -- putStrLn "facets:"
+  -- let facets = IM.elems (_hfacets h)
+  -- let polygons = map (map fst . facetToPolygon') facets
+  -- pPrint polygons
+  -- putStrLn "edges:"
+  -- pPrint $ edgesIds' h
+  -- putStrLn "ridges:"
+  -- let ridges = map (IM.elems . facetRidges h) facets
+  -- pPrint $ map (map (map fst . ridgeToPolygon)) ridges
+
+--  let curve3D = map (\x -> [ sin (pi*x) * cos (2*pi*x)
+--                         ,  sin (pi*x) * sin (2*pi*x)
+--                          ,  cos (pi*x)]) [i/200 | i <- [0 .. 200]]
+-- h <- convexHull curve3D True False Nothing
+--  hullToSTL h "strangeHull.stl"
+
+
+--  h <- convexHull snubDodecahedron True False Nothing
+--  hullToSTL h "MYTEST.stl"
+--  pPrint $ hullSummary h
+--  putStrLn "facets:"
+--  let facets = IM.elems (_hfacets h)
+--  let normals = map _normal facets
+--  let facetNormals = map (\n -> "facet normal  " ++ stringify " " n ++ "\nouter loop")
+--                     normals
+--  pPrint facetNormals
+--  putStrLn "facets normals done"
+--  putStrLn "facets polygons:"
+--  let polygons = map (\f -> "\nvertex " ++ (stringify "\nvertex " . map snd . facetToPolygon') f) facets
+--  pPrint polygons
+  --let polygons = (map (map stringify . snd) . facetToPolygon') facets
+ -- let polygons = map (\f -> stringify ("vertex" ++ ((show . snd) $ facetToPolygon' f))) facets ++ "\nendloop\nendfacet"
+--  let vertices = map (\v -> v ++ "\nendloop\nendfacet") polygons
+--  let vertices' = map (\v -> subRegex (mkRegex "\\]") (subRegex (mkRegex "\\[") v "") "") vertices
+--  putStrLn "vertices:"
+--  pPrint vertices'
+--  putStrLn "THE CONCATENATION:"
+--  pPrint $ subRegex (mkRegex ",") (concat [x ++ y | x <- facetNormals, y <- vertices']) " "
+--  putStrLn "concatenation 0:"
+--  pPrint $ stringify " " $ concat [unlines [x,y] | x <- facetNormals, y <- vertices']
+--  putStrLn "concatenation 1:"
+--  pPrint $ [unlines [x,y] | x <- facetNormals, y <- vertices']
+--  putStrLn "concatenation 2:"
+--  pPrint $ [stringify " " [x,y] | x <- facetNormals, y <- vertices']
+  -- putStrLn "vertices:"
+--  pPrint $ verticesCoordinates h
+--  let polygons = map (map fst . facetToPolygon') facets
+--  pPrint polygons
+--  putStrLn "edges:"
+--  pPrint $ edgesIds' h
+
+--  h <- convexHull biTruncatedTesseract False False Nothing
+--  pPrint $ hullSummary h
+--  putStrLn "vertices:"
+--  pPrint $ verticesCoordinates h
+--  putStrLn "facets:"
+--  let facets = IM.elems (_hfacets h)
+--  let polygons = map (map fst . facetToPolygon') facets
+--  pPrint polygons
+--  putStrLn "edges:"
+--  pPrint $ edgesIds' h
+
+  -- let cube10 = ncube 10
+  -- putStrLn "cube10:"
+  -- let thecube = map swap $ indexed cube10
+  -- pPrint thecube
+  -- chull <- convexHull cube10 False False Nothing
+  -- putStrLn "done"
+  -- pPrint $ hullSummary chull
+  -- pPrint $ verticesCoordinates chull
+  -- putStrLn "facets:"
+  -- let facets = IM.elems (_hfacets chull)
+  -- let polygons = map (map fst . facetToPolygon') facets
+  -- pPrint polygons
+  -- putStrLn "edges:"
+  -- pPrint $ edgesIds' chull
+
+  -- h <- convexHull allVertices False False Nothing
+  -- pPrint $ hullSummary h
+  -- putStrLn "vertices:"
+  -- pPrint $ verticesCoordinates h
+  -- putStrLn "facets:"
+  -- let facets = IM.elems (_hfacets h)
+  -- let polygons = map (map fst . facetToPolygon') facets
+  -- pPrint polygons
+  -- putStrLn "edges:"
+  -- pPrint $ edgesIds' h
+  -- -- putStrLn "tetrahedral facets:"
+  -- -- pPrint $ IM.elems $ IM.map verticesIds $ IM.filter (\f -> length (verticesIds f) == 4) (_hfacets h)
+
+  -- -- sphere1'
+  -- points <- randomOnSphere 100 1
+  -- h <- convexHull points True False Nothing
+  -- pPrint $ hullSummary h
+  -- putStrLn "vertices sphere 1:"
+  -- pPrint $ verticesCoordinates h
+  -- putStrLn "facets sphere 1:"
+  -- let facets = IM.elems (_hfacets h)
+  -- let polygons = map (map fst . facetToPolygon') facets
+  -- pPrint polygons
+
+  -- h <- convexHull allVertices False False Nothing
+  -- pPrint $ hullSummary h
+
+  -- h <- convexHull reuleuxTetrahedron True False Nothing
+  -- let facets = IM.elems (_hfacets h)
+  -- putStrLn "facets:"
+  -- pPrint facets
+  -- putStrLn "facets' (oriented):"
+  -- let polygons = map (map fst . facetToPolygon') facets
+  -- pPrint polygons
+
+  -- h <- convexHull qcube2 True False Nothing
+  -- putStrLn "same vertices:"
+  -- pPrint $ qcube2 == verticesCoordinates h
+  -- putStrLn "facets:"
+  -- let facets = IM.elems (_hfacets h)
+  -- let polygons = map (map fst . facetToPolygon') facets
+  -- pPrint polygons
+  --
+-- -- sphere1'
+--   let (vs1, fs1) = fixIndices (fst sphere1) (snd sphere1)
+--   h <- convexHull (vs1) True False Nothing
+--   putStrLn "vertices 1:"
+--   pPrint vs1
+--   putStrLn "facets:"
+--   let facets = IM.elems (_hfacets h)
+--   let polygons = map (map fst . facetToPolygon') facets
+--   pPrint polygons
+--
+-- --  sphere2' :: IO ([[Double]], [[Int]])
+--   let (vs2,fs2) =  fixIndices (fst sphere2) (snd sphere2)
+--   h <- convexHull (vs2) True False Nothing
+--   putStrLn "vertices 2:"
+--   pPrint vs2
+--   putStrLn "facets:"
+--   let facets = IM.elems (_hfacets h)
+--   let polygons = map (map fst . facetToPolygon') facets
+--   pPrint polygons
+--
+-- --  sphere3' :: IO ([[Double]], [[Int]])
+--   let (vs3, fs3) = fixIndices (fst sphere3) (snd sphere3)
+--   h <- convexHull (vs3) True False Nothing
+--   putStrLn "vertices 3:"
+--   pPrint vs3
+--   putStrLn "facets:"
+--   let facets = IM.elems (_hfacets h)
+--   let polygons = map (map fst . facetToPolygon') facets
+--   pPrint polygons
+--
+-- --  sphere4' :: IO ([[Double]], [[Int]])
+--   let (vs4, fs4) = fixIndices (fst sphere4) (snd sphere4)
+--   h <- convexHull (vs4) True False Nothing
+--   putStrLn "vertices 4:"
+--   pPrint vs4
+--   putStrLn "facets:"
+--   let facets = IM.elems (_hfacets h)
+--   let polygons = map (map fst . facetToPolygon') facets
+--   pPrint polygons
+
+  -- putStrLn "sphere1'"
+  -- pPrint sphere1'
+  -- let facets = IM.elems (_hfacets h)
+  --     polygons = map (map fst . facetToPolygon') facets
+  -- pPrint polygons
+  -- putStrLn "shpere2'"
+  -- pPrint sphere2'
+  -- let facets = IM.elems (_hfacets h)
+  -- let polygons = map (map fst . facetToPolygon') facets
+  -- pPrint polygons
+  -- putStrLn "shpere3'"
+  -- pPrint sphere3'
+  -- let facets = IM.elems (_hfacets h)
+  -- let polygons = map (map fst . facetToPolygon') facets
+  -- pPrint polygons
+  -- putStrLn "shpere4'"
+  -- pPrint sphere4'
+  -- let facets = IM.elems (_hfacets h)
+  -- let polygons = map (map fst . facetToPolygon') facets
+  -- pPrint polygons
+
+
+--
+--   h <- convexHull (dodecaplex) False False Nothing
+--   putStrLn $ hullSummary h
+--   putStrLn "edges:"
+--   pPrint $ edgesIds' h
+--   putStrLn "all vertices:"
+--   pPrint $ verticesCoordinates h
+--   putStrLn "ridges:"
+--   let facets = IM.elems (_hfacets h)
+--   let ridges = map (IM.elems . facetRidges h) facets
+--   pPrint $ map (map (map fst . ridgeToPolygon)) ridges
+
+  -- h <- convexHull qcube1 True False Nothing
+  -- putStrLn "same vertices:"
+  -- pPrint $ qcube1 == verticesCoordinates h
+  -- putStrLn "facets cube1:"
+  -- let facets = IM.elems (_hfacets h)
+  -- let polygons = map (map fst . facetToPolygon') facets
+  -- pPrint polygons
+  --
+  -- h <- convexHull qcube2 True False Nothing
+  -- putStrLn "same vertices:"
+  -- pPrint $ qcube2 == verticesCoordinates h
+  -- putStrLn "facets cube2:"
+  -- let facets = IM.elems (_hfacets h)
+  -- let polygons = map (map fst . facetToPolygon') facets
+  -- pPrint polygons
+  --
+  -- h <- convexHull qcube3 True False Nothing
+  -- putStrLn "same vertices:"
+  -- pPrint $ qcube3 == verticesCoordinates h
+  -- putStrLn "facets cube3:"
+  -- let facets = IM.elems (_hfacets h)
+  -- let polygons = map (map fst . facetToPolygon') facets
+  -- pPrint polygons
+  --
+  -- h <- convexHull qcube4 True False Nothing
+  -- putStrLn "same vertices:"
+  -- pPrint $ qcube4 == verticesCoordinates h
+  -- putStrLn "facets cube4:"
+  -- let facets = IM.elems (_hfacets h)
+  -- let polygons = map (map fst . facetToPolygon') facets
+  -- pPrint polygons
+  --
+  -- h <- convexHull qcube5 True False Nothing
+  -- putStrLn "same vertices:"
+  -- pPrint $ qcube5 == verticesCoordinates h
+  -- putStrLn "facets cube5:"
+  -- let facets = IM.elems (_hfacets h)
+  -- let polygons = map (map fst . facetToPolygon') facets
+  -- pPrint polygons
+
+  -- h <- convexHull regularTetrahedron False False Nothing
+  -- putStrLn $ hullSummary h
+  -- putStrLn "all vertices:"
+  -- pPrint $ verticesCoordinates h
+  -- putStrLn "facets:"
+  -- let facets = IM.elems (_hfacets h)
+  -- let polygons = map (map fst . facetToPolygon') facets
+  -- pPrint polygons
+  -- code <- convexHull3DrglCode regularTetrahedron False (Just "rgl/regularTetrahedron.R")
+  -- putStrLn "done"
+
+  -- let points = regularSphere 30
+  -- h <- convexHull points True False Nothing
+  -- putStrLn $ hullSummary h
+  -- putStrLn "all vertices:"
+  -- pPrint $ verticesCoordinates h
+  -- putStrLn "facets:"
+  -- let facets = IM.elems (_hfacets h)
+  -- let polygons = map (map fst . facetToPolygon') facets
+  -- pPrint polygons
+  -- writeFile "Data/sphere.txt" (show (verticesCoordinates h, polygons))
+
+  -- h <- convexHull octaplex False False Nothing
+  -- putStrLn $ hullSummary h
+  -- putStrLn "edges:"
+  -- pPrint $ edgesIds' h
+  -- putStrLn "all vertices:"
+  -- pPrint $ verticesCoordinates h
+  -- putStrLn "ridges:"
+  -- let facets = IM.elems (_hfacets h)
+  -- let ridges = map (IM.elems . facetRidges h) facets
+  -- pPrint $ map (map (map fst . ridgeToPolygon)) ridges
+
+  -- h <- convexHull icosahedron False False Nothing
+  -- putStrLn $ hullSummary h
+  -- putStrLn "all vertices:"
+  -- pPrint $ verticesCoordinates h
+  -- putStrLn "facets:"
+  -- let facets = IM.elems (_hfacets h)
+  -- let polygons = map (map fst . facetToPolygon') facets
+  -- pPrint polygons
+
+  -- let cube = [[-1,-1,-1],
+  --             [-1,-1, 1],
+  --             [-1, 1,-1],
+  --             [-1, 1, 1],
+  --             [ 1,-1,-1],
+  --             [ 1,-1, 1],
+  --             [ 1, 1,-1],
+  --             [ 1, 1, 1]]
+  -- h <- convexHull cube False False Nothing
+  -- putStrLn $ hullSummary h
+  -- putStrLn "all vertices:"
+  -- pPrint $ verticesCoordinates h
+  -- putStrLn "same vertices:"
+  -- print $ cube == verticesCoordinates h
+  -- putStrLn "facets:"
+  -- let facets = IM.elems (_hfacets h)
+  -- let polygons = map (map fst . facetToPolygon') facets
+  -- pPrint polygons
+
+  -- points <- randomOnSphere 50 1
+  -- h <- convexHull points True False Nothing
+  -- putStrLn $ hullSummary h
+  -- putStrLn "all vertices:"
+  -- pPrint $ verticesCoordinates h
+  -- putStrLn "facets:"
+  -- let facets = IM.elems (_hfacets h)
+  -- let polygons = map (map fst . facetToPolygon') facets
+  -- pPrint polygons
+
+    -- h <- convexHull duocylinder False False Nothing
+    -- putStrLn $ hullSummary h
+    -- putStrLn "\nedges:"
+    -- pPrint $ edgesIds' h
+    -- putStrLn "\nall vertices:"
+    -- pPrint $ verticesCoordinates h
+    -- putStrLn "\nridges:"
+    -- let facets = IM.elems (_hfacets h)
+    -- let ridges = map (IM.elems . facetRidges h) facets
+    -- pPrint $ map (map (map fst . ridgeToPolygon)) ridges
+
+  -- h <- convexHull hexaSquare False False Nothing
+  -- putStrLn $ hullSummary h
+  -- putStrLn "edges:"
+  -- pPrint $ edgesIds' h
+  -- putStrLn "all vertices:"
+  -- pPrint $ verticesCoordinates h
+
+  -- h <- convexHull snub24cell False False Nothing
+  -- putStrLn $ hullSummary h
+  -- putStrLn "edges:"
+  -- pPrint $ edgesIds' h
+  -- putStrLn "same vertices:"
+  -- print $ verticesCoordinates h == snub24cell
+  -- putStrLn "\nALL ROUNDED VERTICES:"
+  -- pPrint $ roundedVertices 2 $ verticesCoordinates h
+  -- putStrLn "\nTETRAHEDRAL FACETS:"
+  -- pPrint $ IM.elems $ IM.map verticesIds $ IM.filter (\f -> length (verticesIds f) == 4) (_hfacets h)
+
+  -- h <- convexHull triangularDuoprism False False Nothing
+  -- putStrLn $ hullSummary h
+  -- putStrLn "edges:"
+  -- pPrint $ edgesIds' h
+  -- putStrLn "all vertices:"
+  -- pPrint $ verticesCoordinates h
+  -- putStrLn "same vertices:"
+  -- print $ verticesCoordinates h == triangularDuoprism
+  -- putStrLn "one prism:"
+  -- let facet = head $ IM.elems (_hfacets h)
+  -- let ridges = IM.elems $ facetRidges h facet
+  -- pPrint $ map (map fst . ridgeToPolygon) ridges
+  -- putStrLn "edges of this facet:"
+  -- pPrint $ edgesIds' facet
+
+  -- h <- convexHull duoprism35 False False Nothing
+  -- putStrLn $ hullSummary h
+  -- putStrLn "edges:"
+  -- pPrint $ edgesIds' h
+  -- putStrLn "same vertices:"
+  -- print $ verticesCoordinates h == duoprism34
+  -- putStrLn "\nALL ROUNDED VERTICES:"
+  -- pPrint $ roundedVertices 3 $ verticesCoordinates h
+  -- putStrLn "\nONE PENTAGONAL PRISM:"
+  -- let prisms = IM.filter (\f -> length (verticesIds f) == 10) (_hfacets h)
+  --     onePrism = head $ IM.elems prisms
+  -- putStrLn "\nRIDGES OF THIS PRISM:"
+  -- let ridges = IM.elems $ facetRidges h onePrism
+  -- pPrint $ map (map fst . ridgeToPolygon) ridges
+
+  -- h <- convexHull duoprism34 False False Nothing
+  -- putStrLn $ hullSummary h
+  -- putStrLn "edges:"
+  -- pPrint $ edgesIds' h
+  -- putStrLn "same vertices:"
+  -- print $ verticesCoordinates h == duoprism34
+  -- putStrLn "\nALL ROUNDED VERTICES:"
+  -- pPrint $ roundedVertices 3 $ verticesCoordinates h
+  -- putStrLn "\nONE SQUARE PRISM:"
+  -- let squarePrisms = IM.filter (\f -> length (verticesIds f) == 8) (_hfacets h)
+  --     oneSquarePrism = head $ IM.elems squarePrisms
+  -- putStrLn "\nRIDGES OF THIS SQUARE PRISM:"
+  -- let ridges = IM.elems $ facetRidges h oneSquarePrism
+  -- pPrint $ map (map fst . ridgeToPolygon) ridges
+  -- putStrLn "\nONE TRIANGULAR PRISM:"
+  -- let triPrisms = IM.filter (\f -> length (verticesIds f) == 6) (_hfacets h)
+  --     oneTriPrism = head $ IM.elems triPrisms
+  -- putStrLn "\nRIDGES OF THIS TRIANGULAR PRISM:"
+  -- let ridges' = IM.elems $ facetRidges h oneTriPrism
+  -- pPrint $ map (map fst . ridgeToPolygon) ridges'
+
+  -- h <- convexHull hexagonalDuoprism False False Nothing
+  -- putStrLn $ hullSummary h
+  -- putStrLn "edges:"
+  -- pPrint $ edgesIds' h
+  -- putStrLn "all vertices:"
+  -- pPrint $ verticesCoordinates h
+  -- putStrLn "same vertices:"
+  -- print $ verticesCoordinates h == hexagonalDuoprism
+  -- putStrLn "one hexagonal prism:"
+  -- let facet = head $ IM.elems (_hfacets h)
+  -- let ridges = IM.elems $ facetRidges h facet
+  -- pPrint $ map (map fst . ridgeToPolygon) ridges
+  -- putStrLn "edges of this facet:"
+  -- pPrint $ edgesIds' facet
+
+  -- h <- convexHull cantellatedTesseract False False Nothing
+  -- putStrLn $ hullSummary h
+  -- putStrLn "edges:"
+  -- pPrint $ edgesIds' h
+  -- putStrLn "all vertices:"
+  -- pPrint $ verticesCoordinates h
+  -- putStrLn "good ridges:"
+  -- let goodfacets = IM.elems $ IM.filter (\f -> nEdges f `elem` [9,12]) (_hfacets h)
+  -- let ridges = nubBy ((==) `on` verticesIds) $ concatMap (IM.elems . facetRidges h) goodfacets
+  -- pPrint $ map (map fst . ridgeToPolygon) ridges
+
+  -- h <- convexHull rectifiedTesseract False False Nothing
+  -- putStrLn $ hullSummary h
+  -- putStrLn "edges:"
+  -- pPrint $ edgesIds' h
+  -- putStrLn "all vertices:"
+  -- pPrint $ verticesCoordinates h
+  -- putStrLn "facets:"
+  -- pPrint $ IM.map verticesIds (_hfacets h)
+  -- putStrLn "tetrahedral facets:"
+  -- pPrint $ IM.elems $ IM.map verticesIds $ IM.filter (\f -> length (verticesIds f) == 4) (_hfacets h)
+  -- putStrLn "ridges of facet 0:"
+  -- let facet = _hfacets h IM.! 0
+  --     ridges = facetRidges h facet
+  -- pPrint $ map (map fst . ridgeToPolygon) (IM.elems ridges)
+
+  -- h <- convexHull truncatedTesseract False False Nothing
+  -- putStrLn "HULL SUMMARY:"
+  -- putStrLn $ hullSummary h
+  -- putStrLn "\nEDGES:"
+  -- pPrint $ edgesIds' h
+  -- -- putStrLn "original vertices:"
+  -- -- pPrint $ take 2 truncatedTesseract
+  -- -- putStrLn "vertices:"
+  -- -- pPrint $ take 2 $ verticesCoordinates h
+  -- putStrLn "\nALL VERTICES:"
+  -- pPrint $ verticesCoordinates h
+  -- putStrLn "\nALL ROUNDED VERTICES:"
+  -- pPrint $ roundedVertices 2 $ verticesCoordinates h
+  -- putStrLn "\nALL ROUNDED ORIGINAL VERTICES:"
+  -- pPrint $ roundedVertices 2 truncatedTesseract
+  -- -- putStrLn "facets:"
+  -- -- pPrint $ IM.map verticesIds (_hfacets h)
+  -- putStrLn "\nTETRAHEDRAL FACETS:"
+  -- pPrint $ IM.elems $ IM.map verticesIds $ IM.filter (\f -> length (verticesIds f) == 4) (_hfacets h)
+  -- -- putStrLn "ridges of facet 9:" -- pkoi ? parce que je voulais illuster une facette je pense...
+  -- -- let facet = _hfacets h IM.! 9
+  -- --     ridges = facetRidges h facet
+  -- -- pPrint $ map (map fst . ridgeToPolygon) (IM.elems ridges)
+  -- -- putStrLn "vertices of facet 9:"
+  -- -- pPrint $ _vertices facet
+
+  -- h <- convexHull icosahedron False False Nothing
+  -- pPrint $ IM.map facetToPolygon (_hfacets h)
+  -- putStrLn $ hullSummary h
+  -- pPrint $ IM.elems $ IM.map (IM.elems . _vertices) (_hfacets h)
+
+  -- -- code <- convexHull3DrglCode irregularPolyhedron True (Just "rgl/irregularPolyhedron.R")
+  -- h <- convexHull truncatedCuboctahedron False False Nothing
+  -- putStrLn $ hullSummary h
+  -- pPrint $ _vertices h
+  -- pPrint $ IM.elems $ IM.map facetToPolygon' (_hfacets h)
+  -- pPrint $ edgesIds' h
+  -- pPrint $ IM.elems $ IM.map _normal (_hfacets h)
+
+  -- code <- convexHull3DrglCode mobiusStrip True (Just "rgl/mobiusHull02.R")
+  -- h <- convexHull mobiusStrip False False Nothing
+  -- putStrLn $ hullSummary h
+
+  -- code <- convexHull3DrglCode truncatedCuboctahedron False (Just "rgl/truncatedCuboctahedron.R")
+  -- putStrLn "done"
+
+  -- h <- convexHull truncatedCuboctahedron False False Nothing
+  -- pPrint $ IM.map toVertex3 (_vertices h)
+  -- pPrint $ IM.elems $ IM.map facetToPolygon' (_hfacets h)
+  -- pPrint $ edgesIds' h
+
+  -- h <- convexHull truncatedCuboctahedron False False Nothing
+  -- pPrint $ IM.elems $ IM.map facetToPolygon (_hfacets h)
+  -- putStrLn $ hullSummary h
+
+  -- code <- convexHull3DrglCode spheresPack True (Just "rgl/convexhull_spheresPack.R")
+  -- putStrLn "done"
+
+  -- h <- convexHull truncatedTesseract False False Nothing
+  -- putStrLn $ hullSummary h
+
+  -- h <- convexHull nonConvexPolyhedron False False Nothing
+  -- putStrLn $ hullSummary h
+  -- --code <- convexHull3DrglCode nonConvexPolyhedron True (Just "rgl/convexhull_nonConvexPolyhedron.R")
+  -- putStrLn "done"
+
+  -- h <- convexHull truncatedTesseract False False Nothing
+  -- putStrLn $ hullSummary h
+  -- let edges = H.keys (_edges h)
+  -- let f (Pair i j) = printf "coolsegment3d(rbind(x[%d,],x[%d,]))\n" (i+1) (j+1)
+  -- let code = map f edges
+  -- pPrint $ verticesCoordinates h
+  -- putStrLn $ concat code
+
+  -- points <- randomInCube 100
+  -- hull <- convexHull points False False Nothing
+  -- pPrint $ _hfacets hull
+  -- pPrint $ _hedges hull
+  -- pPrint $ _hvertices hull
+
+  -- code <- convexHull3DrglCode teapot True (Just "rgl/convexhull_teapot.R")
+  -- putStrLn "done"
+
+  -- let curve3D = map (\x -> [ sin (pi*x) * cos (2*pi*x)
+  --                         ,  sin (pi*x) * sin (2*pi*x)
+  --                         ,  cos (pi*x)]) [i/200 | i <- [0 .. 200]]
+  -- code <- convexHull3DrglCode (nub $ curve3D ++ map (\[x,y,z] -> [x,y,z+2]) curve3D) True
+  --                             (Just "rgl/convexhull_curveOnSphere3.R")
+  -- putStrLn "done"
+
+  -- let curve3D = map (\x -> [ sin (pi*x) * cos (2*pi*x)
+  --                         ,  sin (pi*x) * sin (2*pi*x)
+  --                         ,  cos (pi*x)]) [i/200 | i <- [0 .. 200]]
+  -- code <- convexHull3DrglCode curve3D True (Just "rgl/convexhull_curveOnSphere.R")
+  -- putStrLn "done"
+
+  -- let c = 4
+  --     a = 1
+  -- let curve3D = map (\x -> [ cos (2*pi*x) * (c + a * cos (2*pi*x))
+  --                         ,  sin (2*pi*x) * (c + a * cos (2*pi*x))
+  --                         ,  a * sin (2*pi*x)]) [i/50 | i <- [0 .. 50]]
+  -- code <- convexHull3DrglCode curve3D True (Just "rgl/convexhull_curveOnTorus.R")
+  -- putStrLn "done"
+
+  -- points <- randomInCube 1000
+  -- code <- convexHull3DrglCode (map (map (approx 4)) points) True (Just "rgl/convexhull04.R")
+  -- putStrLn "done"
+
+  -- let square3D = [[-1,-1, 0]
+  --                ,[-1,-1, 0]
+  --                ,[-1, 1, 0]
+  --                ,[-1, 1, 0]]
+  -- chull <- convexHull square3D False
+  -- pPrint chull
+
+  -- let squareLattice = [[0,0],[0,1],[0,2],[1,0],[1,1],[1,2],[2,0],[2,1],[2,2]]
+  -- chull <- convexHull squareLattice False False Nothing
+  -- putStrLn "\n--- SQUARE LATTICE ---"
+  -- pPrint chull
+  --
+  -- let cube = [[-1,-1,-1]
+  --            ,[-1,-1, 1]
+  --            ,[-1, 1,-1]
+  --            ,[-1, 1, 1]
+  --            ,[ 1,-1,-1]
+  --            ,[ 1,-1, 1]
+  --            ,[ 1, 1,-1]
+  --            ,[ 1, 1, 1]]
+  -- chull2 <- convexHull cube False
+  -- putStrLn "\n--- CUBE ---"
+  -- pPrint chull2
+
+  -- chull <- convexHull cube4 True False Nothing
+  -- pPrint chull
+
+  -- chull <- convexHull cube5 False False Nothing
+  -- putStrLn "done"
+  -- pPrint chull
+  -- pPrint $ length $ xxx chull
+  -- pPrint $ length $ nub $ xxx chull
+  -- pPrint $ S.size (_alledges chull)
+
+  -- let square = [[0,0],[0,1],[1,0],[1,1]]
+  -- chull <- convexHull square False
+  -- pPrint chull
diff --git a/src-exe/Delaunay/Main.hs b/src-exe/Delaunay/Main.hs
new file mode 100644
--- /dev/null
+++ b/src-exe/Delaunay/Main.hs
@@ -0,0 +1,65 @@
+module Main
+  where
+import           Delaunay.Examples
+import qualified Data.IntMap.Strict  as IM
+import           Delaunay
+import           Delaunay.R
+import           System.IO
+import           Text.Show.Pretty
+import           Data.HashMap.Strict.InsOrd as H hiding (map)
+
+tesseractVertices :: [[Double]]
+tesseractVertices =
+  map (map (/2))
+      [ [-1,-1,-1,-1],
+        [-1,-1,-1, 1],
+        [-1,-1, 1,-1],
+        [-1,-1, 1, 1],
+        [-1, 1,-1,-1],
+        [-1, 1,-1, 1],
+        [-1, 1, 1,-1],
+        [-1, 1, 1, 1],
+        [ 1,-1,-1,-1],
+        [ 1,-1,-1, 1],
+        [ 1,-1, 1,-1],
+        [ 1,-1, 1, 1],
+        [ 1, 1,-1,-1],
+        [ 1, 1,-1, 1],
+        [ 1, 1, 1,-1],
+        [ 1, 1, 1, 1]
+      ]
+
+main :: IO ()
+main = do
+
+  dtesseract <- delaunay tesseractVertices True False Nothing
+  let vertices = IM.elems $ _vertices dtesseract
+  let edges = Prelude.map fromPair $ H.keys $ _edges dtesseract
+  putStrLn "VERTICES:"
+  pPrint vertices
+  putStrLn "\nEDGES:"
+  pPrint edges
+    where
+      fromPair (Pair i j) = (i,j)
+
+  -- tess <- delaunay nonConvexPolyhedron False False Nothing
+  -- let code = delaunaySpheres tess
+  -- writeFile "rgl/delaunay_spheres_nonConvexPolyhedron.R" code
+
+  --  x <- [0,0,0] : randomOnSphere 100 3
+  -- tess <- delaunay x False False Nothing
+  -- let code = delaunay3rgl tess True False True True Nothing
+  -- writeFile "rgl/delaunay_sphere_interior.R" code
+
+  -- tess <- delaunay duoCylinder False False Nothing
+  -- let edges = H.elems $ _edges tess
+  -- let vertices = IM.elems $ _vertices tess
+  -- let edgesKeys = Prelude.map fromPair $ H.keys $ _edges tess
+  -- putStrLn "VERTICES:"
+  -- pPrint vertices
+  -- putStrLn "\nEDGES:"
+  -- pPrint edges
+  -- putStrLn "\nEDGES KEYS:"
+  -- pPrint edgesKeys
+  --   where
+  --     fromPair (Pair i j) = (i,j)
diff --git a/src-exe/HalfSpaces/Main.hs b/src-exe/HalfSpaces/Main.hs
new file mode 100644
--- /dev/null
+++ b/src-exe/HalfSpaces/Main.hs
@@ -0,0 +1,9 @@
+module Main
+  where
+import           HalfSpaces
+import           Text.Show.Pretty
+
+main :: IO ()
+main = do
+  is <- hsintersections region3D False
+  pPrint is
diff --git a/src-exe/Voronoi/Main.hs b/src-exe/Voronoi/Main.hs
new file mode 100644
--- /dev/null
+++ b/src-exe/Voronoi/Main.hs
@@ -0,0 +1,249 @@
+module Main
+  where
+import           ConvexHull
+import           ConvexHull.R
+import qualified Data.IntMap.Strict as IM
+import qualified Data.IntSet        as IS
+import           Data.List
+import           Delaunay
+import           Delaunay.Examples
+import           Delaunay.R
+import           System.IO
+import           Text.Show.Pretty
+import           Voronoi.R
+import           Voronoi2D
+import           Voronoi3D
+-- import Data.Graph
+-- import Data.List.Index
+-- import Data.List
+
+-- connectedEdges :: Edge2 -> Edge2 -> Bool
+-- connectedEdges (Edge2 (x1,x2)) (Edge2 (y1,y2)) = length ([x1,x2] `intersect` [y1,y2]) == 1
+-- connectedEdges _ _ = False
+--
+-- edgeVertices :: Edge2 -> [[Double]]
+-- edgeVertices (Edge2 ((x1,x2),(y1,y2))) = [[x1,x2],[y1,y2]]
+--
+-- connectedVertices :: Cell2 -> [Double] -> [Double] -> Bool
+-- connectedVertices cell [x1,x2] [y1,y2] =
+--   (Edge2 ((x1,x2),(y1,y2)) `elem` cell) || (Edge2 ((y1,y2),(x1,x2)) `elem` cell)
+
+-- distance :: [Double] -> [Double] -> Double
+-- distance p1 p2 = sum (zipWith (\x y -> (subtract x y)^2) p1 p2)
+--
+-- checkTile :: Tile -> [Double]
+-- checkTile tile = map (distance center) vertices
+--   where
+--     simplex = _simplex tile
+--     center = _circumcenter simplex
+--     vertices = IM.elems (_points simplex)
+
+
+main :: IO ()
+main = do
+
+  tess <- delaunay waves2D False False Nothing
+  let v = voronoi2 tess
+      v' = restrictVoronoi2 v
+      code = voronoi2ForR v' Nothing
+  writeFile "Rplots/voronoi_waves2D.R" code
+
+--  tess <- delaunay ([0,0,0] : projectedHexagonalDuoprims) False True Nothing
+--  let v = voronoi3 tess
+--  summaryVoronoi3 v
+--  code <- voronoi3ForRgl' v Nothing Nothing
+--  writeFile "rgl/voronoi_hexagonalDuoprism.R" code
+
+  -- let x1 = let b=0 in
+  --           [[sin (a*2*pi/100) * cos b, sin (a*2*pi/100) * sin b, cos (a*2*pi/100)] | a <- [0 .. 99]]
+  --     x2 = let b=pi/2 in
+  --           [[sin (a*2*pi/100) * cos b, sin (a*2*pi/100) * sin b, cos (a*2*pi/100)] | a <- [0 .. 99]]
+  --     x3 = [[cos (a*2*pi/100), sin (a*2*pi/100), 0] | a <- [0 .. 99]]
+  -- tess <- delaunay (nub $ x1 ++ x2 ++ x3 ++ map (map (/5)) cube3) False False Nothing
+  -- putStrLn "done delaunay"
+  -- let v = voronoi3 tess
+  --     (_,cell) = last (restrictVoronoi3' v)
+  -- h <- convexHull (cell3Vertices cell) False False Nothing
+  -- putStrLn $ hullSummary h
+  -- code <- convexHull3DrglCode (cell3Vertices cell) True (Just "rgl/convexhull_voronoiCell.R")
+  -- putStrLn "done"
+
+  -- tess <- delaunay (truncatedCuboctahedron ++ [[0,0,0]]) False True Nothing
+  -- let v = voronoi3 tess
+  -- summaryVoronoi3 v
+  -- code <- voronoi3ForRgl' v Nothing Nothing
+  -- writeFile "rgl/voronoi_truncatedCuboctahedron01.R" code
+
+  -- tess <- delaunay spheresPack False True Nothing
+  -- let v = voronoi3 tess
+  -- prettyShowVoronoi3 v Nothing
+  -- summaryVoronoi3 v
+  -- code <- voronoi3ForRgl' v Nothing Nothing
+  -- writeFile "rgl/voronoi_spheresPacking01.R" code
+
+  -- tess <- delaunay projectedTruncatedTesseract False True Nothing
+  -- pPrint $ IM.filter (\tile -> _volume tile < 1e-16 && _volume tile > 0) (_tiles tess)
+  -- let v = voronoi3 tess
+  -- -- pPrint $ map (\(_,cell) -> length cell) (restrictVoronoi3 v)
+  -- summaryVoronoi3 (roundVoronoi3 10 (restrictVoronoi3 v))
+  -- code <- voronoi3ForRgl' v (Just 10) Nothing
+  -- writeFile "rgl/voronoi_truncatedTesseract02.R" code
+
+--   let x1 = let b=0 in
+--             [[sin (a*2*pi/100) * cos b, sin (a*2*pi/100) * sin b, cos (a*2*pi/100)] | a <- [0 .. 99]]
+--       x2 = let b=pi/2 in
+--             [[sin (a*2*pi/100) * cos b, sin (a*2*pi/100) * sin b, cos (a*2*pi/100)] | a <- [0 .. 99]]
+--       x3 = [[cos (a*2*pi/100), sin (a*2*pi/100), 0] | a <- [0 .. 99]]
+--   tess <- delaunay (nub $ x1 ++ x2 ++ x3 ++ map (map (/5)) cube3) False True
+-- --  let code = delaunay3rgl tess False True True (Just 0.5)
+--   pPrint $ IM.filter (isNaN . head) (IM.map (_circumcenter . _simplex) (_tiles tess))
+--   -- pPrint $ IM.filterWithKey (\k _ -> k `elem` [459, 460, 464]) (_tiles tess)
+--   -- pPrint $ IM.map checkTile (IM.filter (\tile -> _volume (_simplex tile) == 0) (_tiles tess))
+--   let v = voronoi3 tess
+--       v' = filter (\(_,cell) -> not (null cell)) v
+-- --      v' = restrictVoronoi3box' ((-2,2),(-2,2),(-2,2)) v
+--       v'' = clipVoronoi3 ((-1,1),(-1,1),(-1,1)) v'
+--   code <- voronoi3ForRgl' v'' Nothing
+--   writeFile "rgl/voronoi_twoCircles04.R" code
+--   putStrLn "done"
+
+--   let x1 = let b=0 in
+--             [[sin (a*2*pi/100) * cos b, sin (a*2*pi/100) * sin b, cos (a*2*pi/100)] | a <- [0 .. 99]]
+--       x2 = let b=pi/2 in
+--             [[sin (a*2*pi/100) * cos b, sin (a*2*pi/100) * sin b, cos (a*2*pi/100)] | a <- [0 .. 99]]
+--   tess <- delaunay (nub $ x1 ++ x2 ++ map (map (/5)) cube3) False True
+--   let v = voronoi3 tess
+--       vv = [last v]
+--       v' = filter (\(_,cell) -> not (null cell)) v
+--       v'' = clipVoronoi3 (-1,1,-1,1,-1,1) v'
+-- --  prettyShowVoronoi3 v' (Just 10)
+--   code <- voronoi3ForRgl' v'' Nothing
+--   writeFile "rgl/voronoi_twoCircles02.R" code
+--   putStrLn "done"
+
+  -- x1 <- randomOnSphere 500 4
+  -- x2 <- randomOnSphere 500 2
+  -- let points = x1 ++ x2 ++ [[1,0,0],[-1,0,0]]
+  -- tess <- delaunay points False False
+  -- let v = voronoi3 tess
+  --     vv = [last v, last (init v)]
+  -- code <- voronoi3ForRgl' vv Nothing
+  -- writeFile "rgl/voronoi_sphere01.R" code
+
+--   let x1 = map (map (*2)) cube3
+--   let x2 = map (map (*(1/2))) cube3
+--   tess <- delaunay (x1 ++ x2) False True
+--   pPrint $ _tiles tess
+--   let ridgeof = filter (\fo -> IS.size fo == 1) (map _facetOf (IM.elems (_tilefacets tess)))
+--   print $ length ridgeof
+--   print $ IM.size (_tilefacets tess)
+--   let ridgevertices = map (IM.keys . _points . _subsimplex) (IM.elems (_tilefacets tess))
+--   print $ length $ nub ridgevertices
+--   let tilevertices = map (IM.keys . _points . _simplex) (IM.elems (_tiles tess))
+--   pPrint $ length $ filter (== [3]) $ map (\verts -> filter (==3) $ map (\tv -> length (intersect tv verts)) tilevertices) ridgevertices
+--   let v = voronoi3 tess
+--   code <- voronoi3ForRgl' v Nothing
+-- --  let code = delaunay3rgl tess False True True (Just 0.5)
+--   writeFile "rgl/voronoi_projhcube4.R" code
+
+  -- x1 <- randomOnSphere 500 1
+  -- x2 <- randomOnSphere 500 0.5
+  -- let points = x1 ++ x2 ++ [[0,0,0]]
+  -- tess <- delaunay points False False
+  -- let v = voronoi3 tess
+  --     vv = [last v]
+  -- code <- voronoi3ForRgl' vv Nothing
+  -- writeFile "rgl/voronoi_sphere.R" code
+
+  -- let x = duplicate3 (duplicate3 (duplicate3 (cube3 ++ [[0,0,0]]) [2,0,0]) [0,2,0]) [0,0,2]
+  -- tess <- delaunay x False False
+  -- let v = voronoi3 tess
+  -- code <- voronoi3ForRgl' v Nothing
+  -- writeFile "rgl/voronoi_multicube.R" code
+
+  -- let x = dodecahedron ++ [[0,0,0]]
+  -- tess <- delaunay x False
+  -- let v = voronoi3 tess
+  -- code <- voronoi3ForRgl' v Nothing
+  -- writeFile "rgl/voronoi_centricDodecahedron.R" code
+  -- prettyShowVoronoi3 v (Just 3)
+
+  -- let c = 4
+  --     a = 1
+  -- let curve3D = map (\x -> [ cos (2*pi*x) * (c + a * cos (2*pi*x))
+  --                         ,  sin (2*pi*x) * (c + a * cos (2*pi*x))
+  --                         ,  a * sin (2*pi*x)]) [i/50 | i <- [0 .. 50]]
+  -- tess <- delaunay curve3D False
+  -- let v = voronoi3 tess
+  -- writeFile "rgl/voronoi_curve3Dtorus.R" (voronoi3ForRgl v (Just tess))
+
+  -- let curve3D = map (\x -> [ sin (pi*x) * cos (2*pi*x)
+  --                         ,  sin (pi*x) * sin (2*pi*x)
+  --                         ,  cos (pi*x)]) [i/50 | i <- [0 .. 50]]
+  -- tess <- delaunay curve3D False
+  -- let v = voronoi3 tess
+  -- writeFile "rgl/voronoi_curve3D.R" (voronoi3ForRgl v (Just tess))
+
+  -- x <- randomOnTorus 50 4 1
+  -- tess <- delaunay (x ++ [[4,0,0],[-4,0,0],[0,4,0],[0,-4,0]]) False
+  -- writeFile "rgl/delaunay_torus00.R" (delaunay3rgl tess True True True Nothing)
+  -- -- pPrint (_tiles tess)
+  -- -- pPrint (_tilefacets tess)
+  -- let v = voronoi3 tess
+  -- -- pPrint v
+  -- -- pPrint $ zip [0 .. 50] (map (map (facetCenters tess) . vertexNeighborFacets tess) [0 .. 50])
+  -- code <- voronoi3ForRgl' v Nothing
+  -- writeFile "rgl/voronoi_torus00.R" code
+
+  -- x <- randomInSquare 100
+  -- tess <- delaunay (x ++ [[-1,-1],[-1,2],[2,-1],[2,2]]) False
+  -- let v = voronoi2 tess
+  -- writeFile "Rplots/voronoi_randomInSquare00.R" (voronoi2ForR v Nothing)
+
+  -- let cube = [[i,j,k] | i <- [-1,2], j <- [-1,2], k <- [-1,2]]
+  -- x <- randomInCube 30
+  -- tess <- delaunay (cube ++ x) False
+  -- let v = voronoi3 tess
+  -- prettyShowVoronoi3 v (Just 3)
+  -- pPrint (map (_circumcenter . _simplex) (IM.elems (_tiles tess)))
+  -- code <- voronoi3ForRgl' v Nothing
+  -- writeFile "rgl/voronoi_randomInCube00.R" code
+
+  -- let cube = [[i,j,k] | i <- [0,1], j <- [0,1], k <- [0,1]]
+  -- x <- randomInCube 30
+  -- tess <- delaunay (cube ++ x) False
+  -- let v = voronoi3 tess
+  --     v' = restrictVoronoi3box (-5,5) (-5,5) (-5,5) v
+  -- prettyShowVoronoi3 v' (Just 3)
+  -- code <- voronoi3ForRgl' v' Nothing
+  -- writeFile "rgl/voronoi_randomInCube01.R" code
+
+  -- x <- randomInCircle 50
+  -- tess <- delaunay (x ++ [[0,0]]) False
+  -- let v = voronoi2 tess
+  -- writeFile "Rplots/voronoi_circle01.R" (voronoi2ForR v Nothing)
+
+  -- tess <- delaunay squareLattice False
+  -- let v = voronoi2 tess
+  -- putStrLn $ voronoi2ForR v (Just tess)
+
+  -- let x = rhombicDodecahedron ++ [[0,0,0]]
+  -- tess <- delaunay x False
+  -- let v = voronoi3 tess
+  --     code1 = voronoi3ForRgl v Nothing
+  --     (_, cell) = last v
+  -- code2 <- convexHull3DrglCode (cell3Vertices cell) False Nothing
+  -- writeFile "rgl/voronoi_centricRhombicDodecahedron.R" (code1 ++ code2)
+  -- pPrint v
+
+  -- tess <- delaunay centricCuboctahedron False
+  -- let v = voronoi3 tess
+  --     code1 = voronoi3ForRgl v Nothing
+  --     (_, cell) = last v
+  -- code2 <- convexHull3DrglCode (cell3Vertices cell) False Nothing
+  -- writeFile "rgl/voronoi_centricCuboctahedron.R" (code1 ++ code2)
+  -- pPrint v
+
+  -- x <- randomInSphere 1000
+  -- tess <- delaunay2 x False
+  -- print $ IM.size (_tiles tess)
diff --git a/src/ConvexHull.hs b/src/ConvexHull.hs
new file mode 100644
--- /dev/null
+++ b/src/ConvexHull.hs
@@ -0,0 +1,7 @@
+module ConvexHull
+  ( module X )
+  where
+import           ConvexHull.ConvexHull as X
+import           ConvexHull.Types      as X
+import           Qhull.Shared          as X
+import           Qhull.Types           as X
diff --git a/src/ConvexHull/BiTruncatedTesseract.hs b/src/ConvexHull/BiTruncatedTesseract.hs
new file mode 100644
--- /dev/null
+++ b/src/ConvexHull/BiTruncatedTesseract.hs
@@ -0,0 +1,23 @@
+module ConvexHull.BiTruncatedTesseract
+  where
+import Math.Combinat.Permutations as P
+import Data.List
+
+signsAll :: (Eq a, Num a) => [[a]] -> [[a]]
+signsAll = concatMap signs
+  where
+  signs :: (Eq a, Num a) => [a] -> [[a]]
+  signs = mapM (\x -> nub [x,-x])
+  
+
+vertices :: ([Double], Bool) -> [[Double]]
+vertices (coords, allperms) =
+  map (map (/ sqrt 18)) $ signsAll $
+  nub $ zipWith permuteList perms (replicate 24 coords)
+  where perms = filter (if allperms then const True else isEvenPermutation) (P.permutations 4)
+
+
+biTruncatedTesseract :: [[Double]]
+biTruncatedTesseract = concatMap vertices 
+  [ ([0, sqrt 2, 2 * sqrt 2, 2 * sqrt 2], True) ]
+
diff --git a/src/ConvexHull/CConvexHull.hs b/src/ConvexHull/CConvexHull.hs
new file mode 100644
--- /dev/null
+++ b/src/ConvexHull/CConvexHull.hs
@@ -0,0 +1,446 @@
+{-# LINE 1 "convexhull.hsc" #-}
+{-# LANGUAGE ForeignFunctionInterface #-}
+module ConvexHull.CConvexHull
+  ( cConvexHullToConvexHull
+  , c_convexhull )
+  where
+import           Control.Monad              ((<$!>), (=<<))
+import           ConvexHull.Types
+import qualified Data.HashMap.Strict.InsOrd as H
+import           Data.IntMap.Strict         (IntMap, fromAscList)
+import qualified Data.IntMap.Strict         as IM
+import qualified Data.IntSet                as IS
+import           Data.List
+import           Data.Tuple.Extra           (both)
+import           Foreign
+import           Foreign.C.String
+import           Foreign.C.Types
+import           Qhull.Types
+
+data CVertex = CVertex {
+    __id    :: CUInt
+  , __point :: Ptr CDouble
+}
+
+instance Storable CVertex where
+    sizeOf    __ = (16)
+{-# LINE 28 "convexhull.hsc" #-}
+    alignment __ = 8
+{-# LINE 29 "convexhull.hsc" #-}
+    peek ptr = do
+      id'     <- (\hsc_ptr -> peekByteOff hsc_ptr 0) ptr
+{-# LINE 31 "convexhull.hsc" #-}
+      point'  <- (\hsc_ptr -> peekByteOff hsc_ptr 8) ptr
+{-# LINE 32 "convexhull.hsc" #-}
+      return CVertex { __id = id'
+                     , __point = point' }
+    poke ptr (CVertex r1 r2)
+      = do
+          (\hsc_ptr -> pokeByteOff hsc_ptr 0) ptr r1
+{-# LINE 37 "convexhull.hsc" #-}
+          (\hsc_ptr -> pokeByteOff hsc_ptr 8) ptr r2
+{-# LINE 38 "convexhull.hsc" #-}
+
+--data Vertex = Vertex {
+--    _id :: Int
+--  , _point :: [Double]
+--} deriving Show
+
+cVerticesToMap :: Int -> [CVertex] -> IO (IntMap [Double])
+cVerticesToMap dim cvertices = do
+  let ids = map (fromIntegral . __id) cvertices
+  points <- mapM (\cv -> (<$!>) (map realToFrac) (peekArray dim (__point cv)))
+                 cvertices
+  return $ fromAscList (zip ids points)
+
+data CVertex' = CVertex' {
+    __id'            :: CUInt
+  , __point'         :: Ptr CDouble
+  , __neighfacets    :: Ptr CUInt
+  , __nneighfacets   :: CUInt
+  , __neighvertices  :: Ptr CUInt
+  , __nneighvertices :: CUInt
+  , __neighridges    :: Ptr CUInt
+  , __nneighridges   :: CUInt
+}
+
+instance Storable CVertex' where
+    sizeOf    __ = (64)
+{-# LINE 64 "convexhull.hsc" #-}
+    alignment __ = 8
+{-# LINE 65 "convexhull.hsc" #-}
+    peek ptr = do
+      id'              <- (\hsc_ptr -> peekByteOff hsc_ptr 0) ptr
+{-# LINE 67 "convexhull.hsc" #-}
+      point'           <- (\hsc_ptr -> peekByteOff hsc_ptr 8) ptr
+{-# LINE 68 "convexhull.hsc" #-}
+      neighfacets'     <- (\hsc_ptr -> peekByteOff hsc_ptr 16) ptr
+{-# LINE 69 "convexhull.hsc" #-}
+      nneighfacets'    <- (\hsc_ptr -> peekByteOff hsc_ptr 24) ptr
+{-# LINE 70 "convexhull.hsc" #-}
+      neighvertices'   <- (\hsc_ptr -> peekByteOff hsc_ptr 32) ptr
+{-# LINE 71 "convexhull.hsc" #-}
+      nneighsvertices' <- (\hsc_ptr -> peekByteOff hsc_ptr 40) ptr
+{-# LINE 72 "convexhull.hsc" #-}
+      neighridges'     <- (\hsc_ptr -> peekByteOff hsc_ptr 48) ptr
+{-# LINE 73 "convexhull.hsc" #-}
+      nneighridges'    <- (\hsc_ptr -> peekByteOff hsc_ptr 56) ptr
+{-# LINE 74 "convexhull.hsc" #-}
+      return CVertex' { __id'            = id'
+                      , __point'         = point'
+                      , __neighfacets    = neighfacets'
+                      , __nneighfacets   = nneighfacets'
+                      , __neighvertices  = neighvertices'
+                      , __nneighvertices = nneighsvertices'
+                      , __neighridges    = neighridges'
+                      , __nneighridges   = nneighridges'
+                      }
+    poke ptr (CVertex' r1 r2 r3 r4 r5 r6 r7 r8)
+      = do
+          (\hsc_ptr -> pokeByteOff hsc_ptr 0) ptr r1
+{-# LINE 86 "convexhull.hsc" #-}
+          (\hsc_ptr -> pokeByteOff hsc_ptr 8) ptr r2
+{-# LINE 87 "convexhull.hsc" #-}
+          (\hsc_ptr -> pokeByteOff hsc_ptr 16) ptr r3
+{-# LINE 88 "convexhull.hsc" #-}
+          (\hsc_ptr -> pokeByteOff hsc_ptr 24) ptr r4
+{-# LINE 89 "convexhull.hsc" #-}
+          (\hsc_ptr -> pokeByteOff hsc_ptr 32) ptr r5
+{-# LINE 90 "convexhull.hsc" #-}
+          (\hsc_ptr -> pokeByteOff hsc_ptr 40) ptr r6
+{-# LINE 91 "convexhull.hsc" #-}
+          (\hsc_ptr -> pokeByteOff hsc_ptr 48) ptr r7
+{-# LINE 92 "convexhull.hsc" #-}
+          (\hsc_ptr -> pokeByteOff hsc_ptr 56) ptr r8
+{-# LINE 93 "convexhull.hsc" #-}
+
+cVerticesToVertexMap :: Int -> [CVertex'] -> IO (IntMap Vertex)
+cVerticesToVertexMap dim cvertices = do
+  let ids             = map (fromIntegral . __id') cvertices
+      nneighfacets    = map (fromIntegral . __nneighfacets) cvertices
+      nneighsvertices = map (fromIntegral . __nneighvertices) cvertices
+      nneighridges    = map (fromIntegral . __nneighridges) cvertices
+  points <- mapM (\cv -> (<$!>) (map realToFrac) (peekArray dim (__point' cv)))
+                 cvertices
+  neighfacets <- mapM (\(i, cv) -> (<$!>) (map fromIntegral)
+                                          (peekArray i (__neighfacets cv)))
+                       (zip nneighfacets cvertices)
+  neighvertices <- mapM (\(i, cv) ->
+                          (<$!>) (map fromIntegral)
+                                 (peekArray i (__neighvertices cv)))
+                        (zip nneighsvertices cvertices)
+  neighridges <- mapM (\(i, cv) ->
+                       (<$!>) (map fromIntegral)
+                              (peekArray i (__neighridges cv)))
+                     (zip nneighridges cvertices)
+  return $ IM.fromList
+           (zip ids (map (\(pt, fneighs, vneighs, eneighs) ->
+                          Vertex { _point         = pt
+                                 , _neighfacets   = IS.fromAscList fneighs
+                                 , _neighvertices = IS.fromAscList vneighs
+                                 , _neighridges   = IS.fromAscList eneighs })
+                          (zip4 points neighfacets neighvertices neighridges)))
+
+data CRidge = CRidge {
+    __rvertices :: Ptr CVertex
+  , __ridgeOf1  :: CUInt
+  , __ridgeOf2  :: CUInt
+  , __ridgeSize :: CUInt
+  , __ridgeid   :: CUInt
+  , __redges    :: Ptr (Ptr CUInt)
+  , __nredges   :: CUInt
+}
+
+instance Storable CRidge where
+    sizeOf    __ = (40)
+{-# LINE 133 "convexhull.hsc" #-}
+    alignment __ = 8
+{-# LINE 134 "convexhull.hsc" #-}
+    peek ptr = do
+      rvertices <- (\hsc_ptr -> peekByteOff hsc_ptr 0) ptr
+{-# LINE 136 "convexhull.hsc" #-}
+      ridgeOf1' <- (\hsc_ptr -> peekByteOff hsc_ptr 8) ptr
+{-# LINE 137 "convexhull.hsc" #-}
+      ridgeOf2' <- (\hsc_ptr -> peekByteOff hsc_ptr 12) ptr
+{-# LINE 138 "convexhull.hsc" #-}
+      ridgeSize <- (\hsc_ptr -> peekByteOff hsc_ptr 16) ptr
+{-# LINE 139 "convexhull.hsc" #-}
+      ridgeid   <- (\hsc_ptr -> peekByteOff hsc_ptr 20) ptr
+{-# LINE 140 "convexhull.hsc" #-}
+      edges'    <- (\hsc_ptr -> peekByteOff hsc_ptr 24) ptr
+{-# LINE 141 "convexhull.hsc" #-}
+      nedges'   <- (\hsc_ptr -> peekByteOff hsc_ptr 32) ptr
+{-# LINE 142 "convexhull.hsc" #-}
+      return CRidge { __rvertices = rvertices
+                    , __ridgeOf1  = ridgeOf1'
+                    , __ridgeOf2  = ridgeOf2'
+                    , __ridgeSize = ridgeSize
+                    , __ridgeid   = ridgeid
+                    , __redges    = edges'
+                    , __nredges   = nedges' }
+    poke ptr (CRidge r1 r2 r3 r4 r5 r6 r7)
+      = do
+          (\hsc_ptr -> pokeByteOff hsc_ptr 0) ptr r1
+{-# LINE 152 "convexhull.hsc" #-}
+          (\hsc_ptr -> pokeByteOff hsc_ptr 8) ptr r2
+{-# LINE 153 "convexhull.hsc" #-}
+          (\hsc_ptr -> pokeByteOff hsc_ptr 12) ptr r3
+{-# LINE 154 "convexhull.hsc" #-}
+          (\hsc_ptr -> pokeByteOff hsc_ptr 16) ptr r4
+{-# LINE 155 "convexhull.hsc" #-}
+          (\hsc_ptr -> pokeByteOff hsc_ptr 20) ptr r5
+{-# LINE 156 "convexhull.hsc" #-}
+          (\hsc_ptr -> pokeByteOff hsc_ptr 24) ptr r6
+{-# LINE 157 "convexhull.hsc" #-}
+          (\hsc_ptr -> pokeByteOff hsc_ptr 32) ptr r7
+{-# LINE 158 "convexhull.hsc" #-}
+
+cRidgeToRidge :: Int -> CRidge -> IO (Int, Ridge)
+cRidgeToRidge dim cridge = do
+  let f1     = fromIntegral $ __ridgeOf1 cridge
+      f2     = fromIntegral $ __ridgeOf2 cridge
+      n      = fromIntegral $ __ridgeSize cridge
+      rid    = fromIntegral $ __ridgeid cridge
+      nedges = fromIntegral $ __nredges cridge
+  vertices <- peekArray n (__rvertices cridge)
+  rvertices <- cVerticesToMap dim vertices
+  edges' <- if dim > 3
+            then (<$!>) (map (\x -> (fromIntegral (x!!0), fromIntegral (x!!1))))
+                        ((=<<) (mapM (peekArray 2))
+                               (peekArray nedges (__redges cridge)))
+            else return []
+  let edges = if dim > 3
+                then H.fromList (zip (map (\(i,j) -> Pair i j) edges')
+                                     (map (both ((IM.!) rvertices)) edges'))
+                else H.empty
+  return (rid, Ridge { _rvertices = rvertices
+                     , _ridgeOf   = IS.fromAscList [f1,f2]
+                     , _redges    = edges })
+
+data CFace = CFace {
+    __fvertices    :: Ptr CVertex
+  , __nvertices'   :: CUInt
+  , __ridges       :: Ptr CUInt
+  , __nridges'     :: CUInt
+  , __center       :: Ptr CDouble
+  , __normal       :: Ptr CDouble
+  , __offset       :: CDouble
+  , __area         :: CDouble
+  , __neighbors    :: Ptr CUInt
+  , __neighborsize :: CUInt
+  , __family       :: CInt
+  , __edges        :: Ptr (Ptr CUInt)
+  , __nedges       :: CUInt
+}
+
+instance Storable CFace where
+    sizeOf    __ = (104)
+{-# LINE 199 "convexhull.hsc" #-}
+    alignment __ = 8
+{-# LINE 200 "convexhull.hsc" #-}
+    peek ptr = do
+      fvertices' <- (\hsc_ptr -> peekByteOff hsc_ptr 0) ptr
+{-# LINE 202 "convexhull.hsc" #-}
+      nvertices' <- (\hsc_ptr -> peekByteOff hsc_ptr 8) ptr
+{-# LINE 203 "convexhull.hsc" #-}
+      ridges'    <- (\hsc_ptr -> peekByteOff hsc_ptr 24) ptr
+{-# LINE 204 "convexhull.hsc" #-}
+      nridges'   <- (\hsc_ptr -> peekByteOff hsc_ptr 32) ptr
+{-# LINE 205 "convexhull.hsc" #-}
+      center'    <- (\hsc_ptr -> peekByteOff hsc_ptr 40) ptr
+{-# LINE 206 "convexhull.hsc" #-}
+      normal'    <- (\hsc_ptr -> peekByteOff hsc_ptr 48) ptr
+{-# LINE 207 "convexhull.hsc" #-}
+      offset'    <- (\hsc_ptr -> peekByteOff hsc_ptr 56) ptr
+{-# LINE 208 "convexhull.hsc" #-}
+      area'      <- (\hsc_ptr -> peekByteOff hsc_ptr 64) ptr
+{-# LINE 209 "convexhull.hsc" #-}
+      neighbors' <- (\hsc_ptr -> peekByteOff hsc_ptr 72) ptr
+{-# LINE 210 "convexhull.hsc" #-}
+      neighsize  <- (\hsc_ptr -> peekByteOff hsc_ptr 80) ptr
+{-# LINE 211 "convexhull.hsc" #-}
+      family'    <- (\hsc_ptr -> peekByteOff hsc_ptr 84) ptr
+{-# LINE 212 "convexhull.hsc" #-}
+      edges'     <- (\hsc_ptr -> peekByteOff hsc_ptr 88) ptr
+{-# LINE 213 "convexhull.hsc" #-}
+      nedges'    <- (\hsc_ptr -> peekByteOff hsc_ptr 96) ptr
+{-# LINE 214 "convexhull.hsc" #-}
+      return CFace { __fvertices    = fvertices'
+                   , __nvertices'   = nvertices'
+                   , __ridges       = ridges'
+                   , __nridges'     = nridges'
+                   , __center       = center'
+                   , __normal       = normal'
+                   , __offset       = offset'
+                   , __area         = area'
+                   , __neighbors    = neighbors'
+                   , __neighborsize = neighsize
+                   , __family       = family'
+                   , __edges        = edges'
+                   , __nedges       = nedges'
+                 }
+    poke ptr (CFace r1 r2 r3 r4 r5 r6 r7 r8 r9 r10 r11 r12 r13)
+      = do
+          (\hsc_ptr -> pokeByteOff hsc_ptr 0) ptr r1
+{-# LINE 231 "convexhull.hsc" #-}
+          (\hsc_ptr -> pokeByteOff hsc_ptr 8) ptr r2
+{-# LINE 232 "convexhull.hsc" #-}
+          (\hsc_ptr -> pokeByteOff hsc_ptr 24) ptr r3
+{-# LINE 233 "convexhull.hsc" #-}
+          (\hsc_ptr -> pokeByteOff hsc_ptr 32) ptr r4
+{-# LINE 234 "convexhull.hsc" #-}
+          (\hsc_ptr -> pokeByteOff hsc_ptr 40) ptr r5
+{-# LINE 235 "convexhull.hsc" #-}
+          (\hsc_ptr -> pokeByteOff hsc_ptr 48) ptr r6
+{-# LINE 236 "convexhull.hsc" #-}
+          (\hsc_ptr -> pokeByteOff hsc_ptr 56) ptr r7
+{-# LINE 237 "convexhull.hsc" #-}
+          (\hsc_ptr -> pokeByteOff hsc_ptr 64) ptr r8
+{-# LINE 238 "convexhull.hsc" #-}
+          (\hsc_ptr -> pokeByteOff hsc_ptr 72) ptr r9
+{-# LINE 239 "convexhull.hsc" #-}
+          (\hsc_ptr -> pokeByteOff hsc_ptr 80) ptr r10
+{-# LINE 240 "convexhull.hsc" #-}
+          (\hsc_ptr -> pokeByteOff hsc_ptr 84) ptr r11
+{-# LINE 241 "convexhull.hsc" #-}
+          (\hsc_ptr -> pokeByteOff hsc_ptr 88) ptr r12
+{-# LINE 242 "convexhull.hsc" #-}
+          (\hsc_ptr -> pokeByteOff hsc_ptr 96) ptr r13
+{-# LINE 243 "convexhull.hsc" #-}
+
+cFaceToFacet :: Int -> CFace -> IO Facet
+cFaceToFacet dim cface = do
+  let area      = realToFrac (__area cface)
+      neighsize = fromIntegral (__neighborsize cface)
+      offset    = realToFrac (__offset cface)
+      family    = fromIntegral (__family cface)
+      nridges   = fromIntegral (__nridges' cface)
+      nvertices = fromIntegral (__nvertices' cface)
+      nedges    = fromIntegral (__nedges cface)
+  center    <- (<$!>) (map realToFrac) (peekArray dim (__center cface))
+  normal    <- (<$!>) (map realToFrac) (peekArray dim (__normal cface))
+  vertices  <- (=<<) (cVerticesToMap dim)
+                     (peekArray nvertices (__fvertices cface))
+  ridges  <- (<$!>) (map fromIntegral)
+                    (peekArray nridges (__ridges cface))
+  neighbors <- (<$!>) (map fromIntegral)
+                      (peekArray neighsize (__neighbors cface))
+  edges' <- (<$!>) (map (\x -> (fromIntegral (x!!0), fromIntegral (x!!1))))
+                      ((=<<) (mapM (peekArray 2))
+                             (peekArray nedges (__edges cface)))
+  let edges = H.fromList
+              (zip (map (\(i,j) -> Pair i j) edges')
+                   (map (both ((IM.!) vertices)) edges'))
+  return Facet { _fvertices = vertices
+               , _fridges   = IS.fromAscList ridges
+               , _centroid  = center
+               , _normal'   = normal
+               , _offset'   = offset
+               , _area      = area
+               , _neighbors = IS.fromAscList neighbors
+               , _family'   = if family == -1 then None else Family family
+               , _fedges    = edges }
+
+data CConvexHull = CConvexHull {
+    __dim         :: CUInt
+  , __allvertices :: Ptr CVertex'
+  , __nvertices   :: CUInt
+  , __faces       :: Ptr CFace
+  , __nfaces      :: CUInt
+  , __allridges   :: Ptr CRidge
+  , __nridges     :: CUInt
+  , __alledges    :: Ptr (Ptr CUInt)
+  , __nalledges   :: CUInt
+}
+
+instance Storable CConvexHull where
+    sizeOf    __ = (72)
+{-# LINE 291 "convexhull.hsc" #-}
+    alignment __ = 8
+{-# LINE 292 "convexhull.hsc" #-}
+    peek ptr = do
+      dim'         <- (\hsc_ptr -> peekByteOff hsc_ptr 0) ptr
+{-# LINE 294 "convexhull.hsc" #-}
+      vertices'    <- (\hsc_ptr -> peekByteOff hsc_ptr 8) ptr
+{-# LINE 295 "convexhull.hsc" #-}
+      nvertices'   <- (\hsc_ptr -> peekByteOff hsc_ptr 16) ptr
+{-# LINE 296 "convexhull.hsc" #-}
+      faces'       <- (\hsc_ptr -> peekByteOff hsc_ptr 24) ptr
+{-# LINE 297 "convexhull.hsc" #-}
+      nfaces'      <- (\hsc_ptr -> peekByteOff hsc_ptr 32) ptr
+{-# LINE 298 "convexhull.hsc" #-}
+      allridges'   <- (\hsc_ptr -> peekByteOff hsc_ptr 40) ptr
+{-# LINE 299 "convexhull.hsc" #-}
+      nridges'     <- (\hsc_ptr -> peekByteOff hsc_ptr 48) ptr
+{-# LINE 300 "convexhull.hsc" #-}
+      alledges'    <- (\hsc_ptr -> peekByteOff hsc_ptr 56) ptr
+{-# LINE 301 "convexhull.hsc" #-}
+      nedges'      <- (\hsc_ptr -> peekByteOff hsc_ptr 64) ptr
+{-# LINE 302 "convexhull.hsc" #-}
+      return CConvexHull { __dim         = dim'
+                         , __allvertices = vertices'
+                         , __nvertices   = nvertices'
+                         , __faces       = faces'
+                         , __nfaces      = nfaces'
+                         , __allridges   = allridges'
+                         , __nridges     = nridges'
+                         , __alledges    = alledges'
+                         , __nalledges   = nedges'
+                     }
+    poke ptr (CConvexHull r1 r2 r3 r4 r5 r6 r7 r8 r9)
+      = do
+          (\hsc_ptr -> pokeByteOff hsc_ptr 0) ptr r1
+{-# LINE 315 "convexhull.hsc" #-}
+          (\hsc_ptr -> pokeByteOff hsc_ptr 8) ptr r2
+{-# LINE 316 "convexhull.hsc" #-}
+          (\hsc_ptr -> pokeByteOff hsc_ptr 16) ptr r3
+{-# LINE 317 "convexhull.hsc" #-}
+          (\hsc_ptr -> pokeByteOff hsc_ptr 24) ptr r4
+{-# LINE 318 "convexhull.hsc" #-}
+          (\hsc_ptr -> pokeByteOff hsc_ptr 32) ptr r5
+{-# LINE 319 "convexhull.hsc" #-}
+          (\hsc_ptr -> pokeByteOff hsc_ptr 40) ptr r6
+{-# LINE 320 "convexhull.hsc" #-}
+          (\hsc_ptr -> pokeByteOff hsc_ptr 48) ptr r7
+{-# LINE 321 "convexhull.hsc" #-}
+          (\hsc_ptr -> pokeByteOff hsc_ptr 56) ptr r8
+{-# LINE 322 "convexhull.hsc" #-}
+          (\hsc_ptr -> pokeByteOff hsc_ptr 64) ptr r9
+{-# LINE 323 "convexhull.hsc" #-}
+
+foreign import ccall unsafe "convexHull" c_convexhull
+  :: Ptr CDouble -- points
+  -> CUInt -- dim
+  -> CUInt -- npoints
+  -> CUInt -- triangulate
+  -> CUInt -- print to stdout
+  -> CString -- summary file
+  -> Ptr CUInt -- exitcode
+  -> IO (Ptr CConvexHull)
+
+cConvexHullToConvexHull :: CConvexHull -> IO ConvexHull
+cConvexHullToConvexHull cconvexhull = do
+  let dim       = fromIntegral (__dim cconvexhull)
+      nvertices = fromIntegral (__nvertices cconvexhull)
+      nfaces    = fromIntegral (__nfaces cconvexhull)
+      nridges   = fromIntegral (__nridges cconvexhull)
+      nedges    = fromIntegral (__nalledges cconvexhull)
+  vertices <- (=<<) (cVerticesToVertexMap dim)
+                    (peekArray nvertices (__allvertices cconvexhull))
+  faces <- (=<<) (mapM (cFaceToFacet dim))
+                       (peekArray nfaces (__faces cconvexhull))
+  allridges <- (=<<) (mapM (cRidgeToRidge dim))
+                          (peekArray nridges (__allridges cconvexhull))
+  alledges' <- (<$!>) (map (\x -> (fromIntegral (x!!0), fromIntegral (x!!1))))
+                      ((=<<) (mapM (peekArray 2))
+                             (peekArray nedges (__alledges cconvexhull)))
+  let alledges = let points = IM.map _point vertices in
+                  H.fromList
+                  (zip (map (\(i,j) -> Pair i j) alledges')
+                       (map (both ((IM.!) points)) alledges'))
+  return ConvexHull {
+                        _hvertices = vertices
+                      , _hfacets   = fromAscList (zip [0 .. nfaces-1] faces)
+                      , _hridges   = fromAscList allridges
+                      , _hedges    = alledges
+                    }
diff --git a/src/ConvexHull/CantiTrunc600Cell/Data.hs b/src/ConvexHull/CantiTrunc600Cell/Data.hs
new file mode 100644
--- /dev/null
+++ b/src/ConvexHull/CantiTrunc600Cell/Data.hs
@@ -0,0 +1,67 @@
+module ConvexHull.CantiTrunc600Cell.Data
+  where
+import Math.Combinat.Permutations as P
+import Data.List
+-- http://eusebeia.dyndns.org/4d/cantitrunc600cell
+
+signs :: (Eq a, Num a) => [a] -> [[a]]
+signs = mapM (\x -> nub [x,-x])
+
+signsAll :: (Eq a, Num a) => [[a]] -> [[a]]
+signsAll = concatMap signs
+
+vertices :: ([Double], Bool) -> [[Double]]
+vertices (coords, allperms) =
+  map (map (/ sqrt 270.1640786)) $ signsAll $
+  nub $ zipWith permuteList perms (replicate 24 coords)
+  where perms = filter (if allperms then const True else isEvenPermutation) (P.permutations 4)
+
+
+allVertices :: [[Double]]
+allVertices = concatMap vertices
+  [ ([1, 1, 1+6*phi, 5+6*phi], True)
+  , ([1, 3, 3+6*phi, 3+6*phi], True)
+  , ([2, 2, 2+6*phi, 2*phi4], True)
+  , ([0, 1, 3*phi, 3+9*phi], False)
+  , ([0, 1, 5*phi, 5+7*phi], False)
+  , ([0, 2, 4*phi, 4*phi3], False)
+  , ([0, 4+3*phi, phi5, 5+4*phi], False)
+  , ([0, 3+4*phi, 4+5*phi, 5+3*phi], False)
+  , ([1, phi, 6*phi2, 1+5*phi], False)
+  , ([1, 2*phi, 3+9*phi, 2+phi], False)
+  , ([1, 3+phi, 3*phi, 4*phi3], False)
+  , ([1, 3*phi, 6*phi2, 3*phi2], False)
+  , ([1, 3+2*phi, 3*phi3, 5+4*phi], False)
+  , ([1, phi4, 2*phi4, 5+3*phi], False)
+  , ([phi, 2, phi3, 3+9*phi], False)
+  , ([phi, 3, 1+3*phi, 4*phi3], False)
+  , ([phi, 3*phi2, 2+6*phi, 5+4*phi], False)
+  , ([phi, 2*phi3, 3*phi3, 5+3*phi], False)
+  , ([2, 2*phi, 6*phi2, 2*phi3], False)
+  , ([2, 3+phi, 3*phi3, 4+5*phi], False)
+  , ([2, 3*phi, 5+7*phi, 3+2*phi], False)
+  , ([2, 1+3*phi, 5+6*phi, 4+3*phi], False)
+  , ([3, 2*phi, 5+7*phi, phi4], False)
+  , ([3, 2+phi, 2*phi4, phi5], False)
+  , ([3, phi3, 5+6*phi, 3+4*phi], False)
+  , ([2*phi, 3*phi2, 1+6*phi, 4+5*phi], False)
+  , ([2*phi, 4+3*phi, 1+5*phi, 3*phi3], False)
+  , ([2+phi, 3+phi, 2+6*phi, 3*phi3], False)
+  , ([2+phi, 1+3*phi, 6*phi2, 3+2*phi], False)
+  , ([2+phi, 4*phi, 5+6*phi, 3*phi2], False)
+  , ([phi3, 3+phi, phi4, 6*phi2], False)
+  , ([phi3, 3+2*phi, 1+6*phi, 3*phi3], False)
+  , ([phi3, 3*phi2, 5*phi, 2*phi4], False)
+  , ([3+phi, 3*phi, 5+6*phi, 2*phi3], False)
+  , ([3*phi, 3+2*phi, 1+5*phi, 2*phi4], False)
+  , ([3*phi, 2*phi3, 1+6*phi, phi5], False)
+  , ([3*phi, 1+5*phi, 2+6*phi, 3+4*phi], False)
+  , ([1+3*phi, phi4, 1+6*phi, 2+6*phi], False)
+  , ([1+3*phi, 5*phi, 3*phi3, 2*phi3], False)
+  , ([4*phi, phi4, 1+5*phi, 3*phi3], False)]
+  where
+    phi2 = phi*phi
+    phi3 = phi2*phi
+    phi4 = phi3*phi
+    phi5 = phi4*phi
+    phi = (1 + sqrt 5) / 2
diff --git a/src/ConvexHull/ConvexHull.hs b/src/ConvexHull/ConvexHull.hs
new file mode 100644
--- /dev/null
+++ b/src/ConvexHull/ConvexHull.hs
@@ -0,0 +1,207 @@
+module ConvexHull.ConvexHull
+  where
+import           Control.Monad              (unless, when)
+import           ConvexHull.CConvexHull
+import           ConvexHull.Types
+import           Data.Function              (on)
+import           Data.Graph                 (flattenSCCs, stronglyConnComp)
+import qualified Data.HashMap.Strict.InsOrd as H
+import           Data.IntMap.Strict         (IntMap)
+import qualified Data.IntMap.Strict         as IM
+import qualified Data.IntSet                as IS
+import           Data.List
+import           Data.List.Index            (imap)
+import           Data.List.Unique           (allUnique, count_)
+import           Data.Tuple.Extra           (both)
+import           Foreign.C.String
+import           Foreign.C.Types
+import           Foreign.Marshal.Alloc      (free, mallocBytes)
+import           Foreign.Marshal.Array      (pokeArray)
+import           Foreign.Storable           (peek, sizeOf)
+import           Qhull.Shared
+import           Qhull.Types
+import           Text.Printf
+import           Text.Regex
+
+convexHull :: [[Double]]     -- vertices
+           -> Bool           -- triangulate
+           -> Bool           -- print output to stdout
+           -> Maybe FilePath -- write summary to a file
+           -> IO ConvexHull
+convexHull points triangulate stdout file = do
+  let n     = length points
+      dim   = length (head points)
+  when (dim < 2) $
+    error "dimension must be at least 2"
+  unless (all (== dim) (map length (tail points))) $
+    error "the points must have the same dimension"
+  when (n <= dim) $
+    error "insufficient number of points"
+  unless (allUnique points) $
+    error "some points are duplicated"
+  pointsPtr <- mallocBytes (n * dim * sizeOf (undefined :: CDouble))
+  pokeArray pointsPtr (concatMap (map realToFrac) points)
+  exitcodePtr <- mallocBytes (sizeOf (undefined :: CUInt))
+  summaryFile <- maybe (newCString []) newCString file
+  resultPtr <- c_convexhull pointsPtr (fromIntegral dim) (fromIntegral n)
+               (fromIntegral $ fromEnum triangulate)
+               (fromIntegral $ fromEnum stdout) summaryFile exitcodePtr
+  exitcode <- peek exitcodePtr
+  free exitcodePtr
+  free pointsPtr
+  if exitcode /= 0
+    then do
+      free resultPtr
+      error $ "qhull returned an error (code " ++ show exitcode ++ ")"
+    else do
+      result <- (>>=) (peek resultPtr) cConvexHullToConvexHull
+      free resultPtr
+      return result
+
+
+-- | convex hull summary
+hullSummary :: ConvexHull -> String
+hullSummary hull =
+  "Convex hull:\n" ++
+  printf "%d vertices\n" (IM.size vertices) ++
+  printf "%d facets (%s)\n" nfacets families ++
+  printf "%d ridges\n" nridges ++
+  printf "%d edges\n" nedges ++
+  (if dim > 2
+    then printf "number of vertices per facet: %s\n" (show counts_vertices) ++
+         (if dim > 3
+           then printf "number of edges per facet: %s\n" (show counts_edges) ++
+                printf "number of ridges per facet: %s\n" (show counts_ridges)
+           else "")
+    else "")
+  where
+    vertices = _vertices hull
+    nedges = nEdges hull
+    nridges = IM.size (_hridges hull)
+    facets = _hfacets hull
+    facets' = IM.elems facets
+    nfacets = IM.size facets
+    (nf1,nf2) = both length $
+                partition (None ==) (nubBy sameFamily (map _family facets'))
+    families = show nf1 ++ " single, " ++
+               show nf2 ++ if nf2 > 1 then " families" else " family"
+    dim = length $ head (IM.elems vertices)
+    counts_vertices = count_ (map nVertices facets')
+    counts_edges = count_ (map nEdges facets')
+    counts_ridges = count_ (map (IS.size . _fridges) facets')
+
+-- | facets ids an edge belongs to
+edgeOf :: ConvexHull -> (Index, Index) -> [Int]
+edgeOf hull (v1,v2) = IM.keys $ IM.filter (elem (Pair v1 v2)) facetsEdges
+  where
+    facetsEdges = IM.map edgesIds (_hfacets hull)
+
+-- | ridges of a facet
+facetRidges :: ConvexHull -> Facet -> IntMap Ridge
+facetRidges hull facet = IM.restrictKeys (_hridges hull) (_fridges facet)
+
+-- | vertices ids of all facets
+facetsVerticesIds :: ConvexHull -> [[Index]]
+facetsVerticesIds hull = map verticesIds (IM.elems $ _hfacets hull)
+
+-- | vertices ids of all ridges
+ridgesVerticesIds :: ConvexHull -> [[Index]]
+ridgesVerticesIds hull = map verticesIds (IM.elems (_hridges hull))
+
+-- | group facets of the same family
+groupedFacets :: ConvexHull -> [(Family, [IndexMap [Double]], [EdgeMap])]
+groupedFacets hull =
+  zip3 (map head families) verticesGroups edgesGroups
+  where
+    facets         = IM.elems (_hfacets hull)
+    facetsGroups   = groupBy (sameFamily `on` _family) facets
+    edgesGroups    = map (map _fedges) facetsGroups
+    verticesGroups = map (map _fvertices) facetsGroups
+    families       = map (map _family) facetsGroups
+
+-- | group facets of the same family and merge vertices and edges
+groupedFacets' :: ConvexHull -> [(Family, IndexMap [Double], EdgeMap)]
+groupedFacets' hull =
+  map (\(f,v,e) -> (f, foldr IM.union IM.empty v, foldr delta H.empty e))
+      (groupedFacets hull)
+  -- zip3 (map head families) (map (foldr IM.union IM.empty) verticesGroups)
+  --      (map (foldr delta H.empty) edgesGroups)
+  where
+    -- facets         = IM.elems (_hfacets hull)
+    -- facetsGroups   = groupBy (sameFamily `on` _family) facets
+    -- edgesGroups    = map (map _fedges) facetsGroups
+    -- verticesGroups = map (map _fvertices) facetsGroups
+    -- families       = map (map _family) facetsGroups
+    delta :: EdgeMap -> EdgeMap -> EdgeMap
+    delta e1 e2 = H.difference (H.union e1 e2) (H.intersection e1 e2)
+
+-- data Vertex3 = Vertex3 Double Double Double
+--   deriving Show
+--
+-- toVertex3 :: [Double] -> Vertex3
+-- toVertex3 xs = Vertex3 (xs!!0) (xs!!1) (xs!!2)
+
+-- | for 3D only, orders the vertices of the facet (i.e. provides a polygon) ;
+-- also returns a Boolean indicating the orientation of the vertices
+facetToPolygon :: Facet -> ([(Index, [Double])], Bool)
+facetToPolygon facet = (polygon, dotProduct > 0)
+  where
+  vs = IM.toList $ _vertices facet
+  x = imap (\i v -> (v, i, findIndices (connectedVertices v) vs)) vs
+    where
+    connectedVertices :: (Index, [Double]) -> (Index, [Double]) -> Bool
+    connectedVertices (i,_) (j,_) = Pair i j `H.member` _edges facet
+  polygon = flattenSCCs (stronglyConnComp x)
+  vertices = map snd polygon
+  v1 = vertices!!0
+  v2 = vertices!!1
+  v3 = vertices!!2
+  normal = crossProd (zipWith subtract v1 v2) (zipWith subtract v1 v3)
+    where
+    crossProd u v = [ u!!1 * v!!2 - u!!2 * v!!1
+                    , u!!2 * v!!0 - u!!0 * v!!2
+                    , u!!0 * v!!1 - u!!1 * v!!0 ]
+  dotProduct = sum $ zipWith (*) normal (_normal facet)
+
+-- | for 3D only, orders the vertices of the facet (i.e. provides a polygon)
+-- in anticlockwise orientation
+facetToPolygon' :: Facet -> [(Index, [Double])]
+facetToPolygon' facet = if test then polygon else reverse polygon
+  where
+  (polygon, test) = facetToPolygon facet
+
+-- -- | like `facetToPolygon`, but returns the vertices indices
+-- facetToPolygon' :: Facet -> [Index]
+-- facetToPolygon' facet = map fst $ flattenSCCs (stronglyConnComp x)
+--   where
+--     vs = IM.toList $ _vertices facet
+--     x = imap (\i v -> (v, i, findIndices (connectedVertices v) vs)) vs
+--     connectedVertices :: (Index, [Double]) -> (Index, [Double]) -> Bool
+--     connectedVertices (i,_) (j,_) = Pair i j `H.member` _edges facet
+
+-- | for 4D only, orders the vertices of a ridge (i.e. provides a polygon)
+ridgeToPolygon :: Ridge -> [(Index, [Double])]
+ridgeToPolygon ridge = flattenSCCs (stronglyConnComp x)
+  where
+  vs = IM.toList $ _vertices ridge
+  x = imap (\i v -> (v, i, findIndices (connectedVertices v) vs)) vs
+    where
+    connectedVertices :: (Index, [Double]) -> (Index, [Double]) -> Bool
+    connectedVertices (i,_) (j,_) = Pair i j `H.member` _edges ridge
+
+-- | for 3D only, convert the convex hull to STL format
+hullToSTL :: ConvexHull -> FilePath -> IO ()
+hullToSTL chull filename = do
+  let facets = IM.elems (_hfacets chull)
+      normals = map _normal facets
+      facetNormals = map (\n -> "facet normal  " ++ stringify " " n ++ "\nouter loop\n")
+                     normals
+      polygons = map (\f -> "vertex  " ++
+                        (stringify "\nvertex  " . map snd . facetToPolygon') f) facets
+      vertices = map (++ "\nendloop\nendfacet\n") polygons
+      vertices' = map (\v -> subRegex (mkRegex "\\]") (subRegex (mkRegex "\\[") v "") "") vertices
+      out = subRegex (mkRegex ",") (concat [x ++ y | x <- facetNormals, y <- vertices']) " "
+  writeFile filename ("solid " ++ filename ++ " produced by QHULL\n" ++ out)
+  where
+    stringify :: Show a => String -> [a] -> String
+    stringify sep = intercalate sep . map show
diff --git a/src/ConvexHull/Examples.hs b/src/ConvexHull/Examples.hs
new file mode 100644
--- /dev/null
+++ b/src/ConvexHull/Examples.hs
@@ -0,0 +1,4996 @@
+module ConvexHull.Examples
+  where
+import           Data.List                  hiding (permutations)
+import           Data.List.Split            (chunksOf)
+import           Math.Combinat.Permutations
+import           System.Random
+
+sixhundredCell :: [[Double]]
+sixhundredCell =
+  [[i, j, k, l] | i <- pm, j <- pm, k <- pm, l <-pm] ++
+  [[0, 0, 0, i*2] | i <- pm] ++
+  [[0, 0, i*2, 0] | i <- pm] ++
+  [[0, i*2, 0, 0] | i <- pm] ++
+  [[i*2, 0, 0, 0] | i <- pm] ++
+  [ permuteList p [i*phi, j, k/phi, 0] |
+    p <- permutations4, isEvenPermutation p, i <- pm, j <- pm, k <- pm] 
+  where
+    permutations4 = permutations 4
+    phi = (1 + sqrt 5) / 2
+    pm = [-1,1]
+
+hexadecachoron :: [[Double]]
+hexadecachoron =
+  [ [1,0,0,0]
+  , [-1,0,0,0]
+  , [0,1,0,0]
+  , [0,-1,0,0]
+  , [0,0,1,0]
+  , [0,0,-1,0]
+  , [0,0,0,1]
+  , [0,0,0,-1] ]
+
+runcitruncated5cell :: [[Double]]
+runcitruncated5cell =
+  [[2/sqrt 10, -2/sqrt 6, 1/sqrt 3, 3]
+  ,[2/sqrt 10, 2/sqrt 6, -1/sqrt 3, 3]
+  ,[2/sqrt 10, -2/sqrt 6, 4/sqrt 3, 2]
+  ,[2/sqrt 10, 2/sqrt 6, -4/sqrt 3, 2]
+  ,[2/sqrt 10, -2/sqrt 6, -5/sqrt 3, 1]
+  ,[2/sqrt 10, 2/sqrt 6, 5/sqrt 3, 1]
+  ,[2/sqrt 10, sqrt 6, 0, 2]
+  ,[2/sqrt 10, -sqrt 6, 0, 2]
+  ,[2/sqrt 10, sqrt 6, sqrt 3, 1]
+  ,[2/sqrt 10, -sqrt 6, sqrt 3, 1]
+  ,[7/sqrt 10, -1/sqrt 6, 2/sqrt 3, 2]
+  ,[7/sqrt 10, -1/sqrt 6, -4/sqrt 3, 0]
+  ,[7/sqrt 10, 3/sqrt 6, 0, 2]
+  ,[7/sqrt 10, 3/sqrt 6, sqrt 3, 1]
+  ,[7/sqrt 10, 3/sqrt 6, -sqrt 3, 1]
+  ,[7/sqrt 10, -5/sqrt 6, 1/sqrt 3, 1]
+  ,[7/sqrt 10, -5/sqrt 6, -2/sqrt 3, 0]
+  ,[-3/sqrt 10, 1/sqrt 6, 1/sqrt 3, 3]
+  ,[-3/sqrt 10, 1/sqrt 6, 4/sqrt 3, 2]
+  ,[-3/sqrt 10, 1/sqrt 6, -5/sqrt 3, 1]
+  ,[-3/sqrt 10, 5/sqrt 6, 2/sqrt 3, 2]
+  ,[-3/sqrt 10, 5/sqrt 6, -4/sqrt 3, 0]
+  ,[-3/sqrt 10, -7/sqrt 6, -1/sqrt 3, 1]
+  ,[-3/sqrt 10, -7/sqrt 6, 2/sqrt 3, 0]
+  ,[-8/sqrt 10, 0, 0, 2]
+  ,[-8/sqrt 10, 0, sqrt 3, 1]
+  ,[-8/sqrt 10, 0, -sqrt 3, 1]
+  ,[-8/sqrt 10, -4/sqrt 6, -1/sqrt 3, 1]
+  ,[-8/sqrt 10, 4/sqrt 6, 1/sqrt 3, 1]
+  ,[-8/sqrt 10, -4/sqrt 6, 2/sqrt 3, 0]
+  ,[-8/sqrt 10, 4/sqrt 6, -2/sqrt 3, 0]
+  ,[2/sqrt 10, -2/sqrt 6, 1/sqrt 3, -3]
+  ,[2/sqrt 10, 2/sqrt 6, -1/sqrt 3, -3]
+  ,[2/sqrt 10, -2/sqrt 6, 4/sqrt 3, -2]
+  ,[2/sqrt 10, 2/sqrt 6, -4/sqrt 3, -2]
+  ,[2/sqrt 10, -2/sqrt 6, -5/sqrt 3, -1]
+  ,[2/sqrt 10, 2/sqrt 6, 5/sqrt 3, -1]
+  ,[2/sqrt 10, sqrt 6, 0, -2]
+  ,[2/sqrt 10, -sqrt 6, 0, -2]
+  ,[2/sqrt 10, sqrt 6, -sqrt 3, -1]
+  ,[2/sqrt 10, -sqrt 6, -sqrt 3, -1]
+  ,[7/sqrt 10, -1/sqrt 6, 2/sqrt 3, -2]
+  ,[7/sqrt 10, 3/sqrt 6, 0, -2]
+  ,[7/sqrt 10, 3/sqrt 6, sqrt 3, -1]
+  ,[7/sqrt 10, 3/sqrt 6, -sqrt 3, -1]
+  ,[7/sqrt 10, -5/sqrt 6, 1/sqrt 3, -1]
+  ,[-3/sqrt 10, 1/sqrt 6, 1/sqrt 3, -3]
+  ,[-3/sqrt 10, 1/sqrt 6, 4/sqrt 3, -2]
+  ,[-3/sqrt 10, 1/sqrt 6, -5/sqrt 3, -1]
+  ,[-3/sqrt 10, 5/sqrt 6, 2/sqrt 3, -2]
+  ,[-3/sqrt 10, -7/sqrt 6, -1/sqrt 3, -1]
+  ,[-8/sqrt 10, 0, 0, -2]
+  ,[-8/sqrt 10, 0, sqrt 3, -1]
+  ,[-8/sqrt 10, 0, -sqrt 3, -1]
+  ,[-8/sqrt 10, -4/sqrt 6, -1/sqrt 3, -1]
+  ,[-8/sqrt 10, 4/sqrt 6, 1/sqrt 3, -1] ]
+
+
+cantellated5cell :: [[Double]]
+cantellated5cell =
+  [[4/sqrt 10, 0, 0, 2]
+  ,[4/sqrt 10, 0, 3/sqrt 3, 1]
+  ,[4/sqrt 10, 0, -3/sqrt 3, 1]
+  ,[4/sqrt 10, -4/sqrt 6, 2/sqrt 3, 0]
+  ,[4/sqrt 10, 4/sqrt 6, -2/sqrt 3, 0]
+  ,[4/sqrt 10, -4/sqrt 6, -1/sqrt 3, 1]
+  ,[4/sqrt 10, 4/sqrt 6, 1/sqrt 3, 1]
+  ,[-6/sqrt 10, -2/sqrt 6, -2/sqrt 3, 0]
+  ,[-6/sqrt 10, 2/sqrt 6, 2/sqrt 3, 0]
+  ,[-1/sqrt 10, -1/sqrt 6, -4/sqrt 3, 0]
+  ,[-1/sqrt 10, -5/sqrt 6, -2/sqrt 3, 0]
+  ,[-1/sqrt 10, 3/sqrt 6, 0, 2]
+  ,[-1/sqrt 10, -1/sqrt 6, 2/sqrt 3, 2]
+  ,[-1/sqrt 10, -5/sqrt 6, 1/sqrt 3, 1]
+  ,[-1/sqrt 10, 3/sqrt 6, 3/sqrt 3, 1]
+  ,[-1/sqrt 10, 3/sqrt 6, -3/sqrt 3, 1]
+  ,[-6/sqrt 10, -2/sqrt 6, 1/sqrt 3, 1]
+  ,[-6/sqrt 10, 2/sqrt 6, -1/sqrt 3, 1]
+  ,[4/sqrt 10, 0, 0, -2]
+  ,[4/sqrt 10, 0, 3/sqrt 3, -1]
+  ,[4/sqrt 10, 0, -3/sqrt 3, -1]
+  ,[4/sqrt 10, -4/sqrt 6, -1/sqrt 3, -1]
+  ,[4/sqrt 10, 4/sqrt 6, 1/sqrt 3, -1]
+  ,[-1/sqrt 10, 3/sqrt 6, 0, -2]
+  ,[-1/sqrt 10, -1/sqrt 6, 2/sqrt 3, -2]
+  ,[-1/sqrt 10, -5/sqrt 6, 1/sqrt 3, -1]
+  ,[-1/sqrt 10, 3/sqrt 6, 3/sqrt 3, -1]
+  ,[-1/sqrt 10, 3/sqrt 6, -3/sqrt 3, -1]
+  ,[-6/sqrt 10, -2/sqrt 6, 1/sqrt 3, -1]
+  ,[-6/sqrt 10, 2/sqrt 6, -1/sqrt 3, -1]]
+
+bitruncated5cell :: [[Double]]
+bitruncated5cell =
+  [ [0, 4/sqrt 6, 4/sqrt 3, 0]
+  , [0, -4/sqrt 6, -4/sqrt 3, 0]
+  , [0, 4/sqrt 6, -2/sqrt 3, 2]
+  , [0, 4/sqrt 6, -2/sqrt 3, -2]
+  , [0, -4/sqrt 6, 2/sqrt 3, -2]
+  , [0, -4/sqrt 6, 2/sqrt 3, 2]
+  , [5/sqrt 10, 1/sqrt 6, 4/sqrt 3, 0]
+  , [-5/sqrt 10, -1/sqrt 6, -4/sqrt 3, 0]
+  , [5/sqrt 10, 1/sqrt 6, -2/sqrt 3, 2]
+  , [5/sqrt 10, 1/sqrt 6, -2/sqrt 3, -2]
+  , [-5/sqrt 10, -1/sqrt 6, 2/sqrt 3, -2]
+  , [-5/sqrt 10, -1/sqrt 6, 2/sqrt 3, 2]
+  , [5/sqrt 10, 5/sqrt 6, 2/sqrt 3, 0]
+  , [-5/sqrt 10, -5/sqrt 6, -2/sqrt 3, 0]
+  , [5/sqrt 10, 5/sqrt 6, -1/sqrt 3, 1]
+  , [5/sqrt 10, 5/sqrt 6, -1/sqrt 3, -1]
+  , [-5/sqrt 10, -5/sqrt 6, 1/sqrt 3, -1]
+  , [-5/sqrt 10, -5/sqrt 6, 1/sqrt 3, 1]
+  , [5/sqrt 10, -3/sqrt 6, 0, 2]
+  , [5/sqrt 10, -3/sqrt 6, 0, -2]
+  , [-5/sqrt 10, 3/sqrt 6, 0, -2]
+  , [-5/sqrt 10, 3/sqrt 6, 0, 2]
+  , [5/sqrt 10, -3/sqrt 6, sqrt 3, 1]
+  , [5/sqrt 10, -3/sqrt 6, sqrt 3, -1]
+  , [5/sqrt 10, -3/sqrt 6, -sqrt 3, 1]
+  , [5/sqrt 10, -3/sqrt 6, -sqrt 3, -1]
+  , [-5/sqrt 10, 3/sqrt 6, -sqrt 3, -1]
+  , [-5/sqrt 10, 3/sqrt 6, -sqrt 3, 1]
+  , [-5/sqrt 10, 3/sqrt 6, sqrt 3, -1]
+  , [-5/sqrt 10, 3/sqrt 6, sqrt 3, 1] ]
+
+rectified5cell :: [[Double]]
+rectified5cell =
+  [ [-3/sqrt 10, -3/sqrt 6, 0, 0]
+  , [-3/sqrt 10, 1/sqrt 6, -2/sqrt 3, 0]
+  , [-3/sqrt 10, 1/sqrt 6, 1/sqrt 3, 1]
+  , [-3/sqrt 10, 1/sqrt 6, 1/sqrt 3, -1]
+  , [2/sqrt 10, 2/sqrt 6, 2/sqrt 3, 0]
+  , [2/sqrt 10, -2/sqrt 6, -2/sqrt 3, 0]
+  , [2/sqrt 10, 2/sqrt 6, -1/sqrt 3, 1]
+  , [2/sqrt 10, 2/sqrt 6, -1/sqrt 3, -1]
+  , [2/sqrt 10, -2/sqrt 6, 1/sqrt 3, 1]
+  , [2/sqrt 10, -2/sqrt 6, 1/sqrt 3, -1] ]
+
+sircope :: [[Double]]
+sircope =
+  let a = (1+ sqrt 2)/2 in
+  [
+   [-a, -0.5, -0.5, -0.5],
+   [a, -0.5, -0.5, -0.5],
+   [-a, 0.5, -0.5, -0.5],
+   [a, 0.5, -0.5, -0.5],
+   [-a, -0.5, 0.5, -0.5],
+   [a, -0.5, 0.5, -0.5],
+   [-a, 0.5, 0.5, -0.5],
+   [a, 0.5, 0.5, -0.5],
+   [-a, -0.5, -0.5, 0.5],
+   [a, -0.5, -0.5, 0.5],
+   [-a, 0.5, -0.5, 0.5],
+   [a, 0.5, -0.5, 0.5],
+   [-a, -0.5, 0.5, 0.5],
+   [a, -0.5, 0.5, 0.5],
+   [-a, 0.5, 0.5, 0.5],
+   [a, 0.5, 0.5, 0.5],
+   [-0.5, -a, -0.5, -0.5],
+   [0.5, -a, -0.5, -0.5],
+   [-0.5, a, -0.5, -0.5],
+   [0.5, a, -0.5, -0.5],
+   [-0.5, -a, 0.5, -0.5],
+   [0.5, -a, 0.5, -0.5],
+   [-0.5, a, 0.5, -0.5],
+   [0.5, a, 0.5, -0.5],
+   [-0.5, -a, -0.5, 0.5],
+   [0.5, -a, -0.5, 0.5],
+   [-0.5, a, -0.5, 0.5],
+   [0.5, a, -0.5, 0.5],
+   [-0.5, -a, 0.5, 0.5],
+   [0.5, -a, 0.5, 0.5],
+   [-0.5, a, 0.5, 0.5],
+   [0.5, a, 0.5, 0.5],
+   [-0.5, -0.5, -a, -0.5],
+   [0.5, -0.5, -a, -0.5],
+   [-0.5, 0.5, -a, -0.5],
+   [0.5, 0.5, -a, -0.5],
+   [-0.5, -0.5, a, -0.5],
+   [0.5, -0.5, a, -0.5],
+   [-0.5, 0.5, a, -0.5],
+   [0.5, 0.5, a, -0.5],
+   [-0.5, -0.5, -a, 0.5],
+   [0.5, -0.5, -a, 0.5],
+   [-0.5, 0.5, -a, 0.5],
+   [0.5, 0.5, -a, 0.5],
+   [-0.5, -0.5, a, 0.5],
+   [0.5, -0.5, a, 0.5],
+   [-0.5, 0.5, a, 0.5],
+   [0.5, 0.5, a, 0.5]
+  ]
+
+tutcup :: [[Double]]
+tutcup =
+  [ [1, -1/sqrt 3, 5/sqrt 6, 1/sqrt 2]
+  , [-1, -1/sqrt 3, 5/sqrt 6, 1/sqrt 2]
+  , [0, 2/sqrt 3, 5/sqrt 6, 1/sqrt 2]
+  , [2, -2/sqrt 3, 1/sqrt 6, 1/sqrt 2]
+  , [-2, -2/sqrt 3, 1/sqrt 6, 1/sqrt 2]
+  , [0, 4/sqrt 3, 1/sqrt 6, 1/sqrt 2]
+  , [1, 3/sqrt 3, -3/sqrt 6, 1/sqrt 2]
+  , [1, -3/sqrt 3, -3/sqrt 6, 1/sqrt 2]
+  , [-1, 3/sqrt 3, -3/sqrt 6, 1/sqrt 2]
+  , [-1, -3/sqrt 3, -3/sqrt 6, 1/sqrt 2]
+  , [2, 0, -3/sqrt 6, 1/sqrt 2]
+  , [-2, 0, -3/sqrt 6, 1/sqrt 2]
+  , [1, 3/sqrt 3, 3/sqrt 6, -1/sqrt 2]
+  , [1, -3/sqrt 3, 3/sqrt 6, -1/sqrt 2]
+  , [-1, 3/sqrt 3, 3/sqrt 6, -1/sqrt 2]
+  , [-1, -3/sqrt 3, 3/sqrt 6, -1/sqrt 2]
+  , [2, 0, 3/sqrt 6, -1/sqrt 2]
+  , [-2, 0, 3/sqrt 6, -1/sqrt 2]
+  , [2, 2/sqrt 3, -1/sqrt 6, -1/sqrt 2]
+  , [-2, 2/sqrt 3, -1/sqrt 6, -1/sqrt 2]
+  , [0, -4/sqrt 3, -1/sqrt 6, -1/sqrt 2]
+  , [1, 1/sqrt 3, -5/sqrt 6, -1/sqrt 2]
+  , [-1, 1/sqrt 3, -5/sqrt 6, -1/sqrt 2]
+  , [0, -2/sqrt 3, -5/sqrt 6, -1/sqrt 2] ]
+
+
+
+runcinatedTesseract :: [[Double]]
+runcinatedTesseract =
+  [[i*a, j*b, k*c, l*d] | i <- pm, j <- pm, k <- pm, l <- pm, (a,b,c,d) <- s]
+  where
+  pm = [-1,1]
+  x = 1 + sqrt 2
+  s = [(1,1,1,x),(1,1,x,1),(1,x,1,1),(x,1,1,1)]
+
+runcinated5cells :: [[Double]]
+runcinated5cells = [ [sqrt(5/2), 1/sqrt 6, 1/sqrt 3, 1]
+                    ,[sqrt(5/2), 1/sqrt 6, 1/sqrt 3, -1]
+                    ,[-sqrt(5/2), -1/sqrt 6, -1/sqrt 3, -1]
+                    ,[-sqrt(5/2), -1/sqrt 6, -1/sqrt 3, 1]
+                    ,[sqrt(5/2), 1/sqrt 6, -2/sqrt 3, 0]
+                    ,[-sqrt(5/2), -1/sqrt 6, 2/sqrt 3, 0]
+                    ,[sqrt(5/2), -sqrt(3/2), 0, 0]
+                    ,[-sqrt(5/2), sqrt(3/2), 0, 0]
+                    ,[0, 2*sqrt(2/3), 1/sqrt 3, 1]
+                    ,[0, 2*sqrt(2/3), 1/sqrt 3, -1]
+                    ,[0, -2*sqrt(2/3), -1/sqrt 3, -1]
+                    ,[0, -2*sqrt(2/3), -1/sqrt 3, 1]
+                    ,[0, 2*sqrt(2/3), -2/sqrt 3, 0]
+                    ,[0, -2*sqrt(2/3), 2/sqrt 3, 0]
+                    ,[0, 0, sqrt 3, 1]
+                    ,[0, 0, sqrt 3, -1]
+                    ,[0, 0, -sqrt 3, 1]
+                    ,[0, 0, -sqrt 3, -1]
+                    ,[0, 0, 0, 2]
+                    ,[0, 0, 0, -2] ]
+
+truncated5cells :: [[Double]]
+truncated5cells = [ [3/sqrt 10, -1/sqrt 6, 2/sqrt 3, 2]
+                   ,[3/sqrt 10, -1/sqrt 6, 2/sqrt 3, -2]
+                   ,[3/sqrt 10, -1/sqrt 6, -4/sqrt 3, 0]
+                   ,[3/sqrt 10, 3/sqrt 6, 0, 2]
+                   ,[3/sqrt 10, 3/sqrt 6, 0, -2]
+                   ,[3/sqrt 10, 3/sqrt 6, sqrt 3, 1]
+                   ,[3/sqrt 10, 3/sqrt 6, sqrt 3, -1]
+                   ,[3/sqrt 10, 3/sqrt 6, -sqrt 3, 1]
+                   ,[3/sqrt 10, 3/sqrt 6, -sqrt 3, -1]
+                   ,[3/sqrt 10, -5/sqrt 6, 1/sqrt 3, 1]
+                   ,[3/sqrt 10, -5/sqrt 6, 1/sqrt 3, -1]
+                   ,[3/sqrt 10, -5/sqrt 6, -2/sqrt 3, 0]
+                   ,[-2/sqrt 10, 2/sqrt 6, 2/sqrt 3, 2]
+                   ,[-2/sqrt 10, 2/sqrt 6, 2/sqrt 3, -2]
+                   ,[-2/sqrt 10, 2/sqrt 6, -4/sqrt 3, 0]
+                   ,[-2/sqrt 10, -sqrt 6, 0, 0]
+                   ,[-7/sqrt 10, 1/sqrt 6, 1/sqrt 3, 1]
+                   ,[-7/sqrt 10, 1/sqrt 6, 1/sqrt 3, -1]
+                   ,[-7/sqrt 10, 1/sqrt 6, -2/sqrt 3, 0]
+                   ,[-7/sqrt 10, -3/sqrt 6, 0, 0] ]
+
+truncated24cells :: [[Double]]
+truncated24cells = [permuteList p [0,i,j*2,k*3] | p <- permutations 4, i <- pm, j <- pm , k <- pm]
+  where
+    pm = [-1,1]
+
+thex :: [[Double]]
+thex = nub [ permuteList p [0,0,i,j*2] | p <- permutations 4, i <- pm, j <- pm]
+  where
+    pm = [-1,1]
+
+truncatedTetrahedron :: [[Double]]
+truncatedTetrahedron = [ [1/sqrt 6, -2/sqrt 3, 2]
+                       , [1/sqrt 6, -2/sqrt 3, -2]
+                       , [1/sqrt 6, 4/ sqrt 3, 0]
+                       , [-3/sqrt 6, 0, 2]
+                       , [-3/sqrt 6, 0, -2]
+                       , [-3/sqrt 6, sqrt 3, 1]
+                       , [-3/sqrt 6, -sqrt 3, 1]
+                       , [-3/sqrt 6, sqrt 3, -1]
+                       , [-3/sqrt 6, -sqrt 3, -1]
+                       , [5/sqrt 6, -1/sqrt 3, 1]
+                       , [5/sqrt 6, -1/sqrt 3, -1]
+                       , [5/sqrt 6, 2/sqrt 3, 0] ]
+
+
+daVinci :: [[Double]]
+daVinci =   [ [1.61352, -0.43234, 1.1862],
+              [1.18118, -1.18118, 1.1862],
+              [0.43234, -1.61352, 1.1862],
+              [-0.43234, -1.61352, 1.1862],
+              [-1.18118, -1.18118, 1.1862],
+              [-1.61352, -0.43234, 1.1862],
+              [-1.61352, 0.43234, 1.1862],
+              [-1.18118, 1.18118, 1.1862],
+              [-0.43234, 1.61352, 1.1862],
+              [0.43234, 1.61352, 1.1862],
+              [1.18118, 1.18118, 1.1862],
+              [1.61352, 0.43234, 1.1862],
+              [1.61352, -0.43234, -1.1862],
+              [1.61352, 0.43234, -1.1862],
+              [1.18118, 1.18118, -1.1862],
+              [0.43234, 1.61352, -1.1862],
+              [-0.43234, 1.61352, -1.1862],
+              [-1.18118, 1.18118, -1.1862],
+              [-1.61352, 0.43234, -1.1862],
+              [-1.61352, -0.43234, -1.1862],
+              [-1.18118, -1.18118, -1.1862],
+              [-0.43234, -1.61352, -1.1862],
+              [0.43234, -1.61352, -1.1862],
+              [1.18118, -1.18118, -1.1862],
+              [2.0102, 0.53863, 0],
+              [1.47157, 1.47157, 0],
+              [0.53863, 2.0102, 0],
+              [-0.53863, 2.0102, 0],
+              [-1.47157, 1.47157, 0],
+              [-2.0102, 0.53863, 0],
+              [-2.0102, -0.53863, 0],
+              [-1.47157, -1.47157, 0],
+              [-0.53863, -2.0102, 0],
+              [0.53863, -2.0102, 0],
+              [1.47157, -1.47157, 0],
+              [2.0102, -0.53863, 0],
+              [0.89068, 0.23866, 1.77777],
+              [0.89068, -0.23866, 1.77777],
+              [0.65202, -0.65202, 1.77777],
+              [0.23866, -0.89068, 1.77777],
+              [-0.23866, -0.89068, 1.77777],
+              [-0.65202, -0.65202, 1.77777],
+              [-0.89068, -0.23866, 1.77777],
+              [-0.89068, 0.23866, 1.77777],
+              [-0.65202, 0.65202, 1.77777],
+              [-0.23866, 0.89068, 1.77777],
+              [0.23866, 0.89068, 1.77777],
+              [0.65202, 0.65202, 1.77777],
+              [0.65202, -0.65202, -1.77777],
+              [0.89068, -0.23866, -1.77777],
+              [0.89068, 0.23866, -1.77777],
+              [0.65202, 0.65202, -1.77777],
+              [0.23866, 0.89068, -1.77777],
+              [-0.23866, 0.89068, -1.77777],
+              [-0.65202, 0.65202, -1.77777],
+              [-0.89068, 0.23866, -1.77777],
+              [-0.89068, -0.23866, -1.77777],
+              [-0.65202, -0.65202, -1.77777],
+              [-0.23866, -0.89068, -1.77777],
+              [0.23866, -0.89068, -1.77777],
+              [0, 0, 2.04922],
+              [0, 0, -2.04922]]
+
+
+-- Cuboctohadron4D - does not work
+cuboctahedron4d :: [[Double]]
+cuboctahedron4d = nub [ permuteList p [0,1,1,2] | p <- permutations 4]
+
+-- OLOID --
+twocircles :: [[Double]]
+twocircles = circle1 `union` circle2
+  where
+    circle1 = [[cos (realToFrac i * 2*pi/30), sin (realToFrac i * 2*pi/30), 0] | i <- [0 .. 29]]
+    circle2 = [[0, cos (realToFrac i * 2*pi/30) - 1, sin (realToFrac i * 2*pi/30)] | i <- [0 .. 29]]
+
+translate3 :: [[Double]] -> [Double] -> [[Double]]
+translate3 points u = map (zipWith (+) u) points
+
+duplicate3 :: [[Double]] -> [Double] -> [[Double]]
+duplicate3 points u = nub $ points ++ translate3 points u
+
+rgg :: [[Double]]
+rgg = [[-5,-5, 16], [-5, 8, 3 ], [ 4,-1, 3 ], [ 4,-5, 7], [ 4,-1,-10],
+       [ 4,-5,-10], [-5, 8,-10], [-5,-5,-10]]
+
+centricCube :: [[Double]]
+centricCube =  [[-1,-1,-1],[-1,-1, 1],[-1, 1,-1],[-1, 1, 1],[ 1,-1,-1],
+                [ 1,-1, 1],[ 1, 1,-1],[ 1, 1, 1],[ 0, 0, 0]]
+
+squareLattice :: [[Double]]
+squareLattice = [[0,0],[0,1],[0,2]
+                ,[1,0],[1,1],[1,2]
+                ,[2,0],[2,1],[2,2]]
+
+centricSquare :: [[Double]]
+centricSquare = [[0,0],[0,2],[2,0],[2,2],[1,1]]
+
+cuboctahedron :: [[Double]]
+cuboctahedron = [[i,j,0] | i <- [-1,1], j <- [-1,1]] ++
+                [[i,0,j] | i <- [-1,1], j <- [-1,1]] ++
+                [[0,i,j] | i <- [-1,1], j <- [-1,1]]
+
+truncatedCuboctahedron :: [[Double]]
+truncatedCuboctahedron =
+  [[i, j * (1 + sqrt 2), k * (1 + 2*sqrt 2)] | i <- pm, j <- pm, k <- pm] ++
+  [[j * (1 + sqrt 2), i, k * (1 + 2*sqrt 2)] | i <- pm, j <- pm, k <- pm] ++
+  [[j * (1 + sqrt 2), k * (1 + 2*sqrt 2), i] | i <- pm, j <- pm, k <- pm] ++
+  [[i, k * (1 + 2*sqrt 2), j * (1 + sqrt 2)] | i <- pm, j <- pm, k <- pm] ++
+  [[k * (1 + 2*sqrt 2), i, j * (1 + sqrt 2)] | i <- pm, j <- pm, k <- pm] ++
+  [[k * (1 + 2*sqrt 2), j * (1 + sqrt 2), i] | i <- pm, j <- pm, k <- pm]
+  where
+    pm = [-1,1]
+
+rhombicDodecahedron :: [[Double]]
+rhombicDodecahedron = [[-1.0, 0.0, 0.0], [-0.5,-0.5,-0.5], [-0.5,-0.5, 0.5],
+                       [ 0.0,-1.0, 0.0], [-0.5, 0.5,-0.5], [-0.5, 0.5, 0.5],
+                       [ 0.0, 1.0, 0.0], [ 1.0, 0.0, 0.0], [ 0.5,-0.5,-0.5],
+                       [ 0.5,-0.5, 0.5], [ 0.5, 0.5,-0.5], [ 0.5, 0.5, 0.5],
+                       [ 0.0, 0.0,-1.0], [ 0.0, 0.0, 1.0]]
+
+faceCenteredCubic :: [[Double]]
+faceCenteredCubic = [[-1,-1,-1],[-1,-1,1],[-1,1,-1],[-1,1,1]
+                    ,[1,-1,-1],[1,-1,1],[1,1,-1],[1,1,1]
+                    ,[1,0,0],[-1,0,0]
+                    ,[0,1,0],[0,-1,0]
+                    ,[0,0,1],[0,0,-1]]
+
+waves :: [[Double]]
+waves = [f u v | u <- seq_u, v <- seq_v]
+  where
+    f u v = [u, 0.05*(sin(v*4*pi)+sin(u*4*pi)), v]
+    frac p q = realToFrac p/ realToFrac q
+    n = 10
+    seq_u = [8 * frac i n - 4 | i <- [0 .. n]]
+    seq_v = [4 * frac i n - 2 | i <- [0 .. n]]
+
+waves2D :: [[Double]]
+waves2D = concatMap (\y' -> map (\[x,y] -> [x, y+y']) sinusoid) seq_y
+  where
+    seq_x = [realToFrac i * pi/2 | i <- [0 .. 20]]
+    sinusoid = map (\(x,y) -> [x,y]) (zip seq_x (map sin seq_x))
+    seq_y = [realToFrac 2*i | i <- [0 .. 20]]
+
+
+type Vertex = [Double]
+ncube :: Int -> [Vertex]
+ncube n = concatMap (mapM (\x -> nub [x,-x])) [replicate n 1]
+
+cube3 :: [[Double]]
+cube3 = [[i,j,k] | i <- [-1,1], j <- [-1,1], k <- [-1,1]]
+
+cube4 :: [[Double]]
+cube4 = [[i,j,k,l] | i <- [-1,1], j <- [-1,1], k <- [-1,1], l <- [-1,1]]
+
+cube5 :: [[Double]]
+cube5 = [[i,j,k,l,m] | i <- [-1,1], j <- [-1,1], k <- [-1,1], l <- [-1,1],
+                       m <- [-1,1]]
+
+cube6 :: [[Double]]
+cube6 = [[i,j,k,l,m,n] | i <- [-1,1], j <- [-1,1], k <- [-1,1], l <- [-1,1],
+                         m <- [-1,1], n <- [-1,1]]
+
+
+mobiusStrip :: [[Double]]
+mobiusStrip = map (\(u,v) -> [ cos u * (1 + v/2 * cos(u/2))
+                             , sin u * (1 + v/2 * cos(u/2))
+                             , v/2 * sin(u/2)              ]) uv
+  where
+    uv = [(u,v) | u <- u_, v <- v_]
+    u_ = [i/50 * 2 *pi | i <- [0 .. 50]]
+    v_ = [-1,1] --  [i/50 | i <- [-50 .. 50]]
+
+irregularPolyhedron :: [[Double]]
+irregularPolyhedron =
+  [ [ -0.586233 , 0.192482 , -1.9732e-2 ]
+  , [ -0.344233 , 0.301871 , 0.358301 ]
+  , [ 0.344233 , -0.301871 , 0.358301 ]
+  , [ 0.179112 , -0.514398 , -0.290554 ]
+  , [ 1.1612e-2 , -0.564879 , 0.137037 ]
+  , [ -1.1612e-2 , 0.564879 , 0.137037 ]
+  , [ -0.179112 , 0.514398 , -0.290554 ]
+  , [ -0.151548 , -0.185001 , -0.529915 ]
+  , [ -5.5686e-2 , -0.430284 , -0.411612 ]
+  , [ -0.364663 , -0.382652 , -0.277601 ]
+  , [ -0.394163 , -0.151218 , -0.422176 ]
+  , [ 5.5686e-2 , 0.430284 , -0.411612 ]
+  , [ 0.364663 , 0.382652 , -0.277601 ]
+  , [ 0.394163 , 0.151218 , -0.422176 ]
+  , [ 0.151548 , 0.185001 , -0.529915 ]
+  , [ 0.361372 , 0.268614 , 0.393607 ]
+  , [ 0.246149 , 0.511456 , 0.185169 ]
+  , [ 0.417404 , 0.426728 , -1.1876e-2 ]
+  , [ 0.510787 , 0.234322 , 0.148969 ]
+  , [ -0.361372 , -0.268614 , 0.393608 ]
+  , [ -0.510787 , -0.234322 , 0.148969 ]
+  , [ -0.417404 , -0.426728 , -1.1876e-2 ]
+  , [ -0.246149 , -0.511456 , 0.185169 ]
+  , [ 0.430396 , -0.334808 , -0.249032 ]
+  , [ 0.586233 , -0.192482 , -1.9732e-2 ]
+  , [ 0.587567 , -0.188189 , -2.1408e-2 ]
+  , [ 0.509201 , -0.136702 , -0.282307 ]
+  , [ -0.430396 , 0.334808 , -0.249032 ]
+  , [ -0.509201 , 0.136702 , -0.282307 ]
+  , [ -0.587567 , 0.188189 , -2.1408e-2 ]
+  , [ -0.272269 , -0.118712 , 0.520703 ]
+  , [ -0.248355 , 9.5223e-2 , 0.535651 ]
+  , [ 0.272269 , 0.118712 , 0.520703 ]
+  , [ -2.22e-4 , 2.388e-3 , 0.617335 ]
+  , [ 2.22e-4 , -2.388e-3 , 0.617335 ]
+  , [ 0.248355 , -9.5223e-2 , 0.535651 ]
+  ]
+
+truncatedCube :: [[Double]]
+truncatedCube =
+  let x = 1 + sqrt 2 in
+  [[i, j*x, k*x] | i <- [-1,1], j <- [-1,1], k <- [-1,1]] ++
+  [[i*x, j, k*x] | i <- [-1,1], j <- [-1,1], k <- [-1,1]] ++
+  [[i*x, j*x, k] | i <- [-1,1], j <- [-1,1], k <- [-1,1]]
+
+truncatedTesseract :: [[Double]]
+truncatedTesseract =
+  let x = 1 + sqrt 2 in
+  [[i, j*x, k*x, l*x] | i <- [-1,1], j <- [-1,1], k <- [-1,1], l <- [-1,1]] ++
+  [[i*x, j, k*x, l*x] | i <- [-1,1], j <- [-1,1], k <- [-1,1], l <- [-1,1]] ++
+  [[i*x, j*x, k, l*x] | i <- [-1,1], j <- [-1,1], k <- [-1,1], l <- [-1,1]] ++
+  [[i*x, j*x, k*x, l] | i <- [-1,1], j <- [-1,1], k <- [-1,1], l <- [-1,1]]
+
+projectedTruncatedTesseract :: [[Double]]
+projectedTruncatedTesseract =
+  [ [-x1,-x2,-x2],
+    [-x4,-x3,-x3],
+    [-x1,-x2, x2],
+    [-x4,-x3, x3],
+    [-x1, x2,-x2],
+    [-x4, x3,-x3],
+    [-x1, x2, x2],
+    [-x4, x3, x3],
+    [ x1,-x2,-x2],
+    [ x4,-x3,-x3],
+    [ x1,-x2, x2],
+    [ x4,-x3, x3],
+    [ x1, x2,-x2],
+    [ x4, x3,-x3],
+    [ x1, x2, x2],
+    [ x4, x3, x3],
+    [-x2,-x1,-x2],
+    [-x3,-x4,-x3],
+    [-x2,-x1, x2],
+    [-x3,-x4, x3],
+    [-x2, x1,-x2],
+    [-x3, x4,-x3],
+    [-x2, x1, x2],
+    [-x3, x4, x3],
+    [ x2,-x1,-x2],
+    [ x3,-x4,-x3],
+    [ x2,-x1, x2],
+    [ x3,-x4, x3],
+    [ x2, x1,-x2],
+    [ x3, x4,-x3],
+    [ x2, x1, x2],
+    [ x3, x4, x3],
+    [-x2,-x2,-x1],
+    [-x3,-x3,-x4],
+    [-x2,-x2, x1],
+    [-x3,-x3, x4],
+    [-x2, x2,-x1],
+    [-x3, x3,-x4],
+    [-x2, x2, x1],
+    [-x3, x3, x4],
+    [ x2,-x2,-x1],
+    [ x3,-x3,-x4],
+    [ x2,-x2, x1],
+    [ x3,-x3, x4],
+    [ x2, x2,-x1],
+    [ x3, x3,-x4],
+    [ x2, x2, x1],
+    [ x3, x3, x4],
+    [-x6,-x6,-x6],
+    [-x5,-x5,-x5],
+    [-x6,-x6, x6],
+    [-x5,-x5, x5],
+    [-x6, x6,-x6],
+    [-x5, x5,-x5],
+    [-x6, x6, x6],
+    [-x5, x5, x5],
+    [ x6,-x6,-x6],
+    [ x5,-x5,-x5],
+    [ x6,-x6, x6],
+    [ x5,-x5, x5],
+    [ x6, x6,-x6],
+    [ x5, x5,-x5],
+    [ x6, x6, x6],
+    [ x5, x5, x5]]
+    where
+      a = 1 + sqrt 2
+      x1 = 2 / (sqrt(1 + 3*a*a) + a) -- 0.29
+      x2 = 2*a / (sqrt(1 + 3*a*a) + a) -- 0.71
+      x3 = 2*a / (sqrt(1 + 3*a*a) - a) -- 2.56
+      x4 = 2 / (sqrt(1 + 3*a*a) - a) -- 1.06
+      x5 = 2*a / (sqrt(1 + 3*a*a) - 1) -- 1.46
+      x6 = 2*a / (sqrt(1 + 3*a*a) + 1) -- 0.91
+
+rectifiedTesseract :: [[Double]]
+rectifiedTesseract =
+  let x = sqrt 2 in
+  [[0, j*x, k*x, l*x] | j <- [-1,1], k <- [-1,1], l <- [-1,1]] ++
+  [[i*x, 0, k*x, l*x] | i <- [-1,1], k <- [-1,1], l <- [-1,1]] ++
+  [[i*x, j*x, 0, l*x] | i <- [-1,1], j <- [-1,1], l <- [-1,1]] ++
+  [[i*x, j*x, k*x, 0] | i <- [-1,1], j <- [-1,1], k <- [-1,1]]
+
+cantellatedTesseract :: [[Double]]
+cantellatedTesseract =
+  let x = 1 + sqrt 2 in
+  map (map (/ sqrt(2 + 2*x*x))) $
+    [[i, j, k*x, l*x] | i <- [-1,1], j <- [-1,1], k <- [-1,1], l <- [-1,1]] ++
+    [[i*x, j, k, l*x] | i <- [-1,1], j <- [-1,1], k <- [-1,1], l <- [-1,1]] ++
+    [[i*x, j*x, k, l] | i <- [-1,1], j <- [-1,1], k <- [-1,1], l <- [-1,1]] ++
+    [[i, j*x, k, l*x] | i <- [-1,1], j <- [-1,1], k <- [-1,1], l <- [-1,1]] ++
+    [[i, j*x, k*x, l] | i <- [-1,1], j <- [-1,1], k <- [-1,1], l <- [-1,1]] ++
+    [[i*x, j, k*x, l] | i <- [-1,1], j <- [-1,1], k <- [-1,1], l <- [-1,1]]
+
+projectedCantellatedTesseract :: [[Double]]
+projectedCantellatedTesseract = map stereographic cantellatedTesseract
+  where
+    stereographic x = map (/(1-x!!3)) [2 * x!!0, 2 * x!!1, 2 * x!!2]
+
+octaplex :: [[Double]]
+octaplex = map (map (/ sqrt 2)) $
+  [[i,j,0,0] | i <- pm, j <- pm] ++
+  [[i,0,j,0] | i <- pm, j <- pm] ++
+  [[i,0,0,j] | i <- pm, j <- pm] ++
+  [[0,0,i,j] | i <- pm, j <- pm] ++
+  [[0,i,0,j] | i <- pm, j <- pm] ++
+  [[0,i,j,0] | i <- pm, j <- pm]
+  where
+    pm = [-1,1]
+
+
+nonConvexPolyhedron :: [[Double]]
+nonConvexPolyhedron =
+  [[i*x, j*x, k*x] | i <- [-1,1], j <- [-1,1], k <- [-1,1]] ++
+  [[i*y, 0, 0] | i <- [-1,1]] ++
+  [[0, j*y, 0] | j <- [-1,1]] ++
+  [[0, 0, k*y] | k <- [-1,1]]
+  where x = 2.1806973249
+        y = 3.5617820682
+  -- tetrahedra: (i 2.18, j 2.18, k 2.18), (i 3.56, 0, 0), (0, j 3.56, 0), (0, 0, k 3.56)
+
+dodecahedron :: [[Double]] -- it's icosahedron !
+dodecahedron = let phi = (1 + sqrt 5)/2 in
+               [[0,i,j] | i <- [-1,1], j <- [-phi, phi]] ++
+               [[j,0,i] | i <- [-1,1], j <- [-phi, phi]] ++
+               [[i,j,0] | i <- [-1,1], j <- [-phi, phi]]
+
+icosahedron :: [[Double]]
+icosahedron = dodecahedron
+
+duoprism330 :: [[Double]]
+duoprism330 = [a ++ b | a <- triangle, b <- p30]
+  where
+    triangle = [[cos (realToFrac i * 2*pi/3), sin (realToFrac i * 2*pi/3)] | i <- [0 .. 2]]
+    p30 = [[cos (realToFrac i * 2*pi/30), sin (realToFrac i * 2*pi/30)] | i <- [0 .. 29]]
+
+
+duoprism35 :: [[Double]]
+duoprism35 = [a ++ b | a <- triangle, b <- pentagon]
+  where
+    triangle = [ [sqrt 3 / 2 ,  0.5]
+               , [-sqrt 3 / 2,  0.5]
+               , [0          , -1  ] ]
+    pentagon = [ [1          , 0          ]
+               , [cos(2*pi/5), sin(2*pi/5)]
+               , [cos(4*pi/5), sin(4*pi/5)]
+               , [cos(6*pi/5), sin(6*pi/5)]
+               , [cos(8*pi/5), sin(8*pi/5)] ]
+
+duoprism34 :: [[Double]]
+duoprism34 = [a ++ b | a <- triangle, b <- square]
+  where
+    triangle = [ [sqrt 3 / 2 ,  0.5]
+               , [-sqrt 3 / 2,  0.5]
+               , [0          , -1  ] ]
+    square = [[i * sqrt 2 / 2, j * sqrt 2 / 2] | i <- [-1,1], j <- [-1,1]]
+
+triangularDuoprism :: [[Double]]
+triangularDuoprism = [a ++ b | a <- triangle, b <- triangle]
+  where
+    triangle = [ [sqrt 3 / 2 ,  0.5]
+               , [-sqrt 3 / 2,  0.5]
+               , [0          , -1  ] ]
+
+hexagonalDuoprism :: [[Double]]
+hexagonalDuoprism = [a ++ b | a <- hexagon, b <- hexagon]
+  where
+    hexagon = [ [sqrt 3 / 2,   0.5]
+              , [0         ,   1  ]
+              , [-sqrt 3 / 2,  0.5]
+              , [-sqrt 3 / 2, -0.5]
+              , [0          , -1  ]
+              , [sqrt 3 / 2 , -0.5] ]
+
+projectedHexagonalDuoprims :: [[Double]]
+projectedHexagonalDuoprims =
+    [ [ 1.8945800837529239 , 1.0938363213560542 , 1.8945800837529239 ]
+    , [ 4.181540550352056 , 2.414213562373096 , 0.0 ]
+    , [ 1.8945800837529239 , 1.0938363213560542 , -1.8945800837529239 ]
+    , [ 0.904836765142137 , 0.522407749927483 , -0.904836765142137 ]
+    , [ 0.7174389352143009 , 0.4142135623730951 , 0.0 ]
+    , [ 0.904836765142137 , 0.522407749927483 , 0.904836765142137 ]
+    , [ 0.0 , 2.1876726427121085 , 1.8945800837529239 ]
+    , [ 0.0 , 4.828427124746192 , 0.0 ]
+    , [ 0.0 , 2.1876726427121085 , -1.8945800837529239 ]
+    , [ 0.0 , 1.044815499854966 , -0.904836765142137 ]
+    , [ 0.0 , 0.8284271247461902 , 0.0 ]
+    , [ 0.0 , 1.044815499854966 , 0.904836765142137 ]
+    , [ -1.8945800837529239 , 1.0938363213560542 , 1.8945800837529239 ]
+    , [ -4.181540550352056 , 2.414213562373096 , 0.0 ]
+    , [ -1.8945800837529239
+      , 1.0938363213560542
+      , -1.8945800837529239
+      ]
+    , [ -0.904836765142137 , 0.522407749927483 , -0.904836765142137 ]
+    , [ -0.7174389352143009 , 0.4142135623730951 , 0.0 ]
+    , [ -0.904836765142137 , 0.522407749927483 , 0.904836765142137 ]
+    , [ -1.8945800837529239
+      , -1.0938363213560542
+      , 1.8945800837529239
+      ]
+    , [ -4.181540550352056 , -2.414213562373096 , 0.0 ]
+    , [ -1.8945800837529239
+      , -1.0938363213560542
+      , -1.8945800837529239
+      ]
+    , [ -0.904836765142137 , -0.522407749927483 , -0.904836765142137 ]
+    , [ -0.7174389352143009 , -0.4142135623730951 , 0.0 ]
+    , [ -0.904836765142137 , -0.522407749927483 , 0.904836765142137 ]
+    , [ 0.0 , -2.1876726427121085 , 1.8945800837529239 ]
+    , [ 0.0 , -4.828427124746192 , 0.0 ]
+    , [ 0.0 , -2.1876726427121085 , -1.8945800837529239 ]
+    , [ 0.0 , -1.044815499854966 , -0.904836765142137 ]
+    , [ 0.0 , -0.8284271247461902 , 0.0 ]
+    , [ 0.0 , -1.044815499854966 , 0.904836765142137 ]
+    , [ 1.8945800837529239 , -1.0938363213560542 , 1.8945800837529239 ]
+    , [ 4.181540550352056 , -2.414213562373096 , 0.0 ]
+    , [ 1.8945800837529239
+      , -1.0938363213560542
+      , -1.8945800837529239
+      ]
+    , [ 0.904836765142137 , -0.522407749927483 , -0.904836765142137 ]
+    , [ 0.7174389352143009 , -0.4142135623730951 , 0.0 ]
+    , [ 0.904836765142137 , -0.522407749927483 , 0.904836765142137 ]
+    ]
+
+hexaSquare :: [[Double]]
+hexaSquare = [a ++ b | a <- hexagon, b <- square]
+  where
+    hexagon = [ [sqrt 3 / 2,   0.5]
+              , [0         ,   1  ]
+              , [-sqrt 3 / 2,  0.5]
+              , [-sqrt 3 / 2, -0.5]
+              , [0          , -1  ]
+              , [sqrt 3 / 2 , -0.5] ]
+    square = [[i * sqrt 2 / 2, j * sqrt 2 / 2] | i <- [-1,1], j <- [-1,1]]
+
+cubinder :: [[Double]]
+cubinder = [a ++ b | a <- circle, b <- square]
+  where
+    circle = [[cos (realToFrac i * 2*pi/30), sin (realToFrac i * 2*pi/30)] | i <- [0 .. 29]]
+    square = [[i * sqrt 2 / 2, j * sqrt 2 / 2] | i <- [-1,1], j <- [-1,1]]
+
+duoprism1616 :: [[Double]]
+duoprism1616 = [a ++ b | a <- p16, b <- p16]
+  where
+    p16 = [[cos (realToFrac i * 2*pi/16), sin (realToFrac i * 2*pi/16)] | i <- [0 .. 15]]
+
+duocylinder :: [[Double]]
+duocylinder = [a ++ b | a <- circle, b <- circle]
+  where
+    circle = [[cos (realToFrac i * 2*pi/30), sin (realToFrac i * 2*pi/30)] | i <- [0 .. 29]]
+
+reuleuxTetrahedron :: [[Double]]
+reuleuxTetrahedron =
+  [ [0.5 / sqrt 3, -0.5, 0.5 / sqrt 6]
+  , [sqrt 3 / 3, 0, -0.5 / sqrt 6]
+  , [0.5 / sqrt 3, 0.5, -0.5 / sqrt 6]
+  , [0, 0, 0.5 * sqrt 3 / sqrt 2] ]
+
+
+hexacosichoron :: [[Double]]
+hexacosichoron =
+  [[i*0.5, j*0.5, k*0.5, l*0.5] | i <- pm, j <- pm, k <- pm, l <-pm] ++
+  [[0, 0, 0, i] | i <- pm] ++
+  [[0, 0, i, 0] | i <- pm] ++
+  [[0, i, 0, 0] | i <- pm] ++
+  [[i, 0, 0, 0] | i <- pm] ++
+  [ permuteList p [phi/2, 1/2, 1/2/phi, 0]    | p <- permutations4, isEvenPermutation p] ++
+  [ permuteList p [phi/2, 1/2, -1/2/phi, 0]   |  p <- permutations4, isEvenPermutation p] ++
+  [ permuteList p [phi/2, -1/2, 1/2/phi, 0]   |  p <- permutations4, isEvenPermutation p] ++
+  [ permuteList p [phi/2, -1/2, -1/2/phi, 0]  |  p <- permutations4, isEvenPermutation p] ++
+  [ permuteList p [-phi/2, 1/2, 1/2/phi, 0]  |  p <- permutations4, isEvenPermutation p] ++
+  [ permuteList p [-phi/2, 1/2, -1/2/phi, 0] |  p <- permutations4, isEvenPermutation p] ++
+  [ permuteList p [-phi/2, -1/2, 1/2/phi, 0]  |  p <- permutations4, isEvenPermutation p] ++
+  [ permuteList p [-phi/2, -1/2, -1/2/phi, 0]   |  p <- permutations4, isEvenPermutation p]
+  where
+    permutations4 = permutations 4
+    phi = (1 + sqrt 5) / 2
+    pm = [-1,1]
+
+snub24cell :: [[Double]]
+snub24cell =
+  [ permuteList p [0, i, i'*phi, i''*phi*phi] | i <- pm, i' <- pm, i'' <- pm, p <- permutations4, isEvenPermutation p]
+  where
+    permutations4 = permutations 4
+    phi = (1 + sqrt 5) / 2
+    pm = [-1,1]
+
+dodecaplex :: [[Double]]
+dodecaplex = nub $
+  [permuteList p [0.0, 0.0, i, j] | i <- pm2 , j <- pm2,  p <- perms4] ++
+  [permuteList p [i, j, k, l] | i <- pm, j <- pm, k <-pm, l <- pmsqrt5, p <- perms4] ++
+  [permuteList p [i ,j ,k ,l] | i <- pmphipowminus2, j <- pmphi, k <- pmphi, l <- pmphi, p <- perms4] ++
+  [permuteList p [i ,j ,k ,l] | i <- pmphipowminus1, j <- pmphipowminus1, k <- pmphipowminus1, l <- pmphipow2, p <- perms4] ++
+  [permuteList p [0, i, j, k] | i <- pmphipowminus2, j <- pm, k <- pmphipow2, p <- perms4, isEvenPermutation p] ++
+  [permuteList p [0, i, j, k] | i <- pmphipowminus1, j <- pmphi, k <- pmsqrt5, p <- perms4, isEvenPermutation p] ++
+  [permuteList p [i, j, k, l] | i <- pmphipowminus1, j <- pm, k <- pmphi, l <- pm2, p <- perms4, isEvenPermutation p]
+  where
+    pm = [-1.0,1.0]
+    pm2 = [-2, 2]
+    perms4 = permutations 4
+    pmsqrt5 = [-sqrt 5, sqrt 5]
+    pmphipowminus2 = [-1/phi/phi, 1/phi/phi]
+    pmphipow2 = [-phi*phi, phi*phi]
+    phi = (1 + sqrt 5) / 2
+--    pmphiminus1 = phi-1
+    pmphi = [phi, -phi]
+    pmphipowminus1 = [-1/phi, 1/phi]
+
+spheresPack :: [[Double]]
+spheresPack = [ [2,1,1], [4,1,1]
+              , [1, 1 + sqrt 3, 1], [3, 1 + sqrt 3, 1], [5, 1 + sqrt 3, 1]
+              , [2, 1 + 2*sqrt 3, 1], [4, 1 + 2*sqrt 3, 1]
+              , [3, 1 + sqrt 3 / 3, 1 + 2*sqrt 6 / 3]
+              , [2, 1 + 4*sqrt 3 / 3, 1 + 2*sqrt 6 / 3], [4, 1 + 4*sqrt 3 / 3, 1 + 2*sqrt 6 / 3]
+              , [3, 1 + sqrt 3, 1 + 4*sqrt 6 /3]
+              , [3, 1 + sqrt 3 / 3, 1 - 2*sqrt 6 / 3]
+              , [2, 1 + 4*sqrt 3 / 3, 1 - 2*sqrt 6 / 3], [4, 1 + 4*sqrt 3 / 3, 1 - 2*sqrt 6 / 3]
+              , [3, 1 + sqrt 3, 1 - 4*sqrt 6 /3] ]
+
+
+randomInCircle :: Int -> IO [[Double]]
+randomInCircle n = do
+  g1 <- newStdGen
+  let theta = map (*(2*pi)) (take n (randoms g1 :: [Double]))
+  g2 <- newStdGen
+  let rho   = map (/2) (take n (randoms g2 :: [Double]))
+  return $ zipWith (\r a  -> [(r+0.5) * cos a, (r+0.5) * sin a]) rho theta
+
+randomInSquare :: Int -> IO [[Double]]
+randomInSquare n = do
+  g <- newStdGen
+  return $ chunksOf 2 (take (2*n) (randoms g :: [Double]))
+
+
+randomInSphere :: Int -> IO [[Double]]
+randomInSphere n = do
+  g1 <- newStdGen
+  let theta = map (*(2*pi)) (take n (randoms g1 :: [Double]))
+  g2 <- newStdGen
+  let phi   = map (*pi) (take n (randoms g2 :: [Double]))
+  g3 <- newStdGen
+  let rho   = take n (randoms g3 :: [Double])
+  return $ zipWith3 (\r a b -> [r * cos a * sin b,
+                                r * sin a * sin b,
+                                r * cos b         ])
+                     rho theta phi
+
+regularSphere :: Int -> [[Double]]
+regularSphere n =
+  concatMap (\a -> map (s2c a) phi) theta
+  where
+  theta = map (*(2*pi)) [frac i n | i <- [0 .. n-1]]
+  phi = map (*pi) [frac i n | i <- [1 .. n-1]]
+  frac :: Int -> Int -> Double
+  frac p q = realToFrac p / realToFrac q
+  s2c :: Double -> Double -> [Double]
+  s2c th ph = [cos th * sin ph, sin th * sin ph, cos ph]
+
+randomOnSphere :: Int -> Double -> IO [[Double]]
+randomOnSphere n r = do
+  g <- newStdGen
+  let x = take (2*n) (randoms g :: [Double])
+  let u_ = map (*(2*pi)) (take n x)
+  let v_ = drop n x
+  return $ zipWith (\u v -> [r * cos u * sin (acos (2*v-1)),
+                             r * sin u * sin (acos (2*v-1)),
+                             r * (2*v-1)                  ]) u_ v_
+
+randomInCube :: Int -> IO [[Double]]
+randomInCube n = do
+  g <- newStdGen
+  return $ chunksOf 3 (take (3*n) (randoms g :: [Double]))
+
+randomOnTorus :: Int -> Double -> Double -> IO [[Double]]
+randomOnTorus n c a = do
+  g <- newStdGen
+  let x = take (2*n) (randoms g :: [Double])
+  let u_ = map (*(2*pi)) (take n x)
+  let v_ = map (*(2*pi)) (drop n x)
+  return $ zipWith (\u v -> [cos u * (c + a * cos v),
+                             sin u * (c + a * cos v),
+                             a * sin v              ]) u_ v_
+
+qcube1,qcube2,qcube3,qcube4,qcube5 :: [[Double]]
+qcube1 =
+  [ [ 0.0 , 0.6180339887498949 , 1.618033988749895 ]
+  , [ -0.6180339887498949 , 1.618033988749895 , 0.0 ]
+  , [ 1.618033988749895 , 0.0 , 0.6180339887498949 ]
+  , [ 1.0 , 1.0 , -1.0 ]
+  , [ 0.6180339887498949 , -1.618033988749895 , 0.0 ]
+  , [ -1.0 , -1.0 , 1.0 ]
+  , [ -1.618033988749895 , 0.0 , -0.6180339887498949 ]
+  , [ 0.0 , -0.6180339887498949 , -1.618033988749895 ]
+  ]
+qcube2 =
+  [ [ -1.0 , 1.0 , 1.0 ]
+  , [ 0.6180339887498949 , 1.618033988749895 , 0.0 ]
+  , [ 0.0 , 0.6180339887498949 , -1.618033988749895 ]
+  , [ 0.0 , -0.6180339887498949 , 1.618033988749895 ]
+  , [ 1.618033988749895 , 0.0 , 0.6180339887498949 ]
+  , [ 1.0 , -1.0 , -1.0 ]
+  , [ -1.618033988749895 , 0.0 , -0.6180339887498949 ]
+  , [ -0.6180339887498949 , -1.618033988749895 , 0.0 ]
+  ]
+qcube3 =
+  [ [ -0.6180339887498949 , 1.618033988749895 , 0.0 ]
+  , [ 0.0 , 0.6180339887498949 , -1.618033988749895 ]
+  , [ 0.0 , -0.6180339887498949 , 1.618033988749895 ]
+  , [ 1.0 , 1.0 , 1.0 ]
+  , [ 1.618033988749895 , 0.0 , -0.6180339887498949 ]
+  , [ 0.6180339887498949 , -1.618033988749895 , 0.0 ]
+  , [ -1.618033988749895 , 0.0 , 0.6180339887498949 ]
+  , [ -1.0 , -1.0 , -1.0 ]
+  ]
+qcube4 =
+  [ [ -1.0 , 1.0 , 1.0 ]
+  , [ 1.0 , -1.0 , 1.0 ]
+  , [ 1.0 , 1.0 , 1.0 ]
+  , [ 1.0 , 1.0 , -1.0 ]
+  , [ 1.0 , -1.0 , -1.0 ]
+  , [ -1.0 , -1.0 , 1.0 ]
+  , [ -1.0 , 1.0 , -1.0 ]
+  , [ -1.0 , -1.0 , -1.0 ]
+  ]
+qcube5 =
+  [ [ 0.0 , 0.6180339887498949 , 1.618033988749895 ]
+  , [ 0.6180339887498949 , 1.618033988749895 , 0.0 ]
+  , [ 1.0 , -1.0 , 1.0 ]
+  , [ 1.618033988749895 , 0.0 , -0.6180339887498949 ]
+  , [ -1.618033988749895 , 0.0 , 0.6180339887498949 ]
+  , [ -1.0 , 1.0 , -1.0 ]
+  , [ -0.6180339887498949 , -1.618033988749895 , 0.0 ]
+  , [ 0.0 , -0.6180339887498949 , -1.618033988749895 ]
+  ]
+
+teapot :: [[Double]]
+teapot =
+  [ [-3, 1.64999997615814, 0]
+  , [-2.98710989952087, 1.64999997615814, -0.0984380021691322]
+  , [-2.98710989952087, 1.64999997615814, 0.0984380021691322]
+  , [-2.98537993431091, 1.56731998920441, -0.0492190010845661]
+  , [-2.98537993431091, 1.56731998920441, 0.0492190010845661]
+  , [-2.9835000038147, 1.48308002948761, 0]
+  , [-2.98188996315002, 1.72346997261047, -0.0492190010845661]
+  , [-2.98188996315002, 1.72346997261047, 0.0492190010845661]
+  , [-2.97656011581421, 1.79852998256683, 0]
+  , [-2.97090005874634, 1.48620998859406, -0.0984380021691322]
+  , [-2.97090005874634, 1.48620998859406, 0.0984380021691322]
+  , [-2.96388006210327, 1.79533994197845, -0.0984380021691322]
+  , [-2.96388006210327, 1.79533994197845, 0.0984380021691322]
+  , [-2.96220993995667, 1.57017004489899, -0.133594006299973]
+  , [-2.96220993995667, 1.57017004489899, 0.133594006299973]
+  , [-2.95864009857178, 1.72056996822357, -0.133594006299973]
+  , [-2.95864009857178, 1.72056996822357, 0.133594006299973]
+  , [-2.95313000679016, 1.64999997615814, -0.168750002980232]
+  , [-2.95313000679016, 1.64999997615814, 0.168750002980232]
+  , [-2.95247006416321, 1.40374004840851, -0.0492190010845661]
+  , [-2.95247006416321, 1.40374004840851, 0.0492190010845661]
+  , [-2.93770003318787, 1.49447000026703, -0.168750002980232]
+  , [-2.93770003318787, 1.49447000026703, 0.168750002980232]
+  , [-2.93523001670837, 1.85214996337891, -0.0492190010845661]
+  , [-2.93523001670837, 1.85214996337891, 0.0492190010845661]
+  , [-2.93358993530273, 1.32011997699738, 0]
+  , [-2.93044996261597, 1.78692996501923, -0.168750002980232]
+  , [-2.93044996261597, 1.78692996501923, 0.168750002980232]
+  , [-2.93037009239197, 1.41149997711182, -0.133594006299973]
+  , [-2.93037009239197, 1.41149997711182, 0.133594006299973]
+  , [-2.92188000679016, 1.32553005218506, -0.0984380021691322]
+  , [-2.92188000679016, 1.32553005218506, 0.0984380021691322]
+  , [-2.91278004646301, 1.84416997432709, -0.133594006299973]
+  , [-2.91278004646301, 1.84416997432709, 0.133594006299973]
+  , [-2.90625, 1.91015994548798, 0]
+  , [-2.89422988891602, 1.90456998348236, -0.0984380021691322]
+  , [-2.89422988891602, 1.90456998348236, 0.0984380021691322]
+  , [-2.89138007164001, 1.57910001277924, -0.196875005960464]
+  , [-2.89138007164001, 1.57910001277924, 0.196875005960464]
+  , [-2.8909900188446, 1.33980000019073, -0.168750002980232]
+  , [-2.8909900188446, 1.33980000019073, 0.168750002980232]
+  , [-2.89065003395081, 1.71208000183105, -0.196875005960464]
+  , [-2.89065003395081, 1.71208000183105, 0.196875005960464]
+  , [-2.88346004486084, 1.24579000473022, -0.0483429990708828]
+  , [-2.88346004486084, 1.24579000473022, 0.0483429990708828]
+  , [-2.86346006393433, 1.25713002681732, -0.132717996835709]
+  , [-2.86346006393433, 1.25713002681732, 0.132717996835709]
+  , [-2.86265993118286, 1.43482995033264, -0.196875005960464]
+  , [-2.86265993118286, 1.43482995033264, 0.196875005960464]
+  , [-2.8625500202179, 1.88982999324799, -0.168750002980232]
+  , [-2.8625500202179, 1.88982999324799, 0.168750002980232]
+  , [-2.84999990463257, 1.64999997615814, -0.224999994039536]
+  , [-2.84999990463257, 1.64999997615814, 0.224999994039536]
+  , [-2.84970998764038, 1.16155004501343, 0]
+  , [-2.84710001945496, 1.82081997394562, -0.196875005960464]
+  , [-2.84710001945496, 1.82081997394562, 0.196875005960464]
+  , [-2.84193992614746, 1.94692003726959, -0.0492190010845661]
+  , [-2.84193992614746, 1.94692003726959, 0.0492190010845661]
+  , [-2.8289999961853, 1.76139998435974, -0.224999994039536]
+  , [-2.8289999961853, 1.76139998435974, 0.224999994039536]
+  , [-2.82867002487183, 1.17597997188568, -0.0949330031871796]
+  , [-2.82867002487183, 1.17597997188568, 0.0949330031871796]
+  , [-2.82470011711121, 1.52193999290466, -0.224999994039536]
+  , [-2.82470011711121, 1.52193999290466, 0.224999994039536]
+  , [-2.82115006446838, 1.93519997596741, -0.133594006299973]
+  , [-2.82115006446838, 1.93519997596741, 0.133594006299973]
+  , [-2.81230998039246, 1.18719005584717, -0.168750002980232]
+  , [-2.81230998039246, 1.18719005584717, 0.168750002980232]
+  , [-2.80501008033752, 1.28997004032135, -0.196875005960464]
+  , [-2.80501008033752, 1.28997004032135, 0.196875005960464]
+  , [-2.79727005958557, 1.38311004638672, -0.224999994039536]
+  , [-2.79727005958557, 1.38311004638672, 0.224999994039536]
+  , [-2.78906011581421, 1.99013996124268, 0]
+  , [-2.78836011886597, 1.69931995868683, -0.196875005960464]
+  , [-2.78836011886597, 1.69931995868683, 0.196875005960464]
+  , [-2.77820992469788, 1.98283004760742, -0.0984380021691322]
+  , [-2.77820992469788, 1.98283004760742, 0.0984380021691322]
+  , [-2.77442002296448, 1.52737998962402, -0.196875005960464]
+  , [-2.77442002296448, 1.52737998962402, 0.196875005960464]
+  , [-2.77356004714966, 1.09860002994537, -0.0843750014901161]
+  , [-2.77356004714966, 1.09860002994537, 0.0843750014901161]
+  , [-2.76641011238098, 1.84511995315552, -0.224999994039536]
+  , [-2.76641011238098, 1.84511995315552, 0.224999994039536]
+  , [-2.76033997535706, 1.90090000629425, -0.196875005960464]
+  , [-2.76033997535706, 1.90090000629425, 0.196875005960464]
+  , [-2.74959993362427, 1.96355998516083, -0.168750002980232]
+  , [-2.74959993362427, 1.96355998516083, 0.168750002980232]
+  , [-2.74831008911133, 1.78569996356964, -0.196875005960464]
+  , [-2.74831008911133, 1.78569996356964, 0.196875005960464]
+  , [-2.74688005447388, 1.64999997615814, -0.168750002980232]
+  , [-2.74688005447388, 1.64999997615814, 0.168750002980232]
+  , [-2.73125004768372, 1.00780999660492, 0]
+  , [-2.72756004333496, 1.73587000370026, -0.168750002980232]
+  , [-2.72756004333496, 1.73587000370026, 0.168750002980232]
+  , [-2.72036004066467, 1.69082999229431, -0.133594006299973]
+  , [-2.72036004066467, 1.69082999229431, 0.133594006299973]
+  , [-2.71948003768921, 1.24977004528046, -0.224999994039536]
+  , [-2.71948003768921, 1.24977004528046, 0.224999994039536]
+  , [-2.71677994728088, 1.14468002319336, -0.196875005960464]
+  , [-2.71677994728088, 1.14468002319336, 0.196875005960464]
+  , [-2.71288990974426, 1.64999997615814, -0.0984380021691322]
+  , [-2.71288990974426, 1.64999997615814, 0.0984380021691322]
+  , [-2.7089900970459, 1.54176998138428, -0.133594006299973]
+  , [-2.7089900970459, 1.54176998138428, 0.133594006299973]
+  , [-2.70354008674622, 1.42640995979309, -0.168750002980232]
+  , [-2.70354008674622, 1.42640995979309, 0.168750002980232]
+  , [-2.70097994804382, 1.03784000873566, -0.168750002980232]
+  , [-2.70097994804382, 1.03784000873566, 0.168750002980232]
+  , [-2.70000004768372, 1.64999997615814, 0]
+  , [-2.69965004920959, 2.0107901096344, -0.0483460016548634]
+  , [-2.69965004920959, 2.0107901096344, 0.0483460016548634]
+  , [-2.69711995124817, 1.68792998790741, -0.0492190010845661]
+  , [-2.69711995124817, 1.68792998790741, 0.0492190010845661]
+  , [-2.69412994384766, 1.72746002674103, -0.0984380021691322]
+  , [-2.69412994384766, 1.72746002674103, 0.0984380021691322]
+  , [-2.68661999702454, 1.54668998718262, -0.0492190010845661]
+  , [-2.68661999702454, 1.54668998718262, 0.0492190010845661]
+  , [-2.68263006210327, 1.76234996318817, -0.133594006299973]
+  , [-2.68263006210327, 1.76234996318817, 0.133594006299973]
+  , [-2.68147993087769, 1.9964599609375, -0.13272100687027]
+  , [-2.68147993087769, 1.9964599609375, 0.13272100687027]
+  , [-2.68144011497498, 1.72426998615265, 0]
+  , [-2.67574000358582, 1.27084994316101, -0.196875005960464]
+  , [-2.67574000358582, 1.27084994316101, 0.196875005960464]
+  , [-2.67265009880066, 1.44068002700806, -0.0984380021691322]
+  , [-2.67265009880066, 1.44068002700806, 0.0984380021691322]
+  , [-2.67025995254517, 1.80040001869202, -0.168750002980232]
+  , [-2.67025995254517, 1.80040001869202, 0.168750002980232]
+  , [-2.667799949646, 1.84623003005981, -0.196875005960464]
+  , [-2.667799949646, 1.84623003005981, 0.196875005960464]
+  , [-2.66279006004333, 1.9050999879837, -0.224999994039536]
+  , [-2.66279006004333, 1.9050999879837, 0.224999994039536]
+  , [-2.66093993186951, 1.44608998298645, 0]
+  , [-2.66018009185791, 1.75436997413635, -0.0492190010845661]
+  , [-2.66018009185791, 1.75436997413635, 0.0492190010845661]
+  , [-2.63858008384705, 1.78567004203796, -0.0984380021691322]
+  , [-2.63858008384705, 1.78567004203796, 0.0984380021691322]
+  , [-2.63438010215759, 1.10390996932983, -0.224999994039536]
+  , [-2.63438010215759, 1.10390996932983, 0.224999994039536]
+  , [-2.63073992729187, 1.95674002170563, -0.196875005960464]
+  , [-2.63073992729187, 1.95674002170563, 0.196875005960464]
+  , [-2.62655997276306, 1.78007996082306, 0]
+  , [-2.625, 2.04375004768372, 0]
+  , [-2.62463998794556, 1.30501997470856, -0.132813006639481]
+  , [-2.62463998794556, 1.30501997470856, 0.132813006639481]
+  , [-2.60642004013062, 1.31745004653931, -0.0484380014240742]
+  , [-2.60642004013062, 1.31745004653931, 0.0484380014240742]
+  , [-2.60631990432739, 2.02643990516663, -0.0949449986219406]
+  , [-2.60631990432739, 2.02643990516663, 0.0949449986219406]
+  , [-2.59179997444153, 2.01298999786377, -0.168750002980232]
+  , [-2.59179997444153, 2.01298999786377, 0.168750002980232]
+  , [-2.57172989845276, 1.83429002761841, -0.168750002980232]
+  , [-2.57172989845276, 1.83429002761841, 0.168750002980232]
+  , [-2.56777000427246, 1.16997003555298, -0.168750002980232]
+  , [-2.56777000427246, 1.16997003555298, 0.168750002980232]
+  , [-2.55460000038147, 1.18304002285004, -0.0953150019049644]
+  , [-2.55460000038147, 1.18304002285004, 0.0953150019049644]
+  , [-2.54975008964539, 1.89058995246887, -0.196875005960464]
+  , [-2.54975008964539, 1.89058995246887, 0.196875005960464]
+  , [-2.5495400428772, 0.878983974456787, -0.0843750014901161]
+  , [-2.5495400428772, 0.878983974456787, 0.0843750014901161]
+  , [-2.5464301109314, 1.83196997642517, -0.13272100687027]
+  , [-2.5464301109314, 1.83196997642517, 0.13272100687027]
+  , [-2.53749990463257, 1.20000004768372, 0]
+  , [-2.52720999717712, 1.81920003890991, -0.0483460016548634]
+  , [-2.52720999717712, 1.81920003890991, 0.0483460016548634]
+  , [-2.51874995231628, 1.94530999660492, -0.224999994039536]
+  , [-2.51874995231628, 1.94530999660492, 0.224999994039536]
+  , [-2.51682996749878, 0.932671010494232, -0.196875005960464]
+  , [-2.51682996749878, 0.932671010494232, 0.196875005960464]
+  , [-2.47183990478516, 1.00648999214172, -0.196875005960464]
+  , [-2.47183990478516, 1.00648999214172, 0.196875005960464]
+  , [-2.44569993019104, 1.87764000892639, -0.168750002980232]
+  , [-2.44569993019104, 1.87764000892639, 0.168750002980232]
+  , [-2.43913006782532, 1.06017994880676, -0.0843750014901161]
+  , [-2.43913006782532, 1.06017994880676, 0.0843750014901161]
+  , [-2.43118000030518, 1.86417996883392, -0.0949449986219406]
+  , [-2.43118000030518, 1.86417996883392, 0.0949449986219406]
+  , [-2.41249990463257, 1.84686994552612, 0]
+  , [-2.38827991485596, 0.716602027416229, 0]
+  , [-2.3822500705719, 0.737662971019745, -0.0958539992570877]
+  , [-2.3822500705719, 0.737662971019745, 0.0958539992570877]
+  , [-2.37883996963501, 2.05202007293701, -0.0843750014901161]
+  , [-2.37883996963501, 2.05202007293701, 0.0843750014901161]
+  , [-2.37766003608704, 0.753679990768433, -0.168750002980232]
+  , [-2.37766003608704, 0.753679990768433, 0.168750002980232]
+  , [-2.36474990844727, 0.798761010169983, -0.199836000800133]
+  , [-2.36474990844727, 0.798761010169983, 0.199836000800133]
+  , [-2.35430002212524, 0.835254013538361, -0.224999994039536]
+  , [-2.35430002212524, 0.835254013538361, 0.224999994039536]
+  , [-2.34383988380432, 0.871747016906738, -0.199836000800133]
+  , [-2.34383988380432, 0.871747016906738, 0.199836000800133]
+  , [-2.3411500453949, 1.99971997737885, -0.196875005960464]
+  , [-2.3411500453949, 1.99971997737885, 0.196875005960464]
+  , [-2.33092999458313, 0.916827023029327, -0.168750002980232]
+  , [-2.33092999458313, 0.916827023029327, 0.168750002980232]
+  , [-2.32031011581421, 0.953905999660492, 0]
+  , [-2.28931999206543, 1.9278199672699, -0.196875005960464]
+  , [-2.28931999206543, 1.9278199672699, 0.196875005960464]
+  , [-2.251620054245, 1.87551999092102, -0.0843750014901161]
+  , [-2.251620054245, 1.87551999092102, 0.0843750014901161]
+  , [-2.24741005897522, 0.882284998893738, -0.0843750014901161]
+  , [-2.24741005897522, 0.882284998893738, 0.0843750014901161]
+  , [-2.17362999916077, 0.844043016433716, 0]
+  , [-2.16852998733521, 0.826951026916504, -0.0971840023994446]
+  , [-2.16852998733521, 0.826951026916504, 0.0971840023994446]
+  , [-2.16476988792419, 0.814364016056061, -0.168750002980232]
+  , [-2.16476988792419, 0.814364016056061, 0.168750002980232]
+  , [-2.15687990188599, 0.78669399023056, -0.187068000435829]
+  , [-2.15687990188599, 0.78669399023056, 0.187068000435829]
+  , [-2.15625, 2.09296989440918, 0]
+  , [-2.15411996841431, 0.740520000457764, -0.215193003416061]
+  , [-2.15411996841431, 0.740520000457764, 0.215193003416061]
+  , [-2.15017008781433, 0.69473397731781, -0.215193003416061]
+  , [-2.15017008781433, 0.69473397731781, 0.215193003416061]
+  , [-2.14741992950439, 0.648559987545013, -0.187068000435829]
+  , [-2.14741992950439, 0.648559987545013, 0.187068000435829]
+  , [-2.14495992660522, 0.6127769947052, -0.132947996258736]
+  , [-2.14495992660522, 0.6127769947052, 0.132947996258736]
+  , [-2.143709897995, 0.59178900718689, -0.0485729984939098]
+  , [-2.143709897995, 0.59178900718689, 0.0485729984939098]
+  , [-2.14232993125916, 2.05836009979248, -0.168750002980232]
+  , [-2.14232993125916, 2.05836009979248, 0.168750002980232]
+  , [-2.11172008514404, 1.98222994804382, -0.224999994039536]
+  , [-2.11172008514404, 1.98222994804382, 0.224999994039536]
+  , [-2.08447003364563, 0.789525985717773, -0.0489050000905991]
+  , [-2.08447003364563, 0.789525985717773, 0.0489050000905991]
+  , [-2.08109998703003, 1.90609002113342, -0.168750002980232]
+  , [-2.08109998703003, 1.90609002113342, 0.168750002980232]
+  , [-2.07834005355835, 0.77038699388504, -0.133279994130135]
+  , [-2.07834005355835, 0.77038699388504, 0.133279994130135]
+  , [-2.06718993186951, 1.87147998809814, 0]
+  , [-2, 0.75, 0]
+  , [-1.99570000171661, 0.737109005451202, -0.0984380021691322]
+  , [-1.99570000171661, 0.737109005451202, 0.0984380021691322]
+  , [-1.98438000679016, 0.703125, -0.168750002980232]
+  , [-1.98438000679016, 0.703125, 0.168750002980232]
+  , [-1.97852003574371, 0.591650009155273, 0]
+  , [-1.96937000751495, 0.670825004577637, -0.202656000852585]
+  , [-1.96937000751495, 0.670825004577637, 0.202656000852585]
+  , [-1.96835994720459, 0.655077993869781, -0.210938006639481]
+  , [-1.96835994720459, 0.655077993869781, 0.210938006639481]
+  , [-1.96000003814697, 0.75, -0.407499998807907]
+  , [-1.96000003814697, 0.75, 0.407499998807907]
+  , [-1.9587299823761, 0.925194978713989, -0.201561003923416]
+  , [-1.9587299823761, 0.925194978713989, 0.201561003923416]
+  , [-1.9570300579071, 1.10038995742798, 0]
+  , [-1.95000004768372, 0.600000023841858, -0.224999994039536]
+  , [-1.95000004768372, 0.600000023841858, 0.224999994039536]
+  , [-1.93894994258881, 0.591650009155273, -0.403122991323471]
+  , [-1.93894994258881, 0.591650009155273, 0.403122991323471]
+  , [-1.93164002895355, 0.54492199420929, -0.210938006639481]
+  , [-1.93164002895355, 0.54492199420929, 0.210938006639481]
+  , [-1.93069005012512, 0.5225830078125, -0.198676005005836]
+  , [-1.93069005012512, 0.5225830078125, 0.198676005005836]
+  , [-1.92188000679016, 0.453516006469727, 0]
+  , [-1.91788995265961, 1.10038995742798, -0.398745000362396]
+  , [-1.91788995265961, 1.10038995742798, 0.398745000362396]
+  , [-1.91561996936798, 0.496874988079071, -0.168750002980232]
+  , [-1.91561996936798, 0.496874988079071, 0.168750002980232]
+  , [-1.90429997444153, 0.462891012430191, -0.0984380021691322]
+  , [-1.90429997444153, 0.462891012430191, 0.0984380021691322]
+  , [-1.89999997615814, 0.449999988079071, 0]
+  , [-1.89227998256683, 0.670825004577637, -0.593047022819519]
+  , [-1.89227998256683, 0.670825004577637, 0.593047022819519]
+  , [-1.8834400177002, 0.453516006469727, -0.391582012176514]
+  , [-1.8834400177002, 0.453516006469727, 0.391582012176514]
+  , [-1.88206005096436, 0.925194978713989, -0.58984500169754]
+  , [-1.88206005096436, 0.925194978713989, 0.58984500169754]
+  , [-1.88138997554779, 1.28612995147705, -0.193601995706558]
+  , [-1.88138997554779, 1.28612995147705, 0.193601995706558]
+  , [-1.85511994361877, 0.5225830078125, -0.581402003765106]
+  , [-1.85511994361877, 0.5225830078125, 0.581402003765106]
+  , [-1.84500002861023, 0.75, -0.785000026226044]
+  , [-1.84500002861023, 0.75, 0.785000026226044]
+  , [-1.84375, 1.47186994552612, 0]
+  , [-1.83317005634308, 1.89067995548248, -0.0843750014901161]
+  , [-1.83317005634308, 1.89067995548248, 0.0843750014901161]
+  , [-1.83179998397827, 1.94649004936218, -0.196875005960464]
+  , [-1.83179998397827, 1.94649004936218, 0.196875005960464]
+  , [-1.82992005348206, 2.02323007583618, -0.196875005960464]
+  , [-1.82992005348206, 2.02323007583618, 0.196875005960464]
+  , [-1.82854998111725, 2.07904005050659, -0.0843750014901161]
+  , [-1.82854998111725, 2.07904005050659, 0.0843750014901161]
+  , [-1.82518005371094, 0.591650009155273, -0.776566982269287]
+  , [-1.82518005371094, 0.591650009155273, 0.776566982269287]
+  , [-1.81757998466492, 0.343944996595383, -0.187035992741585]
+  , [-1.81757998466492, 0.343944996595383, 0.187035992741585]
+  , [-1.80774998664856, 1.28612995147705, -0.566554009914398]
+  , [-1.80774998664856, 1.28612995147705, 0.566554009914398]
+  , [-1.8068699836731, 1.47186994552612, -0.375663995742798]
+  , [-1.8068699836731, 1.47186994552612, 0.375663995742798]
+  , [-1.80535995960236, 1.10038995742798, -0.768135011196136]
+  , [-1.80535995960236, 1.10038995742798, 0.768135011196136]
+  , [-1.77293002605438, 0.453516006469727, -0.754335999488831]
+  , [-1.77293002605438, 0.453516006469727, 0.754335999488831]
+  , [-1.75, 0.234375, 0]
+  , [-1.74644005298615, 0.343944996595383, -0.547339022159576]
+  , [-1.74644005298615, 0.343944996595383, 0.547339022159576]
+  , [-1.7443300485611, 0.670825004577637, -0.949871003627777]
+  , [-1.7443300485611, 0.670825004577637, 0.949871003627777]
+  , [-1.7349100112915, 0.925194978713989, -0.944741010665894]
+  , [-1.7349100112915, 0.925194978713989, 0.944741010665894]
+  , [-1.7150000333786, 0.234375, -0.356563001871109]
+  , [-1.7150000333786, 0.234375, 0.356561988592148]
+  , [-1.71008002758026, 0.5225830078125, -0.931218028068542]
+  , [-1.71008002758026, 0.5225830078125, 0.931218028068542]
+  , [-1.70086002349854, 1.47186994552612, -0.723671972751617]
+  , [-1.70086002349854, 1.47186994552612, 0.723671972751617]
+  , [-1.66639995574951, 1.28612995147705, -0.907437026500702]
+  , [-1.66639995574951, 1.28612995147705, 0.907437026500702]
+  , [-1.66250002384186, 0.75, -1.125]
+  , [-1.66250002384186, 0.75, 1.125]
+  , [-1.65515995025635, 1.86093997955322, -0.170322000980377]
+  , [-1.65515995025635, 1.86093997955322, 0.170322000980377]
+  , [-1.64742004871368, 0.159961000084877, -0.169525995850563]
+  , [-1.64742004871368, 0.159961000084877, 0.169525995850563]
+  , [-1.64463996887207, 0.591650009155273, -1.11292004585266]
+  , [-1.64463996887207, 0.591650009155273, 1.11292004585266]
+  , [-1.62678003311157, 1.10038995742798, -1.10082995891571]
+  , [-1.62678003311157, 1.10038995742798, 1.10082995891571]
+  , [-1.61436998844147, 0.234375, -0.686874985694885]
+  , [-1.61436998844147, 0.234375, 0.686874985694885]
+  , [-1.60988998413086, 0.343944996595383, -0.876659989356995]
+  , [-1.60988998413086, 0.343944996595383, 0.876659989356995]
+  , [-1.60000002384186, 1.875, 0]
+  , [-1.59756004810333, 0.453516006469727, -1.08106005191803]
+  , [-1.59756004810333, 0.453516006469727, 1.08106005191803]
+  , [-1.59037005901337, 1.86093997955322, -0.498427987098694]
+  , [-1.59037005901337, 1.86093997955322, 0.498427987098694]
+  , [-1.58438003063202, 1.91015994548798, -0.168750002980232]
+  , [-1.58438003063202, 1.91015994548798, 0.168750002980232]
+  , [-1.58293998241425, 0.159961000084877, -0.49609899520874]
+  , [-1.58293998241425, 0.159961000084877, 0.49609899520874]
+  , [-1.57813000679016, 0.085547000169754, 0]
+  , [-1.54999995231628, 1.98749995231628, -0.224999994039536]
+  , [-1.54999995231628, 1.98749995231628, 0.224999994039536]
+  , [-1.54656004905701, 0.085547000169754, -0.321543008089066]
+  , [-1.54656004905701, 0.085547000169754, 0.321543008089066]
+  , [-1.53296995162964, 0.670825004577637, -1.26566994190216]
+  , [-1.53296995162964, 0.670825004577637, 1.26566994190216]
+  , [-1.53261995315552, 1.47186994552612, -1.03710997104645]
+  , [-1.53261995315552, 1.47186994552612, 1.03710997104645]
+  , [-1.52469003200531, 0.925194978713989, -1.25882995128632]
+  , [-1.52469003200531, 0.925194978713989, 1.25882995128632]
+  , [-1.52366995811462, 0.042773000895977, -0.156791999936104]
+  , [-1.52366995811462, 0.042773000895977, 0.156791999936104]
+  , [-1.51563000679016, 2.06484007835388, -0.168750002980232]
+  , [-1.51563000679016, 2.06484007835388, 0.168750002980232]
+  , [-1.50286996364594, 0.5225830078125, -1.24081003665924]
+  , [-1.50286996364594, 0.5225830078125, 1.24081003665924]
+  , [-1.5, 0, 0]
+  , [-1.5, 2.09999990463257, 0]
+  , [-1.5, 2.25, 0]
+  , [-1.47000002861023, 0, -0.30562499165535]
+  , [-1.47000002861023, 0, 0.30562499165535]
+  , [-1.47000002861023, 2.25, -0.30562499165535]
+  , [-1.47000002861023, 2.25, 0.30562499165535]
+  , [-1.46601998806, 1.86093997955322, -0.79831999540329]
+  , [-1.46601998806, 1.86093997955322, 0.79831999540329]
+  , [-1.4644900560379, 1.28612995147705, -1.20912003517151]
+  , [-1.4644900560379, 1.28612995147705, 1.20912003517151]
+  , [-1.46403002738953, 0.042773000895977, -0.458833009004593]
+  , [-1.46403002738953, 0.042773000895977, 0.458833009004593]
+  , [-1.45985996723175, 2.28691005706787, -0.15022599697113]
+  , [-1.45985996723175, 2.28691005706787, 0.15022599697113]
+  , [-1.45916998386383, 0.159961000084877, -0.794589996337891]
+  , [-1.45916998386383, 0.159961000084877, 0.794589996337891]
+  , [-1.45581996440887, 0.085547000169754, -0.61941397190094]
+  , [-1.45581996440887, 0.085547000169754, 0.61941397190094]
+  , [-1.45468997955322, 0.234375, -0.984375]
+  , [-1.45468997955322, 0.234375, 0.984375]
+  , [-1.4492199420929, 2.32382988929749, 0]
+  , [-1.42023003101349, 2.32382988929749, -0.295278012752533]
+  , [-1.42023003101349, 2.32382988929749, 0.295278012752533]
+  , [-1.41999995708466, 0.75, -1.41999995708466]
+  , [-1.41999995708466, 0.75, 1.41999995708466]
+  , [-1.41481995582581, 0.343944996595383, -1.16812002658844]
+  , [-1.41481995582581, 0.343944996595383, 1.16812002658844]
+  , [-1.41191005706787, 2.33612990379333, -0.14529100060463]
+  , [-1.41191005706787, 2.33612990379333, 0.14529100060463]
+  , [-1.40474998950958, 0.591650009155273, -1.40474998950958]
+  , [-1.40474998950958, 0.591650009155273, 1.40474998950958]
+  , [-1.40313005447388, 2.34843993186951, 0]
+  , [-1.40271997451782, 2.28691005706787, -0.439617991447449]
+  , [-1.40271997451782, 2.28691005706787, 0.439617991447449]
+  , [-1.39999997615814, 2.25, 0]
+  , [-1.38949000835419, 1.10038995742798, -1.38949000835419]
+  , [-1.38949000835419, 1.10038995742798, 1.38949000835419]
+  , [-1.38374996185303, 0, -0.588750004768372]
+  , [-1.38374996185303, 0, 0.588750004768372]
+  , [-1.38374996185303, 2.25, -0.588750004768372]
+  , [-1.38374996185303, 2.25, 0.588750004768372]
+  , [-1.38047003746033, 2.32382988929749, 0]
+  , [-1.37787997722626, 2.33612990379333, -0.141789004206657]
+  , [-1.37787997722626, 2.33612990379333, 0.141789004206657]
+  , [-1.37633001804352, 2.28691005706787, -0.141629993915558]
+  , [-1.37633001804352, 2.28691005706787, 0.141629993915558]
+  , [-1.37505996227264, 2.34843993186951, -0.285887002944946]
+  , [-1.37505996227264, 2.34843993186951, 0.285887002944946]
+  , [-1.37199997901917, 2.25, -0.285250008106232]
+  , [-1.37199997901917, 2.25, 0.285250008106232]
+  , [-1.36452996730804, 0.453516006469727, -1.36452996730804]
+  , [-1.36452996730804, 0.453516006469727, 1.36452996730804]
+  , [-1.35664999485016, 2.33612990379333, -0.425177007913589]
+  , [-1.35664999485016, 2.33612990379333, 0.425177007913589]
+  , [-1.35285997390747, 2.32382988929749, -0.281271010637283]
+  , [-1.35285997390747, 2.32382988929749, 0.281271010637283]
+  , [-1.34957003593445, 0.042773000895977, -0.734902024269104]
+  , [-1.34957003593445, 0.042773000895977, 0.734902024269104]
+  , [-1.33689999580383, 2.32382988929749, -0.568817973136902]
+  , [-1.33689999580383, 2.32382988929749, 0.568817973136902]
+  , [-1.32395005226135, 2.33612990379333, -0.414929002523422]
+  , [-1.32395005226135, 2.33612990379333, 0.414929002523422]
+  , [-1.32246005535126, 2.28691005706787, -0.414463996887207]
+  , [-1.32246005535126, 2.28691005706787, 0.414463996887207]
+  , [-1.3118200302124, 0.085547000169754, -0.887695014476776]
+  , [-1.3118200302124, 0.085547000169754, 0.887695014476776]
+  , [-1.30905997753143, 1.47186994552612, -1.30905997753143]
+  , [-1.30905997753143, 1.47186994552612, 1.30905997753143]
+  , [-1.29999995231628, 2.25, 0]
+  , [-1.2943799495697, 2.34843993186951, -0.550727009773254]
+  , [-1.2943799495697, 2.34843993186951, 0.550727009773254]
+  , [-1.29305005073547, 2.28691005706787, -0.704126000404358]
+  , [-1.29305005073547, 2.28691005706787, 0.704126000404358]
+  , [-1.29149997234344, 2.25, -0.549499988555908]
+  , [-1.29149997234344, 2.25, 0.549499988555908]
+  , [-1.28839004039764, 1.86093997955322, -1.06373000144958]
+  , [-1.28839004039764, 1.86093997955322, 1.06373000144958]
+  , [-1.28236997127533, 0.159961000084877, -1.05876004695892]
+  , [-1.28236997127533, 0.159961000084877, 1.05876004695892]
+  , [-1.27400004863739, 2.25, -0.264874994754791]
+  , [-1.27400004863739, 2.25, 0.264874994754791]
+  , [-1.27348005771637, 2.32382988929749, -0.541833996772766]
+  , [-1.27348005771637, 2.32382988929749, 0.541833996772766]
+  , [-1.26766002178192, 2.27489995956421, -0.130447998642921]
+  , [-1.26766002178192, 2.27489995956421, 0.130447998642921]
+  , [-1.26566994190216, 0.670825004577637, -1.53296995162964]
+  , [-1.26566994190216, 0.670825004577637, 1.53296995162964]
+  , [-1.26093995571136, 2.29979991912842, 0]
+  , [-1.25882995128632, 0.925194978713989, -1.52469003200531]
+  , [-1.25882995128632, 0.925194978713989, 1.52469003200531]
+  , [-1.25057005882263, 2.33612990379333, -0.680997014045715]
+  , [-1.25057005882263, 2.33612990379333, 0.680997014045715]
+  , [-1.24688005447388, 0, -0.84375]
+  , [-1.24688005447388, 0, 0.84375]
+  , [-1.24688005447388, 2.25, -0.84375]
+  , [-1.24688005447388, 2.25, 0.84375]
+  , [-1.24249994754791, 0.234375, -1.24249994754791]
+  , [-1.24249994754791, 0.234375, 1.24249994754791]
+  , [-1.24081003665924, 0.5225830078125, -1.50286996364594]
+  , [-1.24081003665924, 0.5225830078125, 1.50286996364594]
+  , [-1.235720038414, 2.29979991912842, -0.256915986537933]
+  , [-1.235720038414, 2.29979991912842, 0.256915986537933]
+  , [-1.22043001651764, 2.33612990379333, -0.664583027362823]
+  , [-1.22043001651764, 2.33612990379333, 0.664583027362823]
+  , [-1.21905994415283, 2.28691005706787, -0.663837015628815]
+  , [-1.21905994415283, 2.28691005706787, 0.663837015628815]
+  , [-1.21805000305176, 2.27489995956421, -0.381740003824234]
+  , [-1.21805000305176, 2.27489995956421, 0.381740003824234]
+  , [-1.20912003517151, 1.28612995147705, -1.4644900560379]
+  , [-1.20912003517151, 1.28612995147705, 1.4644900560379]
+  , [-1.20466005802155, 2.32382988929749, -0.815186023712158]
+  , [-1.20466005802155, 2.32382988929749, 0.815186023712158]
+  , [-1.19924998283386, 2.25, -0.510249972343445]
+  , [-1.19924998283386, 2.25, 0.510249972343445]
+  , [-1.19650995731354, 2.31943011283875, -0.123125001788139]
+  , [-1.19650995731354, 2.31943011283875, 0.123125001788139]
+  , [-1.18604004383087, 0.042773000895977, -0.979228973388672]
+  , [-1.18604004383087, 0.042773000895977, 0.979228973388672]
+  , [-1.16812002658844, 0.343944996595383, -1.41481995582581]
+  , [-1.16812002658844, 0.343944996595383, 1.41481995582581]
+  , [-1.16635000705719, 2.34843993186951, -0.789258003234863]
+  , [-1.16635000705719, 2.34843993186951, 0.789258003234863]
+  , [-1.16375005245209, 2.25, -0.787500023841858]
+  , [-1.16375005245209, 2.25, 0.787500023841858]
+  , [-1.16322004795074, 2.29979991912842, -0.494917988777161]
+  , [-1.16322004795074, 2.29979991912842, 0.494917988777161]
+  , [-1.15625, 2.33906006813049, 0]
+  , [-1.14968001842499, 2.31943011283875, -0.360312014818192]
+  , [-1.14968001842499, 2.31943011283875, 0.360312014818192]
+  , [-1.14751994609833, 2.32382988929749, -0.776513993740082]
+  , [-1.14751994609833, 2.32382988929749, 0.776513993740082]
+  , [-1.13636994361877, 2.28691005706787, -0.938220024108887]
+  , [-1.13636994361877, 2.28691005706787, 0.938220024108887]
+  , [-1.13311994075775, 2.33906006813049, -0.235586002469063]
+  , [-1.13311994075775, 2.33906006813049, 0.235586002469063]
+  , [-1.125, 0.75, -1.66250002384186]
+  , [-1.125, 0.75, 1.66250002384186]
+  , [-1.12281000614166, 2.27489995956421, -0.611424028873444]
+  , [-1.12281000614166, 2.27489995956421, 0.611424028873444]
+  , [-1.12047004699707, 0.085547000169754, -1.12047004699707]
+  , [-1.12047004699707, 0.085547000169754, 1.12047004699707]
+  , [-1.11292004585266, 0.591650009155273, -1.64463996887207]
+  , [-1.11292004585266, 0.591650009155273, 1.64463996887207]
+  , [-1.10082995891571, 1.10038995742798, -1.62678003311157]
+  , [-1.10082995891571, 1.10038995742798, 1.62678003311157]
+  , [-1.09904003143311, 2.33612990379333, -0.907401978969574]
+  , [-1.09904003143311, 2.33612990379333, 0.907401978969574]
+  , [-1.08106005191803, 0.453516006469727, -1.59756004810333]
+  , [-1.08106005191803, 0.453516006469727, 1.59756004810333]
+  , [-1.08062994480133, 2.25, -0.731249988079071]
+  , [-1.08062994480133, 2.25, 0.731249988079071]
+  , [-1.07255005836487, 2.33612990379333, -0.885531008243561]
+  , [-1.07255005836487, 2.33612990379333, 0.885531008243561]
+  , [-1.07134997844696, 2.28691005706787, -0.884536981582642]
+  , [-1.07134997844696, 2.28691005706787, 0.884536981582642]
+  , [-1.06664001941681, 2.33906006813049, -0.453828006982803]
+  , [-1.06664001941681, 2.33906006813049, 0.453828006982803]
+  , [-1.06500005722046, 0, -1.06500005722046]
+  , [-1.06500005722046, 0, 1.06500005722046]
+  , [-1.06500005722046, 2.25, -1.06500005722046]
+  , [-1.06500005722046, 2.25, 1.06500005722046]
+  , [-1.06373000144958, 1.86093997955322, -1.28839004039764]
+  , [-1.06373000144958, 1.86093997955322, 1.28839004039764]
+  , [-1.05979001522064, 2.31943011283875, -0.577103972434998]
+  , [-1.05979001522064, 2.31943011283875, 0.577103972434998]
+  , [-1.05876004695892, 0.159961000084877, -1.28236997127533]
+  , [-1.05876004695892, 0.159961000084877, 1.28236997127533]
+  , [-1.04814994335175, 2.29979991912842, -0.709276974201202]
+  , [-1.04814994335175, 2.29979991912842, 0.709276974201202]
+  , [-1.03710997104645, 1.47186994552612, -1.53261995315552]
+  , [-1.03710997104645, 1.47186994552612, 1.53261995315552]
+  , [-1.02893996238708, 2.32382988929749, -1.02893996238708]
+  , [-1.02893996238708, 2.32382988929749, 1.02893996238708]
+  , [-0.996218979358673, 2.34843993186951, -0.996218979358673]
+  , [-0.996218979358673, 2.34843993186951, 0.996218979358673]
+  , [-0.994000017642975, 2.25, -0.994000017642975]
+  , [-0.994000017642975, 2.25, 0.994000017642975]
+  , [-0.986760973930359, 2.27489995956421, -0.814697980880737]
+  , [-0.986760973930359, 2.27489995956421, 0.814697980880737]
+  , [-0.984375, 0.234375, -1.45468997955322]
+  , [-0.984375, 0.234375, 1.45468997955322]
+  , [-0.980718970298767, 2.36952996253967, -0.100919999182224]
+  , [-0.980718970298767, 2.36952996253967, 0.100919999182224]
+  , [-0.98013299703598, 2.32382988929749, -0.98013299703598]
+  , [-0.98013299703598, 2.32382988929749, 0.98013299703598]
+  , [-0.979228973388672, 0.042773000895977, -1.18604004383087]
+  , [-0.979228973388672, 0.042773000895977, 1.18604004383087]
+  , [-0.961133003234863, 2.33906006813049, -0.650390982627869]
+  , [-0.961133003234863, 2.33906006813049, 0.650390982627869]
+  , [-0.949871003627777, 0.670825004577637, -1.7443300485611]
+  , [-0.949871003627777, 0.670825004577637, 1.7443300485611]
+  , [-0.944741010665894, 0.925194978713989, -1.7349100112915]
+  , [-0.944741010665894, 0.925194978713989, 1.7349100112915]
+  , [-0.942332029342651, 2.36952996253967, -0.295329988002777]
+  , [-0.942332029342651, 2.36952996253967, 0.295329988002777]
+  , [-0.938220024108887, 2.28691005706787, -1.13636994361877]
+  , [-0.938220024108887, 2.28691005706787, 1.13636994361877]
+  , [-0.931373000144958, 2.31943011283875, -0.768967986106873]
+  , [-0.931373000144958, 2.31943011283875, 0.768967986106873]
+  , [-0.931218028068542, 0.5225830078125, -1.71008002758026]
+  , [-0.931218028068542, 0.5225830078125, 1.71008002758026]
+  , [-0.922999978065491, 2.25, -0.922999978065491]
+  , [-0.922999978065491, 2.25, 0.922999978065491]
+  , [-0.907437026500702, 1.28612995147705, -1.66639995574951]
+  , [-0.907437026500702, 1.28612995147705, 1.66639995574951]
+  , [-0.907401978969574, 2.33612990379333, -1.09904003143311]
+  , [-0.907401978969574, 2.33612990379333, 1.09904003143311]
+  , [-0.895265996456146, 2.29979991912842, -0.895265996456146]
+  , [-0.895265996456146, 2.29979991912842, 0.895265996456146]
+  , [-0.887695014476776, 0.085547000169754, -1.3118200302124]
+  , [-0.887695014476776, 0.085547000169754, 1.3118200302124]
+  , [-0.885531008243561, 2.33612990379333, -1.07255005836487]
+  , [-0.885531008243561, 2.33612990379333, 1.07255005836487]
+  , [-0.884536981582642, 2.28691005706787, -1.07134997844696]
+  , [-0.884536981582642, 2.28691005706787, 1.07134997844696]
+  , [-0.876659989356995, 0.343944996595383, -1.60988998413086]
+  , [-0.876659989356995, 0.343944996595383, 1.60988998413086]
+  , [-0.868654012680054, 2.36952996253967, -0.473022997379303]
+  , [-0.868654012680054, 2.36952996253967, 0.473022997379303]
+  , [-0.84375, 0, -1.24688005447388]
+  , [-0.84375, 0, 1.24688005447388]
+  , [-0.84375, 2.25, -1.24688005447388]
+  , [-0.84375, 2.25, 1.24688005447388]
+  , [-0.824999988079071, 2.40000009536743, 0]
+  , [-0.820937991142273, 2.33906006813049, -0.820937991142273]
+  , [-0.820937991142273, 2.33906006813049, 0.820937991142273]
+  , [-0.815186023712158, 2.32382988929749, -1.20466005802155]
+  , [-0.815186023712158, 2.32382988929749, 1.20466005802155]
+  , [-0.814697980880737, 2.27489995956421, -0.986760973930359]
+  , [-0.814697980880737, 2.27489995956421, 0.986760973930359]
+  , [-0.808499991893768, 2.40000009536743, -0.168093994259834]
+  , [-0.808499991893768, 2.40000009536743, 0.168093994259834]
+  , [-0.79831999540329, 1.86093997955322, -1.46601998806]
+  , [-0.79831999540329, 1.86093997955322, 1.46601998806]
+  , [-0.794589996337891, 0.159961000084877, -1.45916998386383]
+  , [-0.794589996337891, 0.159961000084877, 1.45916998386383]
+  , [-0.789258003234863, 2.34843993186951, -1.16635000705719]
+  , [-0.789258003234863, 2.34843993186951, 1.16635000705719]
+  , [-0.787500023841858, 2.25, -1.16375005245209]
+  , [-0.787500023841858, 2.25, 1.16375005245209]
+  , [-0.785000026226044, 0.75, -1.84500002861023]
+  , [-0.785000026226044, 0.75, 1.84500002861023]
+  , [-0.776566982269287, 0.591650009155273, -1.82518005371094]
+  , [-0.776566982269287, 0.591650009155273, 1.82518005371094]
+  , [-0.776513993740082, 2.32382988929749, -1.14751994609833]
+  , [-0.776513993740082, 2.32382988929749, 1.14751994609833]
+  , [-0.768967986106873, 2.31943011283875, -0.931373000144958]
+  , [-0.768967986106873, 2.31943011283875, 0.931373000144958]
+  , [-0.768135011196136, 1.10038995742798, -1.80535995960236]
+  , [-0.768135011196136, 1.10038995742798, 1.80535995960236]
+  , [-0.763400018215179, 2.36952996253967, -0.630285024642944]
+  , [-0.763400018215179, 2.36952996253967, 0.630285024642944]
+  , [-0.761062979698181, 2.40000009536743, -0.323812991380692]
+  , [-0.761062979698181, 2.40000009536743, 0.323812991380692]
+  , [-0.754335999488831, 0.453516006469727, -1.77293002605438]
+  , [-0.754335999488831, 0.453516006469727, 1.77293002605438]
+  , [-0.734902024269104, 0.042773000895977, -1.34957003593445]
+  , [-0.734902024269104, 0.042773000895977, 1.34957003593445]
+  , [-0.731249988079071, 2.25, -1.08062994480133]
+  , [-0.731249988079071, 2.25, 1.08062994480133]
+  , [-0.723671972751617, 1.47186994552612, -1.70086002349854]
+  , [-0.723671972751617, 1.47186994552612, 1.70086002349854]
+  , [-0.709276974201202, 2.29979991912842, -1.04814994335175]
+  , [-0.709276974201202, 2.29979991912842, 1.04814994335175]
+  , [-0.704126000404358, 2.28691005706787, -1.29305005073547]
+  , [-0.704126000404358, 2.28691005706787, 1.29305005073547]
+  , [-0.686874985694885, 0.234375, -1.61436998844147]
+  , [-0.686874985694885, 0.234375, 1.61436998844147]
+  , [-0.685781002044678, 2.40000009536743, -0.464062988758087]
+  , [-0.685781002044678, 2.40000009536743, 0.464062988758087]
+  , [-0.680997014045715, 2.33612990379333, -1.25057005882263]
+  , [-0.680997014045715, 2.33612990379333, 1.25057005882263]
+  , [-0.664583027362823, 2.33612990379333, -1.22043001651764]
+  , [-0.664583027362823, 2.33612990379333, 1.22043001651764]
+  , [-0.663837015628815, 2.28691005706787, -1.21905994415283]
+  , [-0.663837015628815, 2.28691005706787, 1.21905994415283]
+  , [-0.650390982627869, 2.33906006813049, -0.961133003234863]
+  , [-0.650390982627869, 2.33906006813049, 0.961133003234863]
+  , [-0.631998002529144, 2.43046998977661, -0.0648249983787537]
+  , [-0.631998002529144, 2.43046998977661, 0.0648249983787537]
+  , [-0.630285024642944, 2.36952996253967, -0.763400018215179]
+  , [-0.630285024642944, 2.36952996253967, 0.763400018215179]
+  , [-0.61941397190094, 0.085547000169754, -1.45581996440887]
+  , [-0.61941397190094, 0.085547000169754, 1.45581996440887]
+  , [-0.611424028873444, 2.27489995956421, -1.12281000614166]
+  , [-0.611424028873444, 2.27489995956421, 1.12281000614166]
+  , [-0.607173979282379, 2.43046998977661, -0.190548002719879]
+  , [-0.607173979282379, 2.43046998977661, 0.190548002719879]
+  , [-0.593047022819519, 0.670825004577637, -1.89227998256683]
+  , [-0.593047022819519, 0.670825004577637, 1.89227998256683]
+  , [-0.58984500169754, 0.925194978713989, -1.88206005096436]
+  , [-0.58984500169754, 0.925194978713989, 1.88206005096436]
+  , [-0.588750004768372, 0, -1.38374996185303]
+  , [-0.588750004768372, 0, 1.38374996185303]
+  , [-0.588750004768372, 2.25, -1.38374996185303]
+  , [-0.588750004768372, 2.25, 1.38374996185303]
+  , [-0.585749983787537, 2.40000009536743, -0.585749983787537]
+  , [-0.585749983787537, 2.40000009536743, 0.585749983787537]
+  , [-0.581402003765106, 0.5225830078125, -1.85511994361877]
+  , [-0.581402003765106, 0.5225830078125, 1.85511994361877]
+  , [-0.577103972434998, 2.31943011283875, -1.05979001522064]
+  , [-0.577103972434998, 2.31943011283875, 1.05979001522064]
+  , [-0.568817973136902, 2.32382988929749, -1.33689999580383]
+  , [-0.568817973136902, 2.32382988929749, 1.33689999580383]
+  , [-0.566554009914398, 1.28612995147705, -1.80774998664856]
+  , [-0.566554009914398, 1.28612995147705, 1.80774998664856]
+  , [-0.559973001480103, 2.43046998977661, -0.304711014032364]
+  , [-0.559973001480103, 2.43046998977661, 0.304711014032364]
+  , [-0.550727009773254, 2.34843993186951, -1.2943799495697]
+  , [-0.550727009773254, 2.34843993186951, 1.2943799495697]
+  , [-0.549499988555908, 2.25, -1.29149997234344]
+  , [-0.549499988555908, 2.25, 1.29149997234344]
+  , [-0.547339022159576, 0.343944996595383, -1.74644005298615]
+  , [-0.547339022159576, 0.343944996595383, 1.74644005298615]
+  , [-0.541833996772766, 2.32382988929749, -1.27348005771637]
+  , [-0.541833996772766, 2.32382988929749, 1.27348005771637]
+  , [-0.510249972343445, 2.25, -1.19924998283386]
+  , [-0.510249972343445, 2.25, 1.19924998283386]
+  , [-0.498427987098694, 1.86093997955322, -1.59037005901337]
+  , [-0.498427987098694, 1.86093997955322, 1.59037005901337]
+  , [-0.49609899520874, 0.159961000084877, -1.58293998241425]
+  , [-0.49609899520874, 0.159961000084877, 1.58293998241425]
+  , [-0.494917988777161, 2.29979991912842, -1.16322004795074]
+  , [-0.494917988777161, 2.29979991912842, 1.16322004795074]
+  , [-0.491907000541687, 2.43046998977661, -0.4064100086689]
+  , [-0.491907000541687, 2.43046998977661, 0.4064100086689]
+  , [-0.473022997379303, 2.36952996253967, -0.868654012680054]
+  , [-0.473022997379303, 2.36952996253967, 0.868654012680054]
+  , [-0.464062988758087, 2.40000009536743, -0.685781002044678]
+  , [-0.464062988758087, 2.40000009536743, 0.685781002044678]
+  , [-0.458833009004593, 0.042773000895977, -1.46403002738953]
+  , [-0.458833009004593, 0.042773000895977, 1.46403002738953]
+  , [-0.456250011920929, 2.46093988418579, 0]
+  , [-0.453828006982803, 2.33906006813049, -1.06664001941681]
+  , [-0.453828006982803, 2.33906006813049, 1.06664001941681]
+  , [-0.439617991447449, 2.28691005706787, -1.40271997451782]
+  , [-0.439617991447449, 2.28691005706787, 1.40271997451782]
+  , [-0.438241004943848, 2.46093988418579, -0.0912069976329803]
+  , [-0.438241004943848, 2.46093988418579, 0.0912069976329803]
+  , [-0.425177007913589, 2.33612990379333, -1.35664999485016]
+  , [-0.425177007913589, 2.33612990379333, 1.35664999485016]
+  , [-0.420890986919403, 2.46093988418579, -0.179077997803688]
+  , [-0.420890986919403, 2.46093988418579, 0.179077997803688]
+  , [-0.414929002523422, 2.33612990379333, -1.32395005226135]
+  , [-0.414929002523422, 2.33612990379333, 1.32395005226135]
+  , [-0.414463996887207, 2.28691005706787, -1.32246005535126]
+  , [-0.414463996887207, 2.28691005706787, 1.32246005535126]
+  , [-0.407499998807907, 0.75, -1.96000003814697]
+  , [-0.407499998807907, 0.75, 1.96000003814697]
+  , [-0.4064100086689, 2.43046998977661, -0.491907000541687]
+  , [-0.4064100086689, 2.43046998977661, 0.491907000541687]
+  , [-0.403122991323471, 0.591650009155273, -1.93894994258881]
+  , [-0.403122991323471, 0.591650009155273, 1.93894994258881]
+  , [-0.398745000362396, 1.10038995742798, -1.91788995265961]
+  , [-0.398745000362396, 1.10038995742798, 1.91788995265961]
+  , [-0.391582012176514, 0.453516006469727, -1.8834400177002]
+  , [-0.391582012176514, 0.453516006469727, 1.8834400177002]
+  , [-0.381740003824234, 2.27489995956421, -1.21805000305176]
+  , [-0.381740003824234, 2.27489995956421, 1.21805000305176]
+  , [-0.375663995742798, 1.47186994552612, -1.8068699836731]
+  , [-0.375663995742798, 1.47186994552612, 1.8068699836731]
+  , [-0.372159004211426, 2.46093988418579, -0.251888990402222]
+  , [-0.372159004211426, 2.46093988418579, 0.251888990402222]
+  , [-0.362109005451202, 2.8971700668335, 0]
+  , [-0.360312014818192, 2.31943011283875, -1.14968001842499]
+  , [-0.360312014818192, 2.31943011283875, 1.14968001842499]
+  , [-0.356563001871109, 0.234375, 1.7150000333786]
+  , [-0.356561988592148, 0.234375, -1.7150000333786]
+  , [-0.340624988079071, 2.95077991485596, 0]
+  , [-0.337859004735947, 2.92396998405457, -0.0692780017852783]
+  , [-0.337859004735947, 2.92396998405457, 0.0692780017852783]
+  , [-0.334237992763519, 2.8971700668335, -0.142704993486404]
+  , [-0.334237992763519, 2.8971700668335, 0.142704993486404]
+  , [-0.33032500743866, 2.8642098903656, -0.067671999335289]
+  , [-0.33032500743866, 2.8642098903656, 0.067671999335289]
+  , [-0.324999988079071, 2.83124995231628, 0]
+  , [-0.323938012123108, 2.46093988418579, -0.323938012123108]
+  , [-0.323938012123108, 2.46093988418579, 0.323938012123108]
+  , [-0.323812991380692, 2.40000009536743, -0.761062979698181]
+  , [-0.323812991380692, 2.40000009536743, 0.761062979698181]
+  , [-0.321543008089066, 0.085547000169754, -1.54656004905701]
+  , [-0.321543008089066, 0.085547000169754, 1.54656004905701]
+  , [-0.315409988164902, 2.50547003746033, -0.0643950030207634]
+  , [-0.315409988164902, 2.50547003746033, 0.0643950030207634]
+  , [-0.314464002847672, 2.95077991485596, -0.134406998753548]
+  , [-0.314464002847672, 2.95077991485596, 0.134406998753548]
+  , [-0.30562499165535, 0, -1.47000002861023]
+  , [-0.30562499165535, 0, 1.47000002861023]
+  , [-0.30562499165535, 2.25, -1.47000002861023]
+  , [-0.30562499165535, 2.25, 1.47000002861023]
+  , [-0.304711014032364, 2.43046998977661, -0.559973001480103]
+  , [-0.304711014032364, 2.43046998977661, 0.559973001480103]
+  , [-0.299953013658524, 2.83124995231628, -0.127984002232552]
+  , [-0.299953013658524, 2.83124995231628, 0.127984002232552]
+  , [-0.295329988002777, 2.36952996253967, -0.942332029342651]
+  , [-0.295329988002777, 2.36952996253967, 0.942332029342651]
+  , [-0.295278012752533, 2.32382988929749, -1.42023003101349]
+  , [-0.295278012752533, 2.32382988929749, 1.42023003101349]
+  , [-0.28719699382782, 2.92396998405457, -0.1942999958992]
+  , [-0.28719699382782, 2.92396998405457, 0.1942999958992]
+  , [-0.285887002944946, 2.34843993186951, -1.37505996227264]
+  , [-0.285887002944946, 2.34843993186951, 1.37505996227264]
+  , [-0.285250008106232, 2.25, -1.37199997901917]
+  , [-0.285250008106232, 2.25, 1.37199997901917]
+  , [-0.281271010637283, 2.32382988929749, -1.35285997390747]
+  , [-0.281271010637283, 2.32382988929749, 1.35285997390747]
+  , [-0.280732005834579, 2.8642098903656, -0.189855992794037]
+  , [-0.280732005834579, 2.8642098903656, 0.189855992794037]
+  , [-0.274421006441116, 2.96880006790161, -0.0563799999654293]
+  , [-0.274421006441116, 2.96880006790161, 0.0563799999654293]
+  , [-0.267832010984421, 2.50547003746033, -0.18087899684906]
+  , [-0.267832010984421, 2.50547003746033, 0.18087899684906]
+  , [-0.264874994754791, 2.25, -1.27400004863739]
+  , [-0.264874994754791, 2.25, 1.27400004863739]
+  , [-0.257609993219376, 2.8971700668335, -0.257609993219376]
+  , [-0.257609993219376, 2.8971700668335, 0.257609993219376]
+  , [-0.256915986537933, 2.29979991912842, -1.235720038414]
+  , [-0.256915986537933, 2.29979991912842, 1.235720038414]
+  , [-0.251888990402222, 2.46093988418579, -0.372159004211426]
+  , [-0.251888990402222, 2.46093988418579, 0.372159004211426]
+  , [-0.250871986150742, 2.7574200630188, -0.0513469986617565]
+  , [-0.250871986150742, 2.7574200630188, 0.0513469986617565]
+  , [-0.242476999759674, 2.95077991485596, -0.242476999759674]
+  , [-0.242476999759674, 2.95077991485596, 0.242476999759674]
+  , [-0.235586002469063, 2.33906006813049, -1.13311994075775]
+  , [-0.235586002469063, 2.33906006813049, 1.13311994075775]
+  , [-0.233382001519203, 2.96880006790161, -0.158017992973328]
+  , [-0.233382001519203, 2.96880006790161, 0.158017992973328]
+  , [-0.231124997138977, 2.83124995231628, -0.231124997138977]
+  , [-0.231124997138977, 2.83124995231628, 0.231124997138977]
+  , [-0.230077996850014, 2.98681998252869, 0]
+  , [-0.213158994913101, 2.7574200630188, -0.14410300552845]
+  , [-0.213158994913101, 2.7574200630188, 0.14410300552845]
+  , [-0.212515994906425, 2.98681998252869, -0.0911130011081696]
+  , [-0.212515994906425, 2.98681998252869, 0.0911130011081696]
+  , [-0.202656000852585, 0.670825004577637, -1.96937000751495]
+  , [-0.202656000852585, 0.670825004577637, 1.96937000751495]
+  , [-0.201561003923416, 0.925194978713989, -1.9587299823761]
+  , [-0.201561003923416, 0.925194978713989, 1.9587299823761]
+  , [-0.200000002980232, 2.54999995231628, 0]
+  , [-0.198676005005836, 0.5225830078125, -1.93069005012512]
+  , [-0.198676005005836, 0.5225830078125, 1.93069005012512]
+  , [-0.196875005960464, 2.68358993530273, 0]
+  , [-0.1942999958992, 2.92396998405457, -0.28719699382782]
+  , [-0.1942999958992, 2.92396998405457, 0.28719699382782]
+  , [-0.193601995706558, 1.28612995147705, -1.88138997554779]
+  , [-0.193601995706558, 1.28612995147705, 1.88138997554779]
+  , [-0.190548002719879, 2.43046998977661, -0.607173979282379]
+  , [-0.190548002719879, 2.43046998977661, 0.607173979282379]
+  , [-0.189855992794037, 2.8642098903656, -0.280732005834579]
+  , [-0.189855992794037, 2.8642098903656, 0.280732005834579]
+  , [-0.187035992741585, 0.343944996595383, -1.81757998466492]
+  , [-0.187035992741585, 0.343944996595383, 1.81757998466492]
+  , [-0.184499993920326, 2.54999995231628, -0.0785000026226044]
+  , [-0.184499993920326, 2.54999995231628, 0.0785000026226044]
+  , [-0.181660994887352, 2.68358993530273, -0.0774049982428551]
+  , [-0.181660994887352, 2.68358993530273, 0.0774049982428551]
+  , [-0.18087899684906, 2.50547003746033, -0.267832010984421]
+  , [-0.18087899684906, 2.50547003746033, 0.267832010984421]
+  , [-0.179077997803688, 2.46093988418579, -0.420890986919403]
+  , [-0.179077997803688, 2.46093988418579, 0.420890986919403]
+  , [-0.17629499733448, 2.58119988441467, -0.0360010005533695]
+  , [-0.17629499733448, 2.58119988441467, 0.0360010005533695]
+  , [-0.174804002046585, 2.64800000190735, -0.0357270017266273]
+  , [-0.174804002046585, 2.64800000190735, 0.0357270017266273]
+  , [-0.170322000980377, 1.86093997955322, -1.65515995025635]
+  , [-0.170322000980377, 1.86093997955322, 1.65515995025635]
+  , [-0.169525995850563, 0.159961000084877, -1.64742004871368]
+  , [-0.169525995850563, 0.159961000084877, 1.64742004871368]
+  , [-0.168093994259834, 2.40000009536743, -0.808499991893768]
+  , [-0.168093994259834, 2.40000009536743, 0.808499991893768]
+  , [-0.166796997189522, 2.61240005493164, 0]
+  , [-0.164073005318642, 2.98681998252869, -0.164073005318642]
+  , [-0.164073005318642, 2.98681998252869, 0.164073005318642]
+  , [-0.158017992973328, 2.96880006790161, -0.233382001519203]
+  , [-0.158017992973328, 2.96880006790161, 0.233382001519203]
+  , [-0.156791999936104, 0.042773000895977, -1.52366995811462]
+  , [-0.156791999936104, 0.042773000895977, 1.52366995811462]
+  , [-0.153881996870041, 2.61240005493164, -0.0655039995908737]
+  , [-0.153881996870041, 2.61240005493164, 0.0655039995908737]
+  , [-0.15022599697113, 2.28691005706787, -1.45985996723175]
+  , [-0.15022599697113, 2.28691005706787, 1.45985996723175]
+  , [-0.14970999956131, 2.58119988441467, -0.101116001605988]
+  , [-0.14970999956131, 2.58119988441467, 0.101116001605988]
+  , [-0.148475006222725, 2.64800000190735, -0.100316002964973]
+  , [-0.148475006222725, 2.64800000190735, 0.100316002964973]
+  , [-0.14529100060463, 2.33612990379333, -1.41191005706787]
+  , [-0.14529100060463, 2.33612990379333, 1.41191005706787]
+  , [-0.14410300552845, 2.7574200630188, -0.213158994913101]
+  , [-0.14410300552845, 2.7574200630188, 0.213158994913101]
+  , [-0.142704993486404, 2.8971700668335, -0.334237992763519]
+  , [-0.142704993486404, 2.8971700668335, 0.334237992763519]
+  , [-0.142000004649162, 2.54999995231628, -0.142000004649162]
+  , [-0.142000004649162, 2.54999995231628, 0.142000004649162]
+  , [-0.141789004206657, 2.33612990379333, -1.37787997722626]
+  , [-0.141789004206657, 2.33612990379333, 1.37787997722626]
+  , [-0.141629993915558, 2.28691005706787, -1.37633001804352]
+  , [-0.141629993915558, 2.28691005706787, 1.37633001804352]
+  , [-0.139898002147675, 2.68358993530273, -0.139898002147675]
+  , [-0.139898002147675, 2.68358993530273, 0.139898002147675]
+  , [-0.134406998753548, 2.95077991485596, -0.314464002847672]
+  , [-0.134406998753548, 2.95077991485596, 0.314464002847672]
+  , [-0.130447998642921, 2.27489995956421, -1.26766002178192]
+  , [-0.130447998642921, 2.27489995956421, 1.26766002178192]
+  , [-0.127984002232552, 2.83124995231628, -0.299953013658524]
+  , [-0.127984002232552, 2.83124995231628, 0.299953013658524]
+  , [-0.123125001788139, 2.31943011283875, -1.19650995731354]
+  , [-0.123125001788139, 2.31943011283875, 1.19650995731354]
+  , [-0.11845800280571, 2.61240005493164, -0.11845800280571]
+  , [-0.11845800280571, 2.61240005493164, 0.11845800280571]
+  , [-0.11064899712801, 2.99341011047363, -0.0227780006825924]
+  , [-0.11064899712801, 2.99341011047363, 0.0227780006825924]
+  , [-0.101116001605988, 2.58119988441467, -0.14970999956131]
+  , [-0.101116001605988, 2.58119988441467, 0.14970999956131]
+  , [-0.100919999182224, 2.36952996253967, -0.980718970298767]
+  , [-0.100919999182224, 2.36952996253967, 0.980718970298767]
+  , [-0.100316002964973, 2.64800000190735, -0.148475006222725]
+  , [-0.100316002964973, 2.64800000190735, 0.148475006222725]
+  , [-0.0941469967365265, 2.99341011047363, -0.0637969970703125]
+  , [-0.0941469967365265, 2.99341011047363, 0.0637969970703125]
+  , [-0.0912069976329803, 2.46093988418579, -0.438241004943848]
+  , [-0.0912069976329803, 2.46093988418579, 0.438241004943848]
+  , [-0.0911130011081696, 2.98681998252869, -0.212515994906425]
+  , [-0.0911130011081696, 2.98681998252869, 0.212515994906425]
+  , [-0.0785000026226044, 2.54999995231628, -0.184499993920326]
+  , [-0.0785000026226044, 2.54999995231628, 0.184499993920326]
+  , [-0.0774049982428551, 2.68358993530273, -0.181660994887352]
+  , [-0.0774049982428551, 2.68358993530273, 0.181660994887352]
+  , [-0.0692780017852783, 2.92396998405457, -0.337859004735947]
+  , [-0.0692780017852783, 2.92396998405457, 0.337859004735947]
+  , [-0.067671999335289, 2.8642098903656, -0.33032500743866]
+  , [-0.067671999335289, 2.8642098903656, 0.33032500743866]
+  , [-0.0655039995908737, 2.61240005493164, -0.153881996870041]
+  , [-0.0655039995908737, 2.61240005493164, 0.153881996870041]
+  , [-0.0648249983787537, 2.43046998977661, -0.631998002529144]
+  , [-0.0648249983787537, 2.43046998977661, 0.631998002529144]
+  , [-0.0643950030207634, 2.50547003746033, -0.315409988164902]
+  , [-0.0643950030207634, 2.50547003746033, 0.315409988164902]
+  , [-0.0637969970703125, 2.99341011047363, -0.0941469967365265]
+  , [-0.0637969970703125, 2.99341011047363, 0.0941469967365265]
+  , [-0.0563799999654293, 2.96880006790161, -0.274421006441116]
+  , [-0.0563799999654293, 2.96880006790161, 0.274421006441116]
+  , [-0.0513469986617565, 2.7574200630188, -0.250871986150742]
+  , [-0.0513469986617565, 2.7574200630188, 0.250871986150742]
+  , [-0.0360010005533695, 2.58119988441467, -0.17629499733448]
+  , [-0.0360010005533695, 2.58119988441467, 0.17629499733448]
+  , [-0.0357270017266273, 2.64800000190735, -0.174804002046585]
+  , [-0.0357270017266273, 2.64800000190735, 0.174804002046585]
+  , [-0.0227780006825924, 2.99341011047363, -0.11064899712801]
+  , [-0.0227780006825924, 2.99341011047363, 0.11064899712801]
+  , [0, 0, -1.5]
+  , [0, 0, 1.5]
+  , [0, 0.085547000169754, -1.57813000679016]
+  , [0, 0.085547000169754, 1.57813000679016]
+  , [0, 0.234375, -1.75]
+  , [0, 0.234375, 1.75]
+  , [0, 0.453516006469727, -1.92188000679016]
+  , [0, 0.453516006469727, 1.92188000679016]
+  , [0, 0.591650009155273, -1.97852003574371]
+  , [0, 0.591650009155273, 1.97852003574371]
+  , [0, 0.75, -2]
+  , [0, 0.75, 2]
+  , [0, 1.10038995742798, -1.9570300579071]
+  , [0, 1.10038995742798, 1.9570300579071]
+  , [0, 1.47186994552612, -1.84375]
+  , [0, 1.47186994552612, 1.84375]
+  , [0, 2.25, -1.5]
+  , [0, 2.25, -1.39999997615814]
+  , [0, 2.25, -1.29999995231628]
+  , [0, 2.25, 1.29999995231628]
+  , [0, 2.25, 1.39999997615814]
+  , [0, 2.25, 1.5]
+  , [0, 2.29979991912842, -1.26093995571136]
+  , [0, 2.29979991912842, 1.26093995571136]
+  , [0, 2.32382988929749, -1.4492199420929]
+  , [0, 2.32382988929749, -1.38047003746033]
+  , [0, 2.32382988929749, 1.38047003746033]
+  , [0, 2.32382988929749, 1.4492199420929]
+  , [0, 2.33906006813049, -1.15625]
+  , [0, 2.33906006813049, 1.15625]
+  , [0, 2.34843993186951, -1.40313005447388]
+  , [0, 2.34843993186951, 1.40313005447388]
+  , [0, 2.40000009536743, -0.824999988079071]
+  , [0, 2.40000009536743, 0.824999988079071]
+  , [0, 2.46093988418579, -0.456250011920929]
+  , [0, 2.46093988418579, 0.456250011920929]
+  , [0, 2.54999995231628, -0.200000002980232]
+  , [0, 2.54999995231628, 0.200000002980232]
+  , [0, 2.61240005493164, -0.166796997189522]
+  , [0, 2.61240005493164, 0.166796997189522]
+  , [0, 2.68358993530273, -0.196875005960464]
+  , [0, 2.68358993530273, 0.196875005960464]
+  , [0, 2.83124995231628, -0.324999988079071]
+  , [0, 2.83124995231628, 0.324999988079071]
+  , [0, 2.8971700668335, -0.362109005451202]
+  , [0, 2.8971700668335, 0.362109005451202]
+  , [0, 2.95077991485596, -0.340624988079071]
+  , [0, 2.95077991485596, 0.340624988079071]
+  , [0, 2.98681998252869, -0.230077996850014]
+  , [0, 2.98681998252869, 0.230077996850014]
+  , [0, 3, 0]
+  , [0.0227780006825924, 2.99341011047363, -0.11064899712801]
+  , [0.0227780006825924, 2.99341011047363, 0.11064899712801]
+  , [0.0357270017266273, 2.64800000190735, -0.174804002046585]
+  , [0.0357270017266273, 2.64800000190735, 0.174804002046585]
+  , [0.0360010005533695, 2.58119988441467, -0.17629499733448]
+  , [0.0360010005533695, 2.58119988441467, 0.17629499733448]
+  , [0.0513469986617565, 2.7574200630188, -0.250871986150742]
+  , [0.0513469986617565, 2.7574200630188, 0.250871986150742]
+  , [0.0563799999654293, 2.96880006790161, -0.274421006441116]
+  , [0.0563799999654293, 2.96880006790161, 0.274421006441116]
+  , [0.0637969970703125, 2.99341011047363, -0.0941469967365265]
+  , [0.0637969970703125, 2.99341011047363, 0.0941469967365265]
+  , [0.0643950030207634, 2.50547003746033, -0.315409988164902]
+  , [0.0643950030207634, 2.50547003746033, 0.315409988164902]
+  , [0.0648249983787537, 2.43046998977661, -0.631998002529144]
+  , [0.0648249983787537, 2.43046998977661, 0.631998002529144]
+  , [0.0655039995908737, 2.61240005493164, -0.153881996870041]
+  , [0.0655039995908737, 2.61240005493164, 0.153881996870041]
+  , [0.067671999335289, 2.8642098903656, -0.33032500743866]
+  , [0.067671999335289, 2.8642098903656, 0.33032500743866]
+  , [0.0692780017852783, 2.92396998405457, -0.337859004735947]
+  , [0.0692780017852783, 2.92396998405457, 0.337859004735947]
+  , [0.0774049982428551, 2.68358993530273, -0.181660994887352]
+  , [0.0774049982428551, 2.68358993530273, 0.181660994887352]
+  , [0.0785000026226044, 2.54999995231628, -0.184499993920326]
+  , [0.0785000026226044, 2.54999995231628, 0.184499993920326]
+  , [0.0911130011081696, 2.98681998252869, -0.212515994906425]
+  , [0.0911130011081696, 2.98681998252869, 0.212515994906425]
+  , [0.0912069976329803, 2.46093988418579, -0.438241004943848]
+  , [0.0912069976329803, 2.46093988418579, 0.438241004943848]
+  , [0.0941469967365265, 2.99341011047363, -0.0637969970703125]
+  , [0.0941469967365265, 2.99341011047363, 0.0637969970703125]
+  , [0.100316002964973, 2.64800000190735, -0.148475006222725]
+  , [0.100316002964973, 2.64800000190735, 0.148475006222725]
+  , [0.100919999182224, 2.36952996253967, -0.980718970298767]
+  , [0.100919999182224, 2.36952996253967, 0.980718970298767]
+  , [0.101116001605988, 2.58119988441467, -0.14970999956131]
+  , [0.101116001605988, 2.58119988441467, 0.14970999956131]
+  , [0.11064899712801, 2.99341011047363, -0.0227780006825924]
+  , [0.11064899712801, 2.99341011047363, 0.0227780006825924]
+  , [0.11845800280571, 2.61240005493164, -0.11845800280571]
+  , [0.11845800280571, 2.61240005493164, 0.11845800280571]
+  , [0.123125001788139, 2.31943011283875, -1.19650995731354]
+  , [0.123125001788139, 2.31943011283875, 1.19650995731354]
+  , [0.127984002232552, 2.83124995231628, -0.299953013658524]
+  , [0.127984002232552, 2.83124995231628, 0.299953013658524]
+  , [0.130447998642921, 2.27489995956421, -1.26766002178192]
+  , [0.130447998642921, 2.27489995956421, 1.26766002178192]
+  , [0.134406998753548, 2.95077991485596, -0.314464002847672]
+  , [0.134406998753548, 2.95077991485596, 0.314464002847672]
+  , [0.139898002147675, 2.68358993530273, -0.139898002147675]
+  , [0.139898002147675, 2.68358993530273, 0.139898002147675]
+  , [0.141629993915558, 2.28691005706787, -1.37633001804352]
+  , [0.141629993915558, 2.28691005706787, 1.37633001804352]
+  , [0.141789004206657, 2.33612990379333, -1.37787997722626]
+  , [0.141789004206657, 2.33612990379333, 1.37787997722626]
+  , [0.142000004649162, 2.54999995231628, -0.142000004649162]
+  , [0.142000004649162, 2.54999995231628, 0.142000004649162]
+  , [0.142704993486404, 2.8971700668335, -0.334237992763519]
+  , [0.142704993486404, 2.8971700668335, 0.334237992763519]
+  , [0.14410300552845, 2.7574200630188, -0.213158994913101]
+  , [0.14410300552845, 2.7574200630188, 0.213158994913101]
+  , [0.14529100060463, 2.33612990379333, -1.41191005706787]
+  , [0.14529100060463, 2.33612990379333, 1.41191005706787]
+  , [0.148475006222725, 2.64800000190735, -0.100316002964973]
+  , [0.148475006222725, 2.64800000190735, 0.100316002964973]
+  , [0.14970999956131, 2.58119988441467, -0.101116001605988]
+  , [0.14970999956131, 2.58119988441467, 0.101116001605988]
+  , [0.15022599697113, 2.28691005706787, -1.45985996723175]
+  , [0.15022599697113, 2.28691005706787, 1.45985996723175]
+  , [0.153881996870041, 2.61240005493164, -0.0655039995908737]
+  , [0.153881996870041, 2.61240005493164, 0.0655039995908737]
+  , [0.156791999936104, 0.042773000895977, -1.52366995811462]
+  , [0.156791999936104, 0.042773000895977, 1.52366995811462]
+  , [0.158017992973328, 2.96880006790161, -0.233382001519203]
+  , [0.158017992973328, 2.96880006790161, 0.233382001519203]
+  , [0.164073005318642, 2.98681998252869, -0.164073005318642]
+  , [0.164073005318642, 2.98681998252869, 0.164073005318642]
+  , [0.166796997189522, 2.61240005493164, 0]
+  , [0.168093994259834, 2.40000009536743, -0.808499991893768]
+  , [0.168093994259834, 2.40000009536743, 0.808499991893768]
+  , [0.169525995850563, 0.159961000084877, -1.64742004871368]
+  , [0.169525995850563, 0.159961000084877, 1.64742004871368]
+  , [0.170322000980377, 1.86093997955322, -1.65515995025635]
+  , [0.170322000980377, 1.86093997955322, 1.65515995025635]
+  , [0.174804002046585, 2.64800000190735, -0.0357270017266273]
+  , [0.174804002046585, 2.64800000190735, 0.0357270017266273]
+  , [0.17629499733448, 2.58119988441467, -0.0360010005533695]
+  , [0.17629499733448, 2.58119988441467, 0.0360010005533695]
+  , [0.179077997803688, 2.46093988418579, -0.420890986919403]
+  , [0.179077997803688, 2.46093988418579, 0.420890986919403]
+  , [0.18087899684906, 2.50547003746033, -0.267832010984421]
+  , [0.18087899684906, 2.50547003746033, 0.267832010984421]
+  , [0.181660994887352, 2.68358993530273, -0.0774049982428551]
+  , [0.181660994887352, 2.68358993530273, 0.0774049982428551]
+  , [0.184499993920326, 2.54999995231628, -0.0785000026226044]
+  , [0.184499993920326, 2.54999995231628, 0.0785000026226044]
+  , [0.187035992741585, 0.343944996595383, -1.81757998466492]
+  , [0.187035992741585, 0.343944996595383, 1.81757998466492]
+  , [0.189855992794037, 2.8642098903656, -0.280732005834579]
+  , [0.189855992794037, 2.8642098903656, 0.280732005834579]
+  , [0.190548002719879, 2.43046998977661, -0.607173979282379]
+  , [0.190548002719879, 2.43046998977661, 0.607173979282379]
+  , [0.193601995706558, 1.28612995147705, -1.88138997554779]
+  , [0.193601995706558, 1.28612995147705, 1.88138997554779]
+  , [0.1942999958992, 2.92396998405457, -0.28719699382782]
+  , [0.1942999958992, 2.92396998405457, 0.28719699382782]
+  , [0.196875005960464, 2.68358993530273, 0]
+  , [0.198676005005836, 0.5225830078125, -1.93069005012512]
+  , [0.198676005005836, 0.5225830078125, 1.93069005012512]
+  , [0.200000002980232, 2.54999995231628, 0]
+  , [0.201561003923416, 0.925194978713989, -1.9587299823761]
+  , [0.201561003923416, 0.925194978713989, 1.9587299823761]
+  , [0.202656000852585, 0.670825004577637, -1.96937000751495]
+  , [0.202656000852585, 0.670825004577637, 1.96937000751495]
+  , [0.212515994906425, 2.98681998252869, -0.0911130011081696]
+  , [0.212515994906425, 2.98681998252869, 0.0911130011081696]
+  , [0.213158994913101, 2.7574200630188, -0.14410300552845]
+  , [0.213158994913101, 2.7574200630188, 0.14410300552845]
+  , [0.230077996850014, 2.98681998252869, 0]
+  , [0.231124997138977, 2.83124995231628, -0.231124997138977]
+  , [0.231124997138977, 2.83124995231628, 0.231124997138977]
+  , [0.233382001519203, 2.96880006790161, -0.158017992973328]
+  , [0.233382001519203, 2.96880006790161, 0.158017992973328]
+  , [0.235586002469063, 2.33906006813049, -1.13311994075775]
+  , [0.235586002469063, 2.33906006813049, 1.13311994075775]
+  , [0.242476999759674, 2.95077991485596, -0.242476999759674]
+  , [0.242476999759674, 2.95077991485596, 0.242476999759674]
+  , [0.250871986150742, 2.7574200630188, -0.0513469986617565]
+  , [0.250871986150742, 2.7574200630188, 0.0513469986617565]
+  , [0.251888990402222, 2.46093988418579, -0.372159004211426]
+  , [0.251888990402222, 2.46093988418579, 0.372159004211426]
+  , [0.256915986537933, 2.29979991912842, -1.235720038414]
+  , [0.256915986537933, 2.29979991912842, 1.235720038414]
+  , [0.257609993219376, 2.8971700668335, -0.257609993219376]
+  , [0.257609993219376, 2.8971700668335, 0.257609993219376]
+  , [0.264874994754791, 2.25, -1.27400004863739]
+  , [0.264874994754791, 2.25, 1.27400004863739]
+  , [0.267832010984421, 2.50547003746033, -0.18087899684906]
+  , [0.267832010984421, 2.50547003746033, 0.18087899684906]
+  , [0.274421006441116, 2.96880006790161, -0.0563799999654293]
+  , [0.274421006441116, 2.96880006790161, 0.0563799999654293]
+  , [0.280732005834579, 2.8642098903656, -0.189855992794037]
+  , [0.280732005834579, 2.8642098903656, 0.189855992794037]
+  , [0.281271010637283, 2.32382988929749, -1.35285997390747]
+  , [0.281271010637283, 2.32382988929749, 1.35285997390747]
+  , [0.285250008106232, 2.25, -1.37199997901917]
+  , [0.285250008106232, 2.25, 1.37199997901917]
+  , [0.285887002944946, 2.34843993186951, -1.37505996227264]
+  , [0.285887002944946, 2.34843993186951, 1.37505996227264]
+  , [0.28719699382782, 2.92396998405457, -0.1942999958992]
+  , [0.28719699382782, 2.92396998405457, 0.1942999958992]
+  , [0.295278012752533, 2.32382988929749, -1.42023003101349]
+  , [0.295278012752533, 2.32382988929749, 1.42023003101349]
+  , [0.295329988002777, 2.36952996253967, -0.942332029342651]
+  , [0.295329988002777, 2.36952996253967, 0.942332029342651]
+  , [0.299953013658524, 2.83124995231628, -0.127984002232552]
+  , [0.299953013658524, 2.83124995231628, 0.127984002232552]
+  , [0.304711014032364, 2.43046998977661, -0.559973001480103]
+  , [0.304711014032364, 2.43046998977661, 0.559973001480103]
+  , [0.30562499165535, 0, -1.47000002861023]
+  , [0.30562499165535, 0, 1.47000002861023]
+  , [0.30562499165535, 2.25, -1.47000002861023]
+  , [0.30562499165535, 2.25, 1.47000002861023]
+  , [0.314464002847672, 2.95077991485596, -0.134406998753548]
+  , [0.314464002847672, 2.95077991485596, 0.134406998753548]
+  , [0.315409988164902, 2.50547003746033, -0.0643950030207634]
+  , [0.315409988164902, 2.50547003746033, 0.0643950030207634]
+  , [0.321543008089066, 0.085547000169754, -1.54656004905701]
+  , [0.321543008089066, 0.085547000169754, 1.54656004905701]
+  , [0.323812991380692, 2.40000009536743, -0.761062979698181]
+  , [0.323812991380692, 2.40000009536743, 0.761062979698181]
+  , [0.323938012123108, 2.46093988418579, -0.323938012123108]
+  , [0.323938012123108, 2.46093988418579, 0.323938012123108]
+  , [0.324999988079071, 2.83124995231628, 0]
+  , [0.33032500743866, 2.8642098903656, -0.067671999335289]
+  , [0.33032500743866, 2.8642098903656, 0.067671999335289]
+  , [0.334237992763519, 2.8971700668335, -0.142704993486404]
+  , [0.334237992763519, 2.8971700668335, 0.142704993486404]
+  , [0.337859004735947, 2.92396998405457, -0.0692780017852783]
+  , [0.337859004735947, 2.92396998405457, 0.0692780017852783]
+  , [0.340624988079071, 2.95077991485596, 0]
+  , [0.356561988592148, 0.234375, 1.7150000333786]
+  , [0.356563001871109, 0.234375, -1.7150000333786]
+  , [0.360312014818192, 2.31943011283875, -1.14968001842499]
+  , [0.360312014818192, 2.31943011283875, 1.14968001842499]
+  , [0.362109005451202, 2.8971700668335, 0]
+  , [0.372159004211426, 2.46093988418579, -0.251888990402222]
+  , [0.372159004211426, 2.46093988418579, 0.251888990402222]
+  , [0.375663995742798, 1.47186994552612, -1.8068699836731]
+  , [0.375663995742798, 1.47186994552612, 1.8068699836731]
+  , [0.381740003824234, 2.27489995956421, -1.21805000305176]
+  , [0.381740003824234, 2.27489995956421, 1.21805000305176]
+  , [0.391582012176514, 0.453516006469727, -1.8834400177002]
+  , [0.391582012176514, 0.453516006469727, 1.8834400177002]
+  , [0.398745000362396, 1.10038995742798, -1.91788995265961]
+  , [0.398745000362396, 1.10038995742798, 1.91788995265961]
+  , [0.403122991323471, 0.591650009155273, -1.93894994258881]
+  , [0.403122991323471, 0.591650009155273, 1.93894994258881]
+  , [0.4064100086689, 2.43046998977661, -0.491907000541687]
+  , [0.4064100086689, 2.43046998977661, 0.491907000541687]
+  , [0.407499998807907, 0.75, -1.96000003814697]
+  , [0.407499998807907, 0.75, 1.96000003814697]
+  , [0.414463996887207, 2.28691005706787, -1.32246005535126]
+  , [0.414463996887207, 2.28691005706787, 1.32246005535126]
+  , [0.414929002523422, 2.33612990379333, -1.32395005226135]
+  , [0.414929002523422, 2.33612990379333, 1.32395005226135]
+  , [0.420890986919403, 2.46093988418579, -0.179077997803688]
+  , [0.420890986919403, 2.46093988418579, 0.179077997803688]
+  , [0.425177007913589, 2.33612990379333, -1.35664999485016]
+  , [0.425177007913589, 2.33612990379333, 1.35664999485016]
+  , [0.438241004943848, 2.46093988418579, -0.0912069976329803]
+  , [0.438241004943848, 2.46093988418579, 0.0912069976329803]
+  , [0.439617991447449, 2.28691005706787, -1.40271997451782]
+  , [0.439617991447449, 2.28691005706787, 1.40271997451782]
+  , [0.453828006982803, 2.33906006813049, -1.06664001941681]
+  , [0.453828006982803, 2.33906006813049, 1.06664001941681]
+  , [0.456250011920929, 2.46093988418579, 0]
+  , [0.458833009004593, 0.042773000895977, -1.46403002738953]
+  , [0.458833009004593, 0.042773000895977, 1.46403002738953]
+  , [0.464062988758087, 2.40000009536743, -0.685781002044678]
+  , [0.464062988758087, 2.40000009536743, 0.685781002044678]
+  , [0.473022997379303, 2.36952996253967, -0.868654012680054]
+  , [0.473022997379303, 2.36952996253967, 0.868654012680054]
+  , [0.491907000541687, 2.43046998977661, -0.4064100086689]
+  , [0.491907000541687, 2.43046998977661, 0.4064100086689]
+  , [0.494917988777161, 2.29979991912842, -1.16322004795074]
+  , [0.494917988777161, 2.29979991912842, 1.16322004795074]
+  , [0.49609899520874, 0.159961000084877, -1.58293998241425]
+  , [0.49609899520874, 0.159961000084877, 1.58293998241425]
+  , [0.498427987098694, 1.86093997955322, -1.59037005901337]
+  , [0.498427987098694, 1.86093997955322, 1.59037005901337]
+  , [0.510249972343445, 2.25, -1.19924998283386]
+  , [0.510249972343445, 2.25, 1.19924998283386]
+  , [0.541833996772766, 2.32382988929749, -1.27348005771637]
+  , [0.541833996772766, 2.32382988929749, 1.27348005771637]
+  , [0.547339022159576, 0.343944996595383, -1.74644005298615]
+  , [0.547339022159576, 0.343944996595383, 1.74644005298615]
+  , [0.549499988555908, 2.25, -1.29149997234344]
+  , [0.549499988555908, 2.25, 1.29149997234344]
+  , [0.550727009773254, 2.34843993186951, -1.2943799495697]
+  , [0.550727009773254, 2.34843993186951, 1.2943799495697]
+  , [0.559973001480103, 2.43046998977661, -0.304711014032364]
+  , [0.559973001480103, 2.43046998977661, 0.304711014032364]
+  , [0.566554009914398, 1.28612995147705, -1.80774998664856]
+  , [0.566554009914398, 1.28612995147705, 1.80774998664856]
+  , [0.568817973136902, 2.32382988929749, -1.33689999580383]
+  , [0.568817973136902, 2.32382988929749, 1.33689999580383]
+  , [0.577103972434998, 2.31943011283875, -1.05979001522064]
+  , [0.577103972434998, 2.31943011283875, 1.05979001522064]
+  , [0.581402003765106, 0.5225830078125, -1.85511994361877]
+  , [0.581402003765106, 0.5225830078125, 1.85511994361877]
+  , [0.585749983787537, 2.40000009536743, -0.585749983787537]
+  , [0.585749983787537, 2.40000009536743, 0.585749983787537]
+  , [0.588750004768372, 0, -1.38374996185303]
+  , [0.588750004768372, 0, 1.38374996185303]
+  , [0.588750004768372, 2.25, -1.38374996185303]
+  , [0.588750004768372, 2.25, 1.38374996185303]
+  , [0.58984500169754, 0.925194978713989, -1.88206005096436]
+  , [0.58984500169754, 0.925194978713989, 1.88206005096436]
+  , [0.593047022819519, 0.670825004577637, -1.89227998256683]
+  , [0.593047022819519, 0.670825004577637, 1.89227998256683]
+  , [0.607173979282379, 2.43046998977661, -0.190548002719879]
+  , [0.607173979282379, 2.43046998977661, 0.190548002719879]
+  , [0.611424028873444, 2.27489995956421, -1.12281000614166]
+  , [0.611424028873444, 2.27489995956421, 1.12281000614166]
+  , [0.61941397190094, 0.085547000169754, -1.45581996440887]
+  , [0.61941397190094, 0.085547000169754, 1.45581996440887]
+  , [0.630285024642944, 2.36952996253967, -0.763400018215179]
+  , [0.630285024642944, 2.36952996253967, 0.763400018215179]
+  , [0.631998002529144, 2.43046998977661, -0.0648249983787537]
+  , [0.631998002529144, 2.43046998977661, 0.0648249983787537]
+  , [0.650390982627869, 2.33906006813049, -0.961133003234863]
+  , [0.650390982627869, 2.33906006813049, 0.961133003234863]
+  , [0.663837015628815, 2.28691005706787, -1.21905994415283]
+  , [0.663837015628815, 2.28691005706787, 1.21905994415283]
+  , [0.664583027362823, 2.33612990379333, -1.22043001651764]
+  , [0.664583027362823, 2.33612990379333, 1.22043001651764]
+  , [0.680997014045715, 2.33612990379333, -1.25057005882263]
+  , [0.680997014045715, 2.33612990379333, 1.25057005882263]
+  , [0.685781002044678, 2.40000009536743, -0.464062988758087]
+  , [0.685781002044678, 2.40000009536743, 0.464062988758087]
+  , [0.686874985694885, 0.234375, -1.61436998844147]
+  , [0.686874985694885, 0.234375, 1.61436998844147]
+  , [0.704126000404358, 2.28691005706787, -1.29305005073547]
+  , [0.704126000404358, 2.28691005706787, 1.29305005073547]
+  , [0.709276974201202, 2.29979991912842, -1.04814994335175]
+  , [0.709276974201202, 2.29979991912842, 1.04814994335175]
+  , [0.723671972751617, 1.47186994552612, -1.70086002349854]
+  , [0.723671972751617, 1.47186994552612, 1.70086002349854]
+  , [0.731249988079071, 2.25, -1.08062994480133]
+  , [0.731249988079071, 2.25, 1.08062994480133]
+  , [0.734902024269104, 0.042773000895977, -1.34957003593445]
+  , [0.734902024269104, 0.042773000895977, 1.34957003593445]
+  , [0.754335999488831, 0.453516006469727, -1.77293002605438]
+  , [0.754335999488831, 0.453516006469727, 1.77293002605438]
+  , [0.761062979698181, 2.40000009536743, -0.323812991380692]
+  , [0.761062979698181, 2.40000009536743, 0.323812991380692]
+  , [0.763400018215179, 2.36952996253967, -0.630285024642944]
+  , [0.763400018215179, 2.36952996253967, 0.630285024642944]
+  , [0.768135011196136, 1.10038995742798, -1.80535995960236]
+  , [0.768135011196136, 1.10038995742798, 1.80535995960236]
+  , [0.768967986106873, 2.31943011283875, -0.931373000144958]
+  , [0.768967986106873, 2.31943011283875, 0.931373000144958]
+  , [0.776513993740082, 2.32382988929749, -1.14751994609833]
+  , [0.776513993740082, 2.32382988929749, 1.14751994609833]
+  , [0.776566982269287, 0.591650009155273, -1.82518005371094]
+  , [0.776566982269287, 0.591650009155273, 1.82518005371094]
+  , [0.785000026226044, 0.75, -1.84500002861023]
+  , [0.785000026226044, 0.75, 1.84500002861023]
+  , [0.787500023841858, 2.25, -1.16375005245209]
+  , [0.787500023841858, 2.25, 1.16375005245209]
+  , [0.789258003234863, 2.34843993186951, -1.16635000705719]
+  , [0.789258003234863, 2.34843993186951, 1.16635000705719]
+  , [0.794589996337891, 0.159961000084877, -1.45916998386383]
+  , [0.794589996337891, 0.159961000084877, 1.45916998386383]
+  , [0.79831999540329, 1.86093997955322, -1.46601998806]
+  , [0.79831999540329, 1.86093997955322, 1.46601998806]
+  , [0.808499991893768, 2.40000009536743, -0.168093994259834]
+  , [0.808499991893768, 2.40000009536743, 0.168093994259834]
+  , [0.814697980880737, 2.27489995956421, -0.986760973930359]
+  , [0.814697980880737, 2.27489995956421, 0.986760973930359]
+  , [0.815186023712158, 2.32382988929749, -1.20466005802155]
+  , [0.815186023712158, 2.32382988929749, 1.20466005802155]
+  , [0.820937991142273, 2.33906006813049, -0.820937991142273]
+  , [0.820937991142273, 2.33906006813049, 0.820937991142273]
+  , [0.824999988079071, 2.40000009536743, 0]
+  , [0.84375, 0, -1.24688005447388]
+  , [0.84375, 0, 1.24688005447388]
+  , [0.84375, 2.25, -1.24688005447388]
+  , [0.84375, 2.25, 1.24688005447388]
+  , [0.868654012680054, 2.36952996253967, -0.473022997379303]
+  , [0.868654012680054, 2.36952996253967, 0.473022997379303]
+  , [0.876659989356995, 0.343944996595383, -1.60988998413086]
+  , [0.876659989356995, 0.343944996595383, 1.60988998413086]
+  , [0.884536981582642, 2.28691005706787, -1.07134997844696]
+  , [0.884536981582642, 2.28691005706787, 1.07134997844696]
+  , [0.885531008243561, 2.33612990379333, -1.07255005836487]
+  , [0.885531008243561, 2.33612990379333, 1.07255005836487]
+  , [0.887695014476776, 0.085547000169754, -1.3118200302124]
+  , [0.887695014476776, 0.085547000169754, 1.3118200302124]
+  , [0.895265996456146, 2.29979991912842, -0.895265996456146]
+  , [0.895265996456146, 2.29979991912842, 0.895265996456146]
+  , [0.907401978969574, 2.33612990379333, -1.09904003143311]
+  , [0.907401978969574, 2.33612990379333, 1.09904003143311]
+  , [0.907437026500702, 1.28612995147705, -1.66639995574951]
+  , [0.907437026500702, 1.28612995147705, 1.66639995574951]
+  , [0.922999978065491, 2.25, -0.922999978065491]
+  , [0.922999978065491, 2.25, 0.922999978065491]
+  , [0.931218028068542, 0.5225830078125, -1.71008002758026]
+  , [0.931218028068542, 0.5225830078125, 1.71008002758026]
+  , [0.931373000144958, 2.31943011283875, -0.768967986106873]
+  , [0.931373000144958, 2.31943011283875, 0.768967986106873]
+  , [0.938220024108887, 2.28691005706787, -1.13636994361877]
+  , [0.938220024108887, 2.28691005706787, 1.13636994361877]
+  , [0.942332029342651, 2.36952996253967, -0.295329988002777]
+  , [0.942332029342651, 2.36952996253967, 0.295329988002777]
+  , [0.944741010665894, 0.925194978713989, -1.7349100112915]
+  , [0.944741010665894, 0.925194978713989, 1.7349100112915]
+  , [0.949871003627777, 0.670825004577637, -1.7443300485611]
+  , [0.949871003627777, 0.670825004577637, 1.7443300485611]
+  , [0.961133003234863, 2.33906006813049, -0.650390982627869]
+  , [0.961133003234863, 2.33906006813049, 0.650390982627869]
+  , [0.979228973388672, 0.042773000895977, -1.18604004383087]
+  , [0.979228973388672, 0.042773000895977, 1.18604004383087]
+  , [0.98013299703598, 2.32382988929749, -0.98013299703598]
+  , [0.98013299703598, 2.32382988929749, 0.98013299703598]
+  , [0.980718970298767, 2.36952996253967, -0.100919999182224]
+  , [0.980718970298767, 2.36952996253967, 0.100919999182224]
+  , [0.984375, 0.234375, -1.45468997955322]
+  , [0.984375, 0.234375, 1.45468997955322]
+  , [0.986760973930359, 2.27489995956421, -0.814697980880737]
+  , [0.986760973930359, 2.27489995956421, 0.814697980880737]
+  , [0.994000017642975, 2.25, -0.994000017642975]
+  , [0.994000017642975, 2.25, 0.994000017642975]
+  , [0.996218979358673, 2.34843993186951, -0.996218979358673]
+  , [0.996218979358673, 2.34843993186951, 0.996218979358673]
+  , [1.02893996238708, 2.32382988929749, -1.02893996238708]
+  , [1.02893996238708, 2.32382988929749, 1.02893996238708]
+  , [1.03710997104645, 1.47186994552612, -1.53261995315552]
+  , [1.03710997104645, 1.47186994552612, 1.53261995315552]
+  , [1.04814994335175, 2.29979991912842, -0.709276974201202]
+  , [1.04814994335175, 2.29979991912842, 0.709276974201202]
+  , [1.05876004695892, 0.159961000084877, -1.28236997127533]
+  , [1.05876004695892, 0.159961000084877, 1.28236997127533]
+  , [1.05979001522064, 2.31943011283875, -0.577103972434998]
+  , [1.05979001522064, 2.31943011283875, 0.577103972434998]
+  , [1.06373000144958, 1.86093997955322, -1.28839004039764]
+  , [1.06373000144958, 1.86093997955322, 1.28839004039764]
+  , [1.06500005722046, 0, -1.06500005722046]
+  , [1.06500005722046, 0, 1.06500005722046]
+  , [1.06500005722046, 2.25, -1.06500005722046]
+  , [1.06500005722046, 2.25, 1.06500005722046]
+  , [1.06664001941681, 2.33906006813049, -0.453828006982803]
+  , [1.06664001941681, 2.33906006813049, 0.453828006982803]
+  , [1.07134997844696, 2.28691005706787, -0.884536981582642]
+  , [1.07134997844696, 2.28691005706787, 0.884536981582642]
+  , [1.07255005836487, 2.33612990379333, -0.885531008243561]
+  , [1.07255005836487, 2.33612990379333, 0.885531008243561]
+  , [1.08062994480133, 2.25, -0.731249988079071]
+  , [1.08062994480133, 2.25, 0.731249988079071]
+  , [1.08106005191803, 0.453516006469727, -1.59756004810333]
+  , [1.08106005191803, 0.453516006469727, 1.59756004810333]
+  , [1.09904003143311, 2.33612990379333, -0.907401978969574]
+  , [1.09904003143311, 2.33612990379333, 0.907401978969574]
+  , [1.10082995891571, 1.10038995742798, -1.62678003311157]
+  , [1.10082995891571, 1.10038995742798, 1.62678003311157]
+  , [1.11292004585266, 0.591650009155273, -1.64463996887207]
+  , [1.11292004585266, 0.591650009155273, 1.64463996887207]
+  , [1.12047004699707, 0.085547000169754, -1.12047004699707]
+  , [1.12047004699707, 0.085547000169754, 1.12047004699707]
+  , [1.12281000614166, 2.27489995956421, -0.611424028873444]
+  , [1.12281000614166, 2.27489995956421, 0.611424028873444]
+  , [1.125, 0.75, -1.66250002384186]
+  , [1.125, 0.75, 1.66250002384186]
+  , [1.13311994075775, 2.33906006813049, -0.235586002469063]
+  , [1.13311994075775, 2.33906006813049, 0.235586002469063]
+  , [1.13636994361877, 2.28691005706787, -0.938220024108887]
+  , [1.13636994361877, 2.28691005706787, 0.938220024108887]
+  , [1.14751994609833, 2.32382988929749, -0.776513993740082]
+  , [1.14751994609833, 2.32382988929749, 0.776513993740082]
+  , [1.14968001842499, 2.31943011283875, -0.360312014818192]
+  , [1.14968001842499, 2.31943011283875, 0.360312014818192]
+  , [1.15625, 2.33906006813049, 0]
+  , [1.16322004795074, 2.29979991912842, -0.494917988777161]
+  , [1.16322004795074, 2.29979991912842, 0.494917988777161]
+  , [1.16375005245209, 2.25, -0.787500023841858]
+  , [1.16375005245209, 2.25, 0.787500023841858]
+  , [1.16635000705719, 2.34843993186951, -0.789258003234863]
+  , [1.16635000705719, 2.34843993186951, 0.789258003234863]
+  , [1.16812002658844, 0.343944996595383, -1.41481995582581]
+  , [1.16812002658844, 0.343944996595383, 1.41481995582581]
+  , [1.18604004383087, 0.042773000895977, -0.979228973388672]
+  , [1.18604004383087, 0.042773000895977, 0.979228973388672]
+  , [1.19650995731354, 2.31943011283875, -0.123125001788139]
+  , [1.19650995731354, 2.31943011283875, 0.123125001788139]
+  , [1.19924998283386, 2.25, -0.510249972343445]
+  , [1.19924998283386, 2.25, 0.510249972343445]
+  , [1.20466005802155, 2.32382988929749, -0.815186023712158]
+  , [1.20466005802155, 2.32382988929749, 0.815186023712158]
+  , [1.20912003517151, 1.28612995147705, -1.4644900560379]
+  , [1.20912003517151, 1.28612995147705, 1.4644900560379]
+  , [1.21805000305176, 2.27489995956421, -0.381740003824234]
+  , [1.21805000305176, 2.27489995956421, 0.381740003824234]
+  , [1.21905994415283, 2.28691005706787, -0.663837015628815]
+  , [1.21905994415283, 2.28691005706787, 0.663837015628815]
+  , [1.22043001651764, 2.33612990379333, -0.664583027362823]
+  , [1.22043001651764, 2.33612990379333, 0.664583027362823]
+  , [1.235720038414, 2.29979991912842, -0.256915986537933]
+  , [1.235720038414, 2.29979991912842, 0.256915986537933]
+  , [1.24081003665924, 0.5225830078125, -1.50286996364594]
+  , [1.24081003665924, 0.5225830078125, 1.50286996364594]
+  , [1.24249994754791, 0.234375, -1.24249994754791]
+  , [1.24249994754791, 0.234375, 1.24249994754791]
+  , [1.24688005447388, 0, -0.84375]
+  , [1.24688005447388, 0, 0.84375]
+  , [1.24688005447388, 2.25, -0.84375]
+  , [1.24688005447388, 2.25, 0.84375]
+  , [1.25057005882263, 2.33612990379333, -0.680997014045715]
+  , [1.25057005882263, 2.33612990379333, 0.680997014045715]
+  , [1.25882995128632, 0.925194978713989, -1.52469003200531]
+  , [1.25882995128632, 0.925194978713989, 1.52469003200531]
+  , [1.26093995571136, 2.29979991912842, 0]
+  , [1.26566994190216, 0.670825004577637, -1.53296995162964]
+  , [1.26566994190216, 0.670825004577637, 1.53296995162964]
+  , [1.26766002178192, 2.27489995956421, -0.130447998642921]
+  , [1.26766002178192, 2.27489995956421, 0.130447998642921]
+  , [1.27348005771637, 2.32382988929749, -0.541833996772766]
+  , [1.27348005771637, 2.32382988929749, 0.541833996772766]
+  , [1.27400004863739, 2.25, -0.264874994754791]
+  , [1.27400004863739, 2.25, 0.264874994754791]
+  , [1.28236997127533, 0.159961000084877, -1.05876004695892]
+  , [1.28236997127533, 0.159961000084877, 1.05876004695892]
+  , [1.28839004039764, 1.86093997955322, -1.06373000144958]
+  , [1.28839004039764, 1.86093997955322, 1.06373000144958]
+  , [1.29149997234344, 2.25, -0.549499988555908]
+  , [1.29149997234344, 2.25, 0.549499988555908]
+  , [1.29305005073547, 2.28691005706787, -0.704126000404358]
+  , [1.29305005073547, 2.28691005706787, 0.704126000404358]
+  , [1.2943799495697, 2.34843993186951, -0.550727009773254]
+  , [1.2943799495697, 2.34843993186951, 0.550727009773254]
+  , [1.29999995231628, 2.25, 0]
+  , [1.30905997753143, 1.47186994552612, -1.30905997753143]
+  , [1.30905997753143, 1.47186994552612, 1.30905997753143]
+  , [1.3118200302124, 0.085547000169754, -0.887695014476776]
+  , [1.3118200302124, 0.085547000169754, 0.887695014476776]
+  , [1.32246005535126, 2.28691005706787, -0.414463996887207]
+  , [1.32246005535126, 2.28691005706787, 0.414463996887207]
+  , [1.32395005226135, 2.33612990379333, -0.414929002523422]
+  , [1.32395005226135, 2.33612990379333, 0.414929002523422]
+  , [1.33689999580383, 2.32382988929749, -0.568817973136902]
+  , [1.33689999580383, 2.32382988929749, 0.568817973136902]
+  , [1.34957003593445, 0.042773000895977, -0.734902024269104]
+  , [1.34957003593445, 0.042773000895977, 0.734902024269104]
+  , [1.35285997390747, 2.32382988929749, -0.281271010637283]
+  , [1.35285997390747, 2.32382988929749, 0.281271010637283]
+  , [1.35664999485016, 2.33612990379333, -0.425177007913589]
+  , [1.35664999485016, 2.33612990379333, 0.425177007913589]
+  , [1.36452996730804, 0.453516006469727, -1.36452996730804]
+  , [1.36452996730804, 0.453516006469727, 1.36452996730804]
+  , [1.37199997901917, 2.25, -0.285250008106232]
+  , [1.37199997901917, 2.25, 0.285250008106232]
+  , [1.37505996227264, 2.34843993186951, -0.285887002944946]
+  , [1.37505996227264, 2.34843993186951, 0.285887002944946]
+  , [1.37633001804352, 2.28691005706787, -0.141629993915558]
+  , [1.37633001804352, 2.28691005706787, 0.141629993915558]
+  , [1.37787997722626, 2.33612990379333, -0.141789004206657]
+  , [1.37787997722626, 2.33612990379333, 0.141789004206657]
+  , [1.38047003746033, 2.32382988929749, 0]
+  , [1.38374996185303, 0, -0.588750004768372]
+  , [1.38374996185303, 0, 0.588750004768372]
+  , [1.38374996185303, 2.25, -0.588750004768372]
+  , [1.38374996185303, 2.25, 0.588750004768372]
+  , [1.38949000835419, 1.10038995742798, -1.38949000835419]
+  , [1.38949000835419, 1.10038995742798, 1.38949000835419]
+  , [1.39999997615814, 2.25, 0]
+  , [1.40271997451782, 2.28691005706787, -0.439617991447449]
+  , [1.40271997451782, 2.28691005706787, 0.439617991447449]
+  , [1.40313005447388, 2.34843993186951, 0]
+  , [1.40474998950958, 0.591650009155273, -1.40474998950958]
+  , [1.40474998950958, 0.591650009155273, 1.40474998950958]
+  , [1.41191005706787, 2.33612990379333, -0.14529100060463]
+  , [1.41191005706787, 2.33612990379333, 0.14529100060463]
+  , [1.41481995582581, 0.343944996595383, -1.16812002658844]
+  , [1.41481995582581, 0.343944996595383, 1.16812002658844]
+  , [1.41999995708466, 0.75, -1.41999995708466]
+  , [1.41999995708466, 0.75, 1.41999995708466]
+  , [1.42023003101349, 2.32382988929749, -0.295278012752533]
+  , [1.42023003101349, 2.32382988929749, 0.295278012752533]
+  , [1.4492199420929, 2.32382988929749, 0]
+  , [1.45468997955322, 0.234375, -0.984375]
+  , [1.45468997955322, 0.234375, 0.984375]
+  , [1.45581996440887, 0.085547000169754, -0.61941397190094]
+  , [1.45581996440887, 0.085547000169754, 0.61941397190094]
+  , [1.45916998386383, 0.159961000084877, -0.794589996337891]
+  , [1.45916998386383, 0.159961000084877, 0.794589996337891]
+  , [1.45985996723175, 2.28691005706787, -0.15022599697113]
+  , [1.45985996723175, 2.28691005706787, 0.15022599697113]
+  , [1.46403002738953, 0.042773000895977, -0.458833009004593]
+  , [1.46403002738953, 0.042773000895977, 0.458833009004593]
+  , [1.4644900560379, 1.28612995147705, -1.20912003517151]
+  , [1.4644900560379, 1.28612995147705, 1.20912003517151]
+  , [1.46601998806, 1.86093997955322, -0.79831999540329]
+  , [1.46601998806, 1.86093997955322, 0.79831999540329]
+  , [1.47000002861023, 0, -0.30562499165535]
+  , [1.47000002861023, 0, 0.30562499165535]
+  , [1.47000002861023, 2.25, -0.30562499165535]
+  , [1.47000002861023, 2.25, 0.30562499165535]
+  , [1.5, 0, 0]
+  , [1.5, 2.25, 0]
+  , [1.50286996364594, 0.5225830078125, -1.24081003665924]
+  , [1.50286996364594, 0.5225830078125, 1.24081003665924]
+  , [1.52366995811462, 0.042773000895977, -0.156791999936104]
+  , [1.52366995811462, 0.042773000895977, 0.156791999936104]
+  , [1.52469003200531, 0.925194978713989, -1.25882995128632]
+  , [1.52469003200531, 0.925194978713989, 1.25882995128632]
+  , [1.53261995315552, 1.47186994552612, -1.03710997104645]
+  , [1.53261995315552, 1.47186994552612, 1.03710997104645]
+  , [1.53296995162964, 0.670825004577637, -1.26566994190216]
+  , [1.53296995162964, 0.670825004577637, 1.26566994190216]
+  , [1.54656004905701, 0.085547000169754, -0.321543008089066]
+  , [1.54656004905701, 0.085547000169754, 0.321543008089066]
+  , [1.57813000679016, 0.085547000169754, 0]
+  , [1.58293998241425, 0.159961000084877, -0.49609899520874]
+  , [1.58293998241425, 0.159961000084877, 0.49609899520874]
+  , [1.59037005901337, 1.86093997955322, -0.498427987098694]
+  , [1.59037005901337, 1.86093997955322, 0.498427987098694]
+  , [1.59756004810333, 0.453516006469727, -1.08106005191803]
+  , [1.59756004810333, 0.453516006469727, 1.08106005191803]
+  , [1.60988998413086, 0.343944996595383, -0.876659989356995]
+  , [1.60988998413086, 0.343944996595383, 0.876659989356995]
+  , [1.61436998844147, 0.234375, -0.686874985694885]
+  , [1.61436998844147, 0.234375, 0.686874985694885]
+  , [1.62678003311157, 1.10038995742798, -1.10082995891571]
+  , [1.62678003311157, 1.10038995742798, 1.10082995891571]
+  , [1.64463996887207, 0.591650009155273, -1.11292004585266]
+  , [1.64463996887207, 0.591650009155273, 1.11292004585266]
+  , [1.64742004871368, 0.159961000084877, -0.169525995850563]
+  , [1.64742004871368, 0.159961000084877, 0.169525995850563]
+  , [1.65515995025635, 1.86093997955322, -0.170322000980377]
+  , [1.65515995025635, 1.86093997955322, 0.170322000980377]
+  , [1.66250002384186, 0.75, -1.125]
+  , [1.66250002384186, 0.75, 1.125]
+  , [1.66639995574951, 1.28612995147705, -0.907437026500702]
+  , [1.66639995574951, 1.28612995147705, 0.907437026500702]
+  , [1.70000004768372, 0.449999988079071, 0]
+  , [1.70000004768372, 0.485448986291885, -0.216563001275063]
+  , [1.70000004768372, 0.485448986291885, 0.216563001275063]
+  , [1.70000004768372, 0.578905999660492, -0.371250003576279]
+  , [1.70000004768372, 0.578905999660492, 0.371250003576279]
+  , [1.70000004768372, 0.711035013198853, -0.464062988758087]
+  , [1.70000004768372, 0.711035013198853, 0.464062988758087]
+  , [1.70000004768372, 0.862500011920929, -0.495000004768372]
+  , [1.70000004768372, 0.862500011920929, 0.495000004768372]
+  , [1.70000004768372, 1.01397001743317, -0.464062988758087]
+  , [1.70000004768372, 1.01397001743317, 0.464062988758087]
+  , [1.70000004768372, 1.14609003067017, -0.371250003576279]
+  , [1.70000004768372, 1.14609003067017, 0.371250003576279]
+  , [1.70000004768372, 1.23954999446869, -0.216563001275063]
+  , [1.70000004768372, 1.23954999446869, 0.216563001275063]
+  , [1.70000004768372, 1.27499997615814, 0]
+  , [1.70086002349854, 1.47186994552612, -0.723671972751617]
+  , [1.70086002349854, 1.47186994552612, 0.723671972751617]
+  , [1.71008002758026, 0.5225830078125, -0.931218028068542]
+  , [1.71008002758026, 0.5225830078125, 0.931218028068542]
+  , [1.7150000333786, 0.234375, -0.356561988592148]
+  , [1.7150000333786, 0.234375, 0.356563001871109]
+  , [1.7349100112915, 0.925194978713989, -0.944741010665894]
+  , [1.7349100112915, 0.925194978713989, 0.944741010665894]
+  , [1.7443300485611, 0.670825004577637, -0.949871003627777]
+  , [1.7443300485611, 0.670825004577637, 0.949871003627777]
+  , [1.74644005298615, 0.343944996595383, -0.547339022159576]
+  , [1.74644005298615, 0.343944996595383, 0.547339022159576]
+  , [1.75, 0.234375, 0]
+  , [1.77293002605438, 0.453516006469727, -0.754335999488831]
+  , [1.77293002605438, 0.453516006469727, 0.754335999488831]
+  , [1.80535995960236, 1.10038995742798, -0.768135011196136]
+  , [1.80535995960236, 1.10038995742798, 0.768135011196136]
+  , [1.8068699836731, 1.47186994552612, -0.375663995742798]
+  , [1.8068699836731, 1.47186994552612, 0.375663995742798]
+  , [1.80774998664856, 1.28612995147705, -0.566554009914398]
+  , [1.80774998664856, 1.28612995147705, 0.566554009914398]
+  , [1.80868005752563, 0.669439971446991, -0.41533499956131]
+  , [1.80868005752563, 0.669439971446991, 0.41533499956131]
+  , [1.81523001194, 0.556497991085052, -0.292881011962891]
+  , [1.81523001194, 0.556497991085052, 0.292881011962891]
+  , [1.81757998466492, 0.343944996595383, -0.187035992741585]
+  , [1.81757998466492, 0.343944996595383, 0.187035992741585]
+  , [1.81850004196167, 0.493822991847992, -0.107904002070427]
+  , [1.81850004196167, 0.493822991847992, 0.107904002070427]
+  , [1.82518005371094, 0.591650009155273, -0.776566982269287]
+  , [1.82518005371094, 0.591650009155273, 0.776566982269287]
+  , [1.84375, 1.47186994552612, 0]
+  , [1.84407997131348, 1.2731100320816, -0.106835998594761]
+  , [1.84407997131348, 1.2731100320816, 0.106835998594761]
+  , [1.84500002861023, 0.75, -0.785000026226044]
+  , [1.84500002861023, 0.75, 0.785000026226044]
+  , [1.8498899936676, 1.21245002746582, -0.289983987808228]
+  , [1.8498899936676, 1.21245002746582, 0.289983987808228]
+  , [1.85511994361877, 0.5225830078125, -0.581402003765106]
+  , [1.85511994361877, 0.5225830078125, 0.581402003765106]
+  , [1.86006999015808, 1.10627996921539, -0.412081986665726]
+  , [1.86006999015808, 1.10627996921539, 0.412081986665726]
+  , [1.87285995483398, 0.972819983959198, -0.473131000995636]
+  , [1.87285995483398, 0.972819983959198, 0.473131000995636]
+  , [1.88138997554779, 1.28612995147705, -0.193601995706558]
+  , [1.88138997554779, 1.28612995147705, 0.193601995706558]
+  , [1.88206005096436, 0.925194978713989, -0.58984500169754]
+  , [1.88206005096436, 0.925194978713989, 0.58984500169754]
+  , [1.8834400177002, 0.453516006469727, -0.391582012176514]
+  , [1.8834400177002, 0.453516006469727, 0.391582012176514]
+  , [1.88652002811432, 0.830256998538971, -0.473131000995636]
+  , [1.88652002811432, 0.830256998538971, 0.473131000995636]
+  , [1.89227998256683, 0.670825004577637, -0.593047022819519]
+  , [1.89227998256683, 0.670825004577637, 0.593047022819519]
+  , [1.90898001194, 0.762850999832153, -0.457367986440659]
+  , [1.90898001194, 0.762850999832153, 0.457367986440659]
+  , [1.91788995265961, 1.10038995742798, -0.398745000362396]
+  , [1.91788995265961, 1.10038995742798, 0.398745000362396]
+  , [1.92188000679016, 0.453516006469727, 0]
+  , [1.92571997642517, 0.624967992305756, -0.368660002946854]
+  , [1.92571997642517, 0.624967992305756, 0.368660002946854]
+  , [1.93069005012512, 0.5225830078125, -0.198676005005836]
+  , [1.93069005012512, 0.5225830078125, 0.198676005005836]
+  , [1.93519997596741, 0.536666989326477, -0.215051993727684]
+  , [1.93519997596741, 0.536666989326477, 0.215051993727684]
+  , [1.93878996372223, 0.503174006938934, 0]
+  , [1.93894994258881, 0.591650009155273, -0.403122991323471]
+  , [1.93894994258881, 0.591650009155273, 0.403122991323471]
+  , [1.9570300579071, 1.10038995742798, 0]
+  , [1.9587299823761, 0.925194978713989, -0.201561003923416]
+  , [1.9587299823761, 0.925194978713989, 0.201561003923416]
+  , [1.96000003814697, 0.75, -0.407499998807907]
+  , [1.96000003814697, 0.75, 0.407499998807907]
+  , [1.96937000751495, 0.670825004577637, -0.202656000852585]
+  , [1.96937000751495, 0.670825004577637, 0.202656000852585]
+  , [1.97852003574371, 0.591650009155273, 0]
+  , [1.98495995998383, 1.30458998680115, 0]
+  , [1.99135994911194, 1.27330994606018, -0.210782006382942]
+  , [1.99135994911194, 1.27330994606018, 0.210782006382942]
+  , [2, 0.75, 0]
+  , [2.00798988342285, 0.721262991428375, -0.409761011600494]
+  , [2.00798988342285, 0.721262991428375, 0.409761011600494]
+  , [2.00820994377136, 1.19084000587463, -0.36133998632431]
+  , [2.00820994377136, 1.19084000587463, 0.36133998632431]
+  , [2.02470993995667, 0.614948987960815, -0.288958013057709]
+  , [2.02470993995667, 0.614948987960815, 0.288958013057709]
+  , [2.03204989433289, 1.07423996925354, -0.451674997806549]
+  , [2.03204989433289, 1.07423996925354, 0.451674997806549]
+  , [2.03379011154175, 0.556061983108521, -0.106458000838757]
+  , [2.03379011154175, 0.556061983108521, 0.106458000838757]
+  , [2.05938005447388, 0.940576016902924, -0.481786996126175]
+  , [2.05938005447388, 0.940576016902924, 0.481786996126175]
+  , [2.08644008636475, 1.33047997951508, -0.101580999791622]
+  , [2.08644008636475, 1.33047997951508, 0.101580999791622]
+  , [2.08669996261597, 0.806914985179901, -0.451674997806549]
+  , [2.08669996261597, 0.806914985179901, 0.451674997806549]
+  , [2.10140991210938, 1.27814996242523, -0.275720000267029]
+  , [2.10140991210938, 1.27814996242523, 0.275720000267029]
+  , [2.11052989959717, 0.69031697511673, -0.36133998632431]
+  , [2.11052989959717, 0.69031697511673, 0.36133998632431]
+  , [2.12738990783691, 0.60784500837326, -0.210782006382942]
+  , [2.12738990783691, 0.60784500837326, 0.210782006382942]
+  , [2.1275999546051, 1.18656003475189, -0.39181199669838]
+  , [2.1275999546051, 1.18656003475189, 0.39181199669838]
+  , [2.13379001617432, 0.576563000679016, 0]
+  , [2.16054010391235, 1.07142996788025, -0.449858993291855]
+  , [2.16054010391235, 1.07142996788025, 0.449858993291855]
+  , [2.16921997070312, 0.790259003639221, -0.399360001087189]
+  , [2.16921997070312, 0.790259003639221, 0.399360001087189]
+  , [2.17968988418579, 1.38515996932983, 0]
+  , [2.1897599697113, 1.35887002944946, -0.195541992783546]
+  , [2.1897599697113, 1.35887002944946, 0.195541992783546]
+  , [2.19480991363525, 0.691761016845703, -0.281558990478516]
+  , [2.19480991363525, 0.691761016845703, 0.281558990478516]
+  , [2.19570994377136, 0.948444008827209, -0.449858993291855]
+  , [2.19570994377136, 0.948444008827209, 0.449858993291855]
+  , [2.20836997032166, 0.637081980705261, -0.103731997311115]
+  , [2.20836997032166, 0.637081980705261, 0.103731997311115]
+  , [2.21631002426147, 1.28956997394562, -0.335215002298355]
+  , [2.21631002426147, 1.28956997394562, 0.335215002298355]
+  , [2.2202000617981, 0.891314029693604, -0.434457004070282]
+  , [2.2202000617981, 0.891314029693604, 0.434457004070282]
+  , [2.24856996536255, 1.43299996852875, -0.0923840031027794]
+  , [2.24856996536255, 1.43299996852875, 0.0923840031027794]
+  , [2.25383996963501, 1.19159996509552, -0.419019013643265]
+  , [2.25383996963501, 1.19159996509552, 0.419019013643265]
+  , [2.25943994522095, 0.772489011287689, -0.349967002868652]
+  , [2.25943994522095, 0.772489011287689, 0.349967002868652]
+  , [2.26856994628906, 1.39015996456146, -0.250757992267609]
+  , [2.26856994628906, 1.39015996456146, 0.250757992267609]
+  , [2.28188991546631, 0.696393013000488, -0.20414699614048]
+  , [2.28188991546631, 0.696393013000488, 0.20414699614048]
+  , [2.29041004180908, 0.667528986930847, 0]
+  , [2.29688000679016, 1.0793000459671, -0.446952998638153]
+  , [2.29688000679016, 1.0793000459671, 0.446952998638153]
+  , [2.29924988746643, 0.874952971935272, -0.384663999080658]
+  , [2.29924988746643, 0.874952971935272, 0.384663999080658]
+  , [2.30358004570007, 1.31519997119904, -0.356339991092682]
+  , [2.30358004570007, 1.31519997119904, 0.356339991092682]
+  , [2.30644011497498, 1.50440001487732, 0]
+  , [2.31838011741638, 1.48355996608734, -0.17399600148201]
+  , [2.31838011741638, 1.48355996608734, 0.17399600148201]
+  , [2.33068990707397, 0.784406006336212, -0.271218001842499]
+  , [2.33068990707397, 0.784406006336212, 0.271218001842499]
+  , [2.33991003036499, 0.966988980770111, -0.419019013643265]
+  , [2.33991003036499, 0.966988980770111, 0.419019013643265]
+  , [2.34758996963501, 0.734270989894867, -0.0999220013618469]
+  , [2.34758996963501, 0.734270989894867, 0.0999220013618469]
+  , [2.34758996963501, 1.22096002101898, -0.409130990505219]
+  , [2.34758996963501, 1.22096002101898, 0.409130990505219]
+  , [2.34983992576599, 1.42864000797272, -0.298278987407684]
+  , [2.34983992576599, 1.42864000797272, 0.298278987407684]
+  , [2.35317993164062, 1.56816005706787, -0.0808229967951775]
+  , [2.35317993164062, 1.56816005706787, 0.0808229967951775]
+  , [2.37575006484985, 1.53531002998352, -0.219376996159554]
+  , [2.37575006484985, 1.53531002998352, 0.219376996159554]
+  , [2.37743997573853, 0.869018971920013, -0.335215002298355]
+  , [2.37743997573853, 0.869018971920013, 0.335215002298355]
+  , [2.38750004768372, 1.64999997615814, 0]
+  , [2.39432001113892, 1.35098004341125, -0.372848987579346]
+  , [2.39432001113892, 1.35098004341125, 0.372848987579346]
+  , [2.39459991455078, 1.12030005455017, -0.409130990505219]
+  , [2.39459991455078, 1.12030005455017, 0.409130990505219]
+  , [2.40038990974426, 1.63469004631042, -0.149296998977661]
+  , [2.40038990974426, 1.63469004631042, 0.149296998977661]
+  , [2.4039900302887, 0.799722015857697, -0.195541992783546]
+  , [2.4039900302887, 0.799722015857697, 0.195541992783546]
+  , [2.41406011581421, 0.773437976837158, 0]
+  , [2.41524004936218, 1.47781002521515, -0.311747014522552]
+  , [2.41524004936218, 1.47781002521515, 0.311747014522552]
+  , [2.43438005447388, 1.59433996677399, -0.255937993526459]
+  , [2.43438005447388, 1.59433996677399, 0.255937993526459]
+  , [2.4386100769043, 1.02605998516083, -0.356339991092682]
+  , [2.4386100769043, 1.02605998516083, 0.356339991092682]
+  , [2.44531011581421, 1.26196002960205, -0.397704988718033]
+  , [2.44531011581421, 1.26196002960205, 0.397704988718033]
+  , [2.45167994499207, 1.805340051651, -0.0630870014429092]
+  , [2.45167994499207, 1.805340051651, 0.0630870014429092]
+  , [2.46489000320435, 1.40551996231079, -0.357930988073349]
+  , [2.46489000320435, 1.40551996231079, 0.357930988073349]
+  , [2.47361993789673, 0.95109897851944, -0.250757992267609]
+  , [2.47361993789673, 0.95109897851944, 0.250757992267609]
+  , [2.47767996788025, 1.78638005256653, -0.171237006783485]
+  , [2.47767996788025, 1.78638005256653, 0.171237006783485]
+  , [2.48241996765137, 1.53727996349335, -0.319922000169754]
+  , [2.48241996765137, 1.53727996349335, 0.319922000169754]
+  , [2.49361991882324, 0.908263981342316, -0.0923840031027794]
+  , [2.49361991882324, 0.908263981342316, 0.0923840031027794]
+  , [2.49629998207092, 1.17295002937317, -0.372848987579346]
+  , [2.49629998207092, 1.17295002937317, 0.372848987579346]
+  , [2.50155997276306, 1.97108995914459, 0]
+  , [2.5172700881958, 1.9655499458313, -0.103051997721195]
+  , [2.5172700881958, 1.9655499458313, 0.103051997721195]
+  , [2.51792001724243, 1.32831001281738, -0.357930988073349]
+  , [2.51792001724243, 1.32831001281738, 0.357930988073349]
+  , [2.52318000793457, 1.75321996212006, -0.243336006999016]
+  , [2.52318000793457, 1.75321996212006, 0.243336006999016]
+  , [2.53749990463257, 1.47186994552612, -0.341250002384186]
+  , [2.53749990463257, 1.47186994552612, 0.341250002384186]
+  , [2.54078006744385, 1.09528994560242, -0.298278987407684]
+  , [2.54078006744385, 1.09528994560242, 0.298278987407684]
+  , [2.5491099357605, 2.0446400642395, -0.047715999186039]
+  , [2.5491099357605, 2.0446400642395, 0.047715999186039]
+  , [2.55869007110596, 1.95095002651215, -0.176660001277924]
+  , [2.55869007110596, 1.95095002651215, 0.176660001277924]
+  , [2.56756997108459, 1.25602996349335, -0.311747014522552]
+  , [2.56756997108459, 1.25602996349335, 0.311747014522552]
+  , [2.57224988937378, 1.04035997390747, -0.17399600148201]
+  , [2.57224988937378, 1.04035997390747, 0.17399600148201]
+  , [2.57909989356995, 2.1219699382782, 0]
+  , [2.58038997650146, 1.71152997016907, -0.279386013746262]
+  , [2.58038997650146, 1.71152997016907, 0.279386013746262]
+  , [2.58101010322571, 2.0377299785614, -0.129515007138252]
+  , [2.58101010322571, 2.0377299785614, 0.129515007138252]
+  , [2.58418011665344, 1.0195300579071, 0]
+  , [2.59258008003235, 1.40646994113922, -0.319922000169754]
+  , [2.59258008003235, 1.40646994113922, 0.319922000169754]
+  , [2.59848999977112, 2.11992001533508, -0.0878119990229607]
+  , [2.59848999977112, 2.11992001533508, 0.0878119990229607]
+  , [2.60177993774414, 1.55472004413605, -0.304019004106522]
+  , [2.60177993774414, 1.55472004413605, 0.304019004106522]
+  , [2.60706996917725, 1.19852995872498, -0.219376996159554]
+  , [2.60706996917725, 1.19852995872498, 0.219376996159554]
+  , [2.61161994934082, 1.69128000736237, -0.287907987833023]
+  , [2.61161994934082, 1.69128000736237, 0.287907987833023]
+  , [2.61724996566772, 1.93031001091003, -0.220825001597404]
+  , [2.61724996566772, 1.93031001091003, 0.220825001597404]
+  , [2.62963008880615, 1.16568005084991, -0.0808229967951775]
+  , [2.62963008880615, 1.16568005084991, 0.0808229967951775]
+  , [2.6378800868988, 2.02554988861084, -0.180818006396294]
+  , [2.6378800868988, 2.02554988861084, 0.180818006396294]
+  , [2.64063000679016, 1.34941005706787, -0.255937993526459]
+  , [2.64063000679016, 1.34941005706787, 0.255937993526459]
+  , [2.6496000289917, 2.11451005935669, -0.150535002350807]
+  , [2.6496000289917, 2.11451005935669, 0.150535002350807]
+  , [2.65084004402161, 2.18547010421753, -0.0424610003829002]
+  , [2.65084004402161, 2.18547010421753, 0.0424610003829002]
+  , [2.65390992164612, 1.50419998168945, -0.264113008975983]
+  , [2.65390992164612, 1.50419998168945, 0.264113008975983]
+  , [2.6654200553894, 1.64925003051758, -0.266995012760162]
+  , [2.6654200553894, 1.64925003051758, 0.266995012760162]
+  , [2.67460989952087, 1.30905997753143, -0.149296998977661]
+  , [2.67460989952087, 1.30905997753143, 0.149296998977661]
+  , [2.67823004722595, 1.78253996372223, -0.252819001674652]
+  , [2.67823004722595, 1.78253996372223, 0.252819001674652]
+  , [2.68438005447388, 1.90664005279541, -0.235547006130219]
+  , [2.68438005447388, 1.90664005279541, 0.235547006130219]
+  , [2.6875, 1.29375004768372, 0]
+  , [2.69190001487732, 2.18360996246338, -0.115250997245312]
+  , [2.69190001487732, 2.18360996246338, 0.115250997245312]
+  , [2.69644999504089, 1.46379995346069, -0.185856997966766]
+  , [2.69644999504089, 1.46379995346069, 0.185856997966766]
+  , [2.70000004768372, 2.25, 0]
+  , [2.70808005332947, 2.01037001609802, -0.208084002137184]
+  , [2.70808005332947, 2.01037001609802, 0.208084002137184]
+  , [2.71703004837036, 1.61167001724243, -0.213596001267433]
+  , [2.71703004837036, 1.61167001724243, 0.213596001267433]
+  , [2.72076010704041, 1.44071996212006, -0.0684740021824837]
+  , [2.72076010704041, 1.44071996212006, 0.0684740021824837]
+  , [2.72578001022339, 2.25, -0.0820309966802597]
+  , [2.72578001022339, 2.25, 0.0820309966802597]
+  , [2.72599005699158, 2.10643005371094, -0.175249993801117]
+  , [2.72599005699158, 2.10643005371094, 0.175249993801117]
+  , [2.73600006103516, 1.75154995918274, -0.219519004225731]
+  , [2.73600006103516, 1.75154995918274, 0.219519004225731]
+  , [2.75021004676819, 2.26919007301331, -0.039733998477459]
+  , [2.75021004676819, 2.26919007301331, 0.039733998477459]
+  , [2.75149989128113, 1.8829699754715, -0.220825001597404]
+  , [2.75149989128113, 1.8829699754715, 0.220825001597404]
+  , [2.7535400390625, 1.58508002758026, -0.124597996473312]
+  , [2.7535400390625, 1.58508002758026, 0.124597996473312]
+  , [2.76737999916077, 1.57500004768372, 0]
+  , [2.77555990219116, 2.28399991989136, 0]
+  , [2.7809898853302, 1.9943699836731, -0.208084002137184]
+  , [2.7809898853302, 1.9943699836731, 0.208084002137184]
+  , [2.78303003311157, 1.72669994831085, -0.154476001858711]
+  , [2.78303003311157, 1.72669994831085, 0.154476001858711]
+  , [2.79375004768372, 2.25, -0.140625]
+  , [2.79375004768372, 2.25, 0.140625]
+  , [2.79782009124756, 2.27174997329712, -0.10784900188446]
+  , [2.79782009124756, 2.27174997329712, 0.10784900188446]
+  , [2.79948997497559, 2.29274988174438, -0.0769039988517761]
+  , [2.79948997497559, 2.29274988174438, 0.0769039988517761]
+  , [2.79999995231628, 2.25, 0]
+  , [2.80468988418579, 2.09809994697571, -0.200712993741035]
+  , [2.80468988418579, 2.09809994697571, 0.200712993741035]
+  , [2.8099000453949, 1.71249997615814, -0.0569120012223721]
+  , [2.8099000453949, 1.71249997615814, 0.0569120012223721]
+  , [2.81006002426147, 1.86232995986938, -0.176660001277924]
+  , [2.81006002426147, 1.86232995986938, 0.176660001277924]
+  , [2.81201004981995, 2.17814993858337, -0.169843003153801]
+  , [2.81201004981995, 2.17814993858337, 0.169843003153801]
+  , [2.81274008750916, 2.29753994941711, -0.035631999373436]
+  , [2.81274008750916, 2.29753994941711, 0.035631999373436]
+  , [2.81718993186951, 2.25, -0.0492190010845661]
+  , [2.81718993186951, 2.25, 0.0492190010845661]
+  , [2.82500004768372, 2.30625009536743, 0]
+  , [2.8301100730896, 2.27129006385803, -0.025891000404954]
+  , [2.8301100730896, 2.27129006385803, 0.025891000404954]
+  , [2.84063005447388, 2.29219007492065, 0]
+  , [2.84478998184204, 2.29963994026184, -0.0299929995089769]
+  , [2.84478998184204, 2.29963994026184, 0.0299929995089769]
+  , [2.85091996192932, 2.30715990066528, -0.0656249970197678]
+  , [2.85091996192932, 2.30715990066528, 0.0656249970197678]
+  , [2.85118007659912, 1.97918999195099, -0.180818006396294]
+  , [2.85118007659912, 1.97918999195099, 0.180818006396294]
+  , [2.85148000717163, 1.84773004055023, -0.103051997721195]
+  , [2.85148000717163, 1.84773004055023, 0.103051997721195]
+  , [2.86048007011414, 2.30093002319336, -0.0967160016298294]
+  , [2.86048007011414, 2.30093002319336, 0.0967160016298294]
+  , [2.86249995231628, 2.25, -0.0843750014901161]
+  , [2.86249995231628, 2.25, 0.0843750014901161]
+  , [2.86262989044189, 2.29297995567322, -0.0543459989130497]
+  , [2.86262989044189, 2.29297995567322, 0.0543459989130497]
+  , [2.86574006080627, 2.27201008796692, -0.0702759996056557]
+  , [2.86574006080627, 2.27201008796692, 0.0702759996056557]
+  , [2.86718988418579, 1.84219002723694, 0]
+  , [2.87227988243103, 2.29425001144409, -0.131835997104645]
+  , [2.87227988243103, 2.29425001144409, 0.131835997104645]
+  , [2.88338994979858, 2.08977007865906, -0.175249993801117]
+  , [2.88338994979858, 2.08977007865906, 0.175249993801117]
+  , [2.88836002349854, 2.30118989944458, -0.0814089998602867]
+  , [2.88836002349854, 2.30118989944458, 0.0814089998602867]
+  , [2.89826989173889, 2.17088007926941, -0.19438199698925]
+  , [2.89826989173889, 2.17088007926941, 0.19438199698925]
+  , [2.90805006027222, 1.96700000762939, -0.129515007138252]
+  , [2.90805006027222, 1.96700000762939, 0.129515007138252]
+  , [2.91923999786377, 2.30955004692078, -0.112499997019768]
+  , [2.91923999786377, 2.30955004692078, 0.112499997019768]
+  , [2.92063999176025, 2.29506993293762, -0.0931639969348907]
+  , [2.92063999176025, 2.29506993293762, 0.0931639969348907]
+  , [2.93279004096985, 2.13103008270264, -0.17221100628376]
+  , [2.93279004096985, 2.13103008270264, 0.17221100628376]
+  , [2.93980002403259, 2.27326011657715, -0.158935993909836]
+  , [2.93980002403259, 2.27326011657715, 0.158935993909836]
+  , [2.93996000289917, 1.96010005474091, -0.047715999186039]
+  , [2.93996000289917, 1.96010005474091, 0.047715999186039]
+  , [2.95977997779846, 2.08168005943298, -0.150535002350807]
+  , [2.95977997779846, 2.08168005943298, 0.150535002350807]
+  , [2.96994996070862, 2.27412009239197, -0.103564001619816]
+  , [2.96994996070862, 2.27412009239197, 0.103564001619816]
+  , [3, 2.25, -0.1875]
+  , [3, 2.25, -0.112499997019768]
+  , [3, 2.25, 0.112499997019768]
+  , [3, 2.25, 0.1875]
+  , [3.00281000137329, 2.30484008789062, -0.142528995871544]
+  , [3.00281000137329, 2.30484008789062, 0.142528995871544]
+  , [3.01089000701904, 2.07627010345459, -0.0878119990229607]
+  , [3.01089000701904, 2.07627010345459, 0.0878119990229607]
+  , [3.01577997207642, 2.30571007728577, -0.119970999658108]
+  , [3.01577997207642, 2.30571007728577, 0.119970999658108]
+  , [3.03027009963989, 2.0742199420929, 0]
+  , [3.04150009155273, 2.12566995620728, -0.116276003420353]
+  , [3.04150009155273, 2.12566995620728, 0.116276003420353]
+  , [3.0432300567627, 2.2110800743103, -0.166430994868279]
+  , [3.0432300567627, 2.2110800743103, 0.166430994868279]
+  , [3.06841993331909, 2.17344999313354, -0.143215000629425]
+  , [3.06841993331909, 2.17344999313354, 0.143215000629425]
+  , [3.07928991317749, 2.12305998802185, -0.0428379997611046]
+  , [3.07928991317749, 2.12305998802185, 0.0428379997611046]
+  , [3.09315991401672, 2.29877996444702, -0.17578099668026]
+  , [3.09315991401672, 2.29877996444702, 0.17578099668026]
+  , [3.09667992591858, 2.30141997337341, -0.124219000339508]
+  , [3.09667992591858, 2.30141997337341, 0.124219000339508]
+  , [3.12655997276306, 2.31680011749268, -0.150000005960464]
+  , [3.12655997276306, 2.31680011749268, 0.150000005960464]
+  , [3.12671995162964, 2.2772901058197, -0.103564001619816]
+  , [3.12671995162964, 2.2772901058197, 0.103564001619816]
+  , [3.12690997123718, 2.17127990722656, -0.0835419967770576]
+  , [3.12690997123718, 2.17127990722656, 0.0835419967770576]
+  , [3.13750004768372, 2.25, -0.0843750014901161]
+  , [3.13750004768372, 2.25, 0.0843750014901161]
+  , [3.14910006523132, 2.17045998573303, 0]
+  , [3.15336990356445, 2.27552008628845, -0.158935993909836]
+  , [3.15336990356445, 2.27552008628845, 0.158935993909836]
+  , [3.16895008087158, 2.21117997169495, -0.11235299706459]
+  , [3.16895008087158, 2.21117997169495, 0.11235299706459]
+  , [3.18281006813049, 2.25, -0.0492190010845661]
+  , [3.18281006813049, 2.25, 0.0492190010845661]
+  , [3.20000004768372, 2.25, 0]
+  , [3.20624995231628, 2.25, -0.140625]
+  , [3.20624995231628, 2.25, 0.140625]
+  , [3.20745992660522, 2.31251001358032, -0.119970999658108]
+  , [3.20745992660522, 2.31251001358032, 0.119970999658108]
+  , [3.21255993843079, 2.21042990684509, -0.0413930006325245]
+  , [3.21255993843079, 2.21042990684509, 0.0413930006325245]
+  , [3.21691989898682, 2.31072998046875, -0.142528995871544]
+  , [3.21691989898682, 2.31072998046875, 0.142528995871544]
+  , [3.23094010353088, 2.27940011024475, -0.0702759996056557]
+  , [3.23094010353088, 2.27940011024475, 0.0702759996056557]
+  , [3.26724004745483, 2.2781400680542, -0.025891000404954]
+  , [3.26724004745483, 2.2781400680542, 0.025891000404954]
+  , [3.27272009849548, 2.30776000022888, -0.0931639969348907]
+  , [3.27272009849548, 2.30776000022888, 0.0931639969348907]
+  , [3.27421998977661, 2.25, -0.0820309966802597]
+  , [3.27421998977661, 2.25, 0.0820309966802597]
+  , [3.29534006118774, 2.2770299911499, -0.10784900188446]
+  , [3.29534006118774, 2.2770299911499, 0.10784900188446]
+  , [3.29999995231628, 2.25, 0]
+  , [3.31404995918274, 2.30330991744995, -0.131835997104645]
+  , [3.31404995918274, 2.30330991744995, 0.131835997104645]
+  , [3.33072996139526, 2.30984997749329, -0.0543459989130497]
+  , [3.33072996139526, 2.30984997749329, 0.0543459989130497]
+  , [3.33388996124268, 2.324049949646, -0.112499997019768]
+  , [3.33388996124268, 2.324049949646, 0.112499997019768]
+  , [3.33488988876343, 2.31701993942261, -0.0814089998602867]
+  , [3.33488988876343, 2.31701993942261, 0.0814089998602867]
+  , [3.34236001968384, 2.2800600528717, -0.039733998477459]
+  , [3.34236001968384, 2.2800600528717, 0.039733998477459]
+  , [3.35542988777161, 2.30270004272461, 0]
+  , [3.35925006866455, 2.31465005874634, -0.0967160016298294]
+  , [3.35925006866455, 2.31465005874634, 0.0967160016298294]
+  , [3.37912011146545, 2.31658005714417, -0.0299929995089769]
+  , [3.37912011146545, 2.31658005714417, 0.0299929995089769]
+  , [3.38684010505676, 2.30481004714966, -0.0769039988517761]
+  , [3.38684010505676, 2.30481004714966, 0.0769039988517761]
+  , [3.40220999717712, 2.32644009590149, -0.0656249970197678]
+  , [3.40220999717712, 2.32644009590149, 0.0656249970197678]
+  , [3.40638995170593, 2.31850004196167, -0.035631999373436]
+  , [3.40638995170593, 2.31850004196167, 0.035631999373436]
+  , [3.40838003158569, 2.31542992591858, 0]
+  , [3.42811989784241, 2.32733988761902, 0] ]
+
+voronoiCellTeapot :: [[Double]]
+voronoiCellTeapot =
+  [0, 1.5, 0] :
+  [
+    [ -0.18667466541973257
+    , 0.5968790484510281
+    , 0.5717139272637414
+    ]
+  , [ -0.15441948465435382
+    , 0.7326922941987842
+    , 0.7170049842821647
+    ]
+  , [ -1.7990621802658653e-2
+    , 1.9860825589150588e-2
+    , 1.7990621802654917e-2
+    ]
+  , [ -0.29182490099196906
+    , 0.7297971305995979
+    , 0.6710598534195158
+    ]
+  , [ -6.448622334243023e-2
+    , 0.5851752797903265
+    , 0.5851752797903262
+    ]
+  , [ -0.1624173204710072 , 0.7759039565328439 , 0.7575092969306046 ]
+  , [ -2.3194160942496607e-2
+    , 2.6785629054845606e-2
+    , 2.3194160942459883e-2
+    ]
+  , [ -0.39216297275000733
+    , 0.6074273900130303
+    , 0.4720306023200158
+    ]
+  , [ -2.215103618775975e-2
+    , 2.5331770412981847e-2
+    , 2.215103618775975e-2
+    ]
+  , [ -0.4720306023200158
+    , 0.6074273900130303
+    , 0.39216297275000733
+    ]
+  , [ -0.1228050514414682 , 0.8753822495491728 , 0.8567258893994795 ]
+  , [ -0.23969991808709246
+    , 0.8768406254951662
+    , 0.8337562524679074
+    ]
+  , [ 0.7805147235214109
+    , 0.8624999821186053
+    , 4.365935118304741e-2
+    ]
+  , [ 0.863072608376728 , 1.4862706190912907 , 0.14576662495052128 ]
+  , [ 0.7805147235214106
+    , 0.8624999821186055
+    , -4.36593511830482e-2
+    ]
+  , [ 0.7805144198006899
+    , 0.8624994903802878
+    , 4.366114397309236e-2
+    ]
+  , [ 0.7740595934432167 , 0.862499490380288 , 9.433094929942933e-2 ]
+  , [ 0.8122497597351315 , 1.3634301491185252 , 0.39698702598827496 ]
+  , [ 0.7740579104368239 , 0.8624980151653314 , 9.43372496836588e-2 ]
+  , [ 2.3194160942461885e-2
+    , 2.6785629054845606e-2
+    , 2.319416094249719e-2
+    ]
+  , [ 0.4720306023200158 , 0.6074273900130303 , 0.39216297275000733 ]
+  , [ 2.215103618775549e-2
+    , 2.5331770412978624e-2
+    , 2.2151036187755824e-2
+    ]
+  , [ 0.39216297275000733 , 0.6074273900130303 , 0.4720306023200158 ]
+  , [ -0.18893822974950683
+    , 0.9120771417338428
+    , 0.8765366554024406
+    ]
+  , [ -0.15370753430700942
+    , 0.9074581172580759
+    , 0.8796369493525763
+    ]
+  , [ -0.19282328992414305
+    , 1.0886815800201057
+    , 0.8962126634940768
+    ]
+  , [ -0.21959663365004273 , 0.90801535760842 , 0.8664476852626979 ]
+  , [ 0.6553927028305585 , 0.6599214690464071 , 7.68209537002328e-2 ]
+  , [ 0.6480770038782534
+    , 0.6480770038782535
+    , 3.783067537459344e-2
+    ]
+  , [ 0.6480770038782537
+    , 0.6480770038782535
+    , -3.7830675374592546e-2
+    ]
+  , [ 0.5851752797903262
+    , 0.5851752797903265
+    , 6.448622334243026e-2
+    ]
+  , [ 0.6500310589666277 , 0.6577486876432921 , 0.10873036208745468 ]
+  , [ 0.7710778571614446 , 0.862498015165331 , 0.10798328513535715 ]
+  , [ 0.6540866269118542 , 0.6844355992166629 , 0.20191695153447142 ]
+  , [ -0.3246126189761304 , 0.9080154562527455 , 0.8335353871385288 ]
+  , [ -0.289831893259673 , 0.8768408129798988 , 0.818044872661731 ]
+  , [ -9.869506660256815e-2
+    , 1.1894267902510014
+    , 0.9173511809757207
+    ]
+  , [ -0.283130810168734 , 1.1814109441614336 , 0.8788805908385666 ]
+  , [ -0.28347840420931564
+    , 1.2593473684859822
+    , 0.8800216675702618
+    ]
+  , [ -0.36590860085651855
+    , 1.0902530151825576
+    , 0.8421531448806922
+    ]
+  , [ 9.87971813131522e-2 , 1.257602275825742 , 0.9183913329068394 ]
+  , [ 0.1916029836825906 , 1.29931244670657 , 0.8997455590151622 ]
+  , [ 9.86950666025681e-2 , 1.1894267902510014 , 0.9173511809757205 ]
+  , [ 0.0 , 1.3011746498142653 , 0.9190561156885906 ]
+  , [ -9.879718131315218e-2
+    , 1.2576022758257417
+    , 0.9183913329068394
+    ]
+  , [ -0.1916029836825906 , 1.29931244670657 , 0.8997455590151622 ]
+  , [ 0.0 , 1.0862351104914478 , 0.9157767877158491 ]
+  , [ 0.0 , 1.4739386059327377 , 0.8813815581455101 ]
+  , [ -3.497482895265056e-2
+    , 0.9074580789365942
+    , 0.8918549800719824
+    ]
+  , [ -6.080525584912345e-2 , 0.8753821429287596 , 0.86310582524099 ]
+  , [ 7.812642760551707e-3
+    , 7.812642760551707e-3
+    , 7.812642760550714e-3
+    ]
+  , [ 0.6491833829319444 , 0.6692949179238175 , 0.1694761444119623 ]
+  , [ -2.7755575615628914e-17
+    , 0.7373497776866849
+    , 0.7373497776866843
+    ]
+  , [ -7.812642760551686e-3
+    , 7.812642760551715e-3
+    , 7.812642760551602e-3
+    ]
+  , [ -0.3581881925466474 , 0.9124253481133149 , 0.8235344289369642 ]
+  , [ -0.4468960748716194 , 1.1765076558719516 , 0.8091216099094026 ]
+  , [ -0.4474984305598345 , 1.2598574705147236 , 0.8102438227139427 ]
+  , [ -0.36541277208493106
+    , 1.2970587028449376
+    , 0.8457844093935878
+    ]
+  , [ -0.5194379814088046 , 1.2957898827631247 , 0.7622224951613341 ]
+  , [ -0.518569427022487 , 1.0903982826657406 , 0.7590404339629727 ]
+  , [ -0.5851752797903265
+    , 0.5851752797903264
+    , 6.448622334243026e-2
+    ]
+  , [ -7.812642760551698e-3
+    , 7.81264276055331e-3
+    , 7.812642760553379e-3
+    ]
+  , [ -0.7170049842821663 , 0.732692294198784 , 0.1544194846543815 ]
+  , [ -0.7373497776866849
+    , 0.7373497776866845
+    , -2.7755575615628914e-17
+    ]
+  , [ -0.5717139272637514
+    , 0.5968790484510277
+    , 0.18667466541971128
+    ]
+  , [ -0.6710598534195159
+    , 0.7297971305995982
+    , 0.29182490099196917
+    ]
+  , [ -1.7990621802656183e-2
+    , 1.9860825589150588e-2
+    , 1.7990621802655427e-2
+    ]
+  , [ -0.7575092969305982
+    , 0.7759039565328366
+    , 0.16241732047101434
+    ]
+  , [ -0.532913153617887 , 0.6041132479674722 , 0.2962999185904398 ]
+  , [ -0.6045930611639508
+    , 0.7294381763744873
+    , 0.41317238743168894
+    ]
+  , [ -0.715603493434698 , 0.780363714438081 , 0.3102929809778049 ]
+  , [ 0.6491833829319443 , 0.6692949179238176 , -0.1694761444119623 ]
+  , [ 0.6500310589666282 , 0.657748687643292 , -0.10873036208745479 ]
+  , [ 0.6540866269118539 , 0.684435599216663 , -0.20191695153447145 ]
+  , [ 0.5717139272637514
+    , 0.5968790484510275
+    , -0.18667466541971123
+    ]
+  , [ 0.78051441980069 , 0.8624994903802875 , -4.366114397309004e-2 ]
+  , [ 0.7740595934432167
+    , 0.8624994903802875
+    , -9.43309492994287e-2
+    ]
+  , [ 0.6553927028305583
+    , 0.6599214690464065
+    , -7.682095370023323e-2
+    ]
+  , [ 0.8630726083767282 , 1.4862706190912907 , -0.1457666249505203 ]
+  , [ 0.8651615019144336
+    , 1.5020533690353601
+    , -9.103018997631947e-2
+    ]
+  , [ 0.8545090595639963
+    , 1.4798172601240018
+    , -0.20522856413423346
+    ]
+  , [ 0.2834784042093155 , 1.2593473684859822 , 0.8800216675702618 ]
+  , [ 0.3654127720849311 , 1.2970587028449378 , 0.8457844093935878 ]
+  , [ 0.2831308101687339 , 1.1814109441614336 , 0.8788805908385666 ]
+  , [ 0.18424327753183914 , 1.4736973001288365 , 0.8624748737615431 ]
+  , [ -0.6846273996141601 , 0.8790068974537645 , 0.5388137355108445 ]
+  , [ -0.6567496895904281 , 0.8790071413593897 , 0.5725795213922534 ]
+  , [ -0.6464588188586459
+    , 0.7823424436625966
+    , 0.44119347577210544
+    ]
+  , [ -0.7213170770599064 , 0.908850927324427 , 0.5321478467750975 ]
+  , [ -0.5181196004900488 , 0.7295797909722026 , 0.5181196004900487 ]
+  , [ -0.7505675479671516 , 0.8781985739488957 , 0.4400805652422398 ]
+  , [ 0.7708735521448062 , 0.8625025153160115 , 0.10873936568443998 ]
+  , [ 0.6626047492685796 , 0.9655545808978969 , 0.6132901903841046 ]
+  , [ 0.771074187418324 , 0.8625025153160115 , 0.108004379308 ]
+  , [ 0.6419080894398502 , 0.7908779235632789 , 0.45940727414369065 ]
+  , [ 0.6490953253086557 , 0.7194452653409781 , 0.31163387003160176 ]
+  , [ 0.7391783489851426 , 1.1731157788543425 , 0.550164799101456 ]
+  , [ -6.169852060031459e-17
+    , 0.912056858034264
+    , 0.8959767931433186
+    ]
+  , [ 0.1928232899241431 , 1.0886815800201057 , 0.8962126634940769 ]
+  , [ 0.15370753430700868 , 0.907458117258076 , 0.879636949352576 ]
+  , [ 0.18893822974950675 , 0.9120771417338429 , 0.8765366554024405 ]
+  , [ 0.12280505144146822 , 0.8753822495491728 , 0.8567258893994794 ]
+  , [ 3.4974828952650555e-2
+    , 0.9074580789365942
+    , 0.8918549800719824
+    ]
+  , [ 0.0 , 0.7712853278687586 , 0.7698054940256471 ]
+  , [ 6.080525584912347e-2
+    , 0.8753821429287594
+    , 0.8631058252409901
+    ]
+  , [ 0.18667466541971134 , 0.5968790484510275 , 0.5717139272637516 ]
+  , [ 0.29182490099196917 , 0.729797130599598 , 0.6710598534195159 ]
+  , [ 1.7990621802653095e-2
+    , 1.9860825589147996e-2
+    , 1.799062180265376e-2
+    ]
+  , [ 0.15441948465438154 , 0.7326922941987839 , 0.7170049842821663 ]
+  , [ 0.23969991808709235 , 0.8768406254951658 , 0.8337562524679074 ]
+  , [ 0.289831893259673 , 0.876840812979899 , 0.818044872661731 ]
+  , [ 0.21959663365004123 , 0.9080153576084199 , 0.866447685262698 ]
+  , [ 0.16241732047101431 , 0.7759039565328366 , 0.757509296930598 ]
+  , [ -0.49818544023253314
+    , 1.4717742047093716
+    , 0.7304675058910846
+    ]
+  , [ -0.5911999246804508 , 1.2599845756664991 , 0.7130046626566298 ]
+  , [ 0.4068358188765504 , 1.74614655744181 , 0.4921333979229422 ]
+  , [ 0.4787714299468666 , 1.6423746127113557 , 0.5796425375805216 ]
+  , [ 0.4378291627777074 , 1.7613180604656151 , 0.43782916277770734 ]
+  , [ 0.34656596334419265 , 1.7617035212112468 , 0.5125010376980281 ]
+  , [ 0.35938264373453305 , 1.643431115641555 , 0.6590455307424883 ]
+  , [ 0.44011909325789345 , 1.6091658630935881 , 0.6493805393140206 ]
+  , [ 0.3077193099076989 , 1.6095465768220536 , 0.7210973557556136 ]
+  , [ 0.30618459927572234 , 1.7463793702756347 , 0.5599378049841232 ]
+  , [ 0.5851752797903262
+    , 0.5851752797903265
+    , -6.448622334243026e-2
+    ]
+  , [ 7.812642760551491e-3
+    , 7.812642760551715e-3
+    , -7.812642760550492e-3
+    ]
+  , [ -0.7622224951613341
+    , 1.2957898827631245
+    , -0.5194379814088045
+    ]
+  , [ -0.7304675058910846
+    , 1.4717742047093714
+    , -0.4981854402325332
+    ]
+  , [ -0.7130046626566298
+    , 1.2599845756664991
+    , -0.5911999246804508
+    ]
+  , [ -0.8102438227139428
+    , 1.2598574705147236
+    , -0.4474984305598346
+    ]
+  , [ 0.4206163489045973 , 1.4946619231456852 , -0.7670726439144389 ]
+  , [ 0.4981854402325333 , 1.4717742047093716 , -0.7304675058910849 ]
+  , [ 0.3507821217020296 , 1.4727320843657137 , -0.8105000296381791 ]
+  , [ 0.4146046771732798 , 1.5166611367705873 , -0.755872347832471 ]
+  , [ -0.5073894465820622 , 0.9128065261969955 , 0.7423359031749056 ]
+  , [ -0.4768060384239586 , 0.9085520538404275 , 0.7584440485557873 ]
+  , [ -0.5321478467750965 , 0.9088509273244271 , 0.7213170770599069 ]
+  , [ -0.5903677161648284 , 1.1741465971067808 , 0.7119923208428119 ]
+  , [ -0.5388137355108447 , 0.8790068974537645 , 0.6846273996141601 ]
+  , [ -0.6097884029414722 , 0.9088511267237062 , 0.6572150990573598 ]
+  , [ -0.29629991859043964
+    , 0.6041132479674723
+    , 0.5329131536178867
+    ]
+  , [ -2.2151036187758197e-2
+    , 2.5331770412981625e-2
+    , 2.215103618775873e-2
+    ]
+  , [ -0.41317238743168894
+    , 0.7294381763744873
+    , 0.6045930611639508
+    ]
+  , [ -0.31029298097780494 , 0.780363714438081 , 0.715603493434698 ]
+  , [ -0.810500029638179
+    , 1.4727320843657137
+    , -0.35078212170202955
+    ]
+  , [ -0.7670726439144387
+    , 1.4946619231456855
+    , -0.42061634890459737
+    ]
+  , [ -0.8260665185299887
+    , 1.4902762907980336
+    , -0.2884082499027695
+    ]
+  , [ -0.8457844093935879
+    , 1.2970587028449376
+    , -0.3654127720849311
+    ]
+  , [ -0.18424327753183928
+    , 1.4736973001288365
+    , 0.8624748737615431
+    ]
+  , [ -0.3507821217020296 , 1.4727320843657137 , 0.8105000296381792 ]
+  , [ -0.26432271823494774
+    , 1.4970509470350615
+    , 0.8320774835437866
+    ]
+  , [ -0.42061634890459737
+    , 1.4946619231456855
+    , 0.7670726439144387
+    ]
+  , [ -9.114226751346854e-2
+    , 1.5000742593050618
+    , 0.8663032960314866
+    ]
+  , [ -0.26089628483355987
+    , 1.5173670225190703
+    , 0.8208298543940763
+    ]
+  , [ 0.8208298543940764 , 1.5173670225190703 , 0.2608962848335599 ]
+  , [ 0.8320774835437869 , 1.4970509470350615 , 0.2643227182349477 ]
+  , [ 0.8228126117740736 , 1.5458407114144659 , 0.1707356424814656 ]
+  , [ 0.7750736880510629 , 1.543449628093767 , 0.3300998702021285 ]
+  , [ 0.8651615019144336
+    , 1.5020533690353601
+    , 9.103018997631947e-2
+    ]
+  , [ 0.8545090595639963 , 1.4798172601240018 , 0.2052285641342334 ]
+  , [ 0.8562836943885954 , 1.517441568182377 , 9.01587519579244e-2 ]
+  , [ 0.7673351760829068 , 1.6096835638600524 , 0.15978142628509895 ]
+  , [ 0.8239288970287512 , 1.4058726252835607 , 0.35635039483568703 ]
+  , [ 0.7733308622857777 , 1.287477942063759 , 0.502796814195706 ]
+  , [ 0.4474984305598345 , 1.2598574705147234 , 0.8102438227139425 ]
+  , [ 0.3507821217020293 , 1.4727320843657137 , 0.810500029638179 ]
+  , [ 0.32461261897613036 , 0.9080154562527452 , 0.833535387138529 ]
+  , [ 0.36590860085651866 , 1.0902530151825576 , 0.8421531448806923 ]
+  , [ -0.6045930611639505
+    , 0.7294381763744877
+    , -0.41317238743168877
+    ]
+  , [ -0.4720306023200157
+    , 0.6074273900130303
+    , -0.3921629727500072
+    ]
+  , [ -0.646458818858646 , 0.7823424436625966 , -0.4411934757721056 ]
+  , [ -0.5329131536178867
+    , 0.6041132479674725
+    , -0.2962999185904397
+    ]
+  , [ -0.6846273996141597
+    , 0.8790068974537646
+    , -0.5388137355108444
+    ]
+  , [ -0.7505675479671517
+    , 0.8781985739488954
+    , -0.44008056524223993
+    ]
+  , [ 0.29629991859043964 , 0.6041132479674723 , 0.5329131536178867 ]
+  , [ 0.41317238743168894 , 0.7294381763744873 , 0.6045930611639508 ]
+  , [ 2.215103618775549e-2
+    , 2.533177041297828e-2
+    , 2.21510361877556e-2
+    ]
+  , [ 0.31029298097780494 , 0.780363714438081 , 0.715603493434698 ]
+  , [ -0.15441948465438154
+    , 0.732692294198784
+    , -0.7170049842821664
+    ]
+  , [ -6.448622334243025e-2
+    , 0.5851752797903265
+    , -0.5851752797903262
+    ]
+  , [ -0.16241732047101426
+    , 0.7759039565328368
+    , -0.7575092969305981
+    ]
+  , [ -0.18667466541971123
+    , 0.5968790484510273
+    , -0.5717139272637514
+    ]
+  , [ -0.5539859487435899
+    , 0.7828729928425695
+    , -0.5539859487435899
+    ]
+  , [ -0.5725795213922535
+    , 0.8790071413593896
+    , -0.6567496895904281
+    ]
+  , [ -0.5181196004900485
+    , 0.7295797909722027
+    , -0.5181196004900485
+    ]
+  , [ -0.6567496895904281
+    , 0.8790071413593897
+    , -0.5725795213922533
+    ]
+  , [ 0.8122497597351318 , 1.3634301491185252 , -0.396987025988275 ]
+  , [ 0.7740579104368243
+    , 0.862498015165331
+    , -9.433724968365648e-2
+    ]
+  , [ 6.44862233424303e-2 , 0.5851752797903262 , 0.5851752797903267 ]
+  , [ 7.812642760551684e-3
+    , 7.812642760551713e-3
+    , 7.812642760551824e-3
+    ]
+  , [ 0.19212615972377473 , 1.746701943306404 , 0.6080853167177859 ]
+  , [ 0.2414067196592598 , 1.7624263437337935 , 0.568522694152947 ]
+  , [ 0.22440550617121352 , 1.6454841596605991 , 0.7140470378267627 ]
+  , [ 0.12443603223298119 , 1.763801627319081 , 0.6031237664636153 ]
+  , [ -0.6254774679047082 , 1.4713967749278507 , 0.6254774679047082 ]
+  , [ -0.558287861814229 , 1.4934020335516496 , 0.6747863108748675 ]
+  , [ -0.6747863108748675 , 1.4934020335516496 , 0.558287861814229 ]
+  , [ -0.6524829343907664 , 1.2954115625390261 , 0.6524829343907664 ]
+  , [ 0.2571184603540509 , 1.8714260967581884 , 0.37885544369469604 ]
+  , [ 0.28526260180018986
+    , 1.8775138180920772
+    , 0.34408768727411854
+    ]
+  , [ 0.21271144734580563 , 1.877827270100134 , 0.3923832499457651 ]
+  , [ 0.18017138946943928 , 1.871613285510211 , 0.42041538751051694 ]
+  , [ 0.5599378049841232 , 1.7463793702756347 , 0.30618459927572234 ]
+  , [ 0.6590455307424883 , 1.643431115641555 , 0.3593826437345331 ]
+  , [ 0.568522694152947 , 1.7624263437337935 , 0.2414067196592598 ]
+  , [ 0.512501037698028 , 1.7617035212112466 , 0.34656596334419265 ]
+  , [ 0.755872347832471 , 1.5166611367705873 , 0.4146046771732797 ]
+  , [ 0.7210973557556135 , 1.6095465768220534 , 0.3077193099076989 ]
+  , [ 0.7140470378267627 , 1.6454841596605991 , 0.2244055061712136 ]
+  , [ 0.6493805393140206 , 1.6091658630935881 , 0.4401190932578934 ]
+  , [ -0.8664476852626979
+    , 0.9080153576084198
+    , -0.21959663365004278
+    ]
+  , [ -0.8337562524679073
+    , 0.8768406254951661
+    , -0.2396999180870924
+    ]
+  , [ -0.8765366554024407
+    , 0.9120771417338429
+    , -0.18893822974950678
+    ]
+  , [ -0.833535387138529
+    , 0.9080154562527452
+    , -0.32461261897613036
+    ]
+  , [ 0.44689607487161953
+    , 1.1765076558719516
+    , -0.8091216099094027
+    ]
+  , [ 0.518569427022487 , 1.0903982826657406 , -0.7590404339629727 ]
+  , [ 0.4474984305598345 , 1.2598574705147234 , -0.8102438227139426 ]
+  , [ 0.36590860085651866
+    , 1.0902530151825576
+    , -0.8421531448806926
+    ]
+  , [ 0.5194379814088046 , 1.2957898827631247 , -0.7622224951613341 ]
+  , [ 0.36541277208493106
+    , 1.2970587028449376
+    , -0.8457844093935878
+    ]
+  , [ 0.26432271823494774
+    , 1.4970509470350615
+    , -0.8320774835437867
+    ]
+  , [ 0.2834784042093156 , 1.2593473684859824 , -0.8800216675702618 ]
+  , [ 0.283130810168734 , 1.1814109441614336 , -0.8788805908385665 ]
+  , [ 0.19160298368259063 , 1.29931244670657 , -0.8997455590151622 ]
+  , [ 1.7990621802653317e-2
+    , 1.9860825589147875e-2
+    , -1.799062180265354e-2
+    ]
+  , [ 0.647296121375148 , 0.6980028758147818 , -0.26667276723840716 ]
+  , [ -0.86310582524099 , 0.8753821429287596 , 6.080525584912345e-2 ]
+  , [ -0.7698054940256472 , 0.7712853278687583 , 0.0 ]
+  , [ -0.8567258893994796
+    , 0.8753822495491728
+    , 0.12280505144146828
+    ]
+  , [ -0.891854980071982 , 0.9074580789365941 , 3.4974828952651e-2 ]
+  , [ -0.8337562524679074
+    , 0.8768406254951661
+    , 0.23969991808709246
+    ]
+  , [ -0.8180448726617309 , 0.8768408129798988 , 0.289831893259673 ]
+  , [ -0.8664476852626981 , 0.90801535760842 , 0.21959663365004095 ]
+  , [ -0.879636949352576 , 0.907458117258076 , 0.15370753430700804 ]
+  , [ -0.4400805652422398 , 0.8781985739488954 , 0.7505675479671517 ]
+  , [ -0.3856172858664447 , 0.908552060323908 , 0.8081008260732252 ]
+  , [ -0.3991852390173557 , 0.8781995871178615 , 0.772837999772326 ]
+  , [ -0.44119347577210544
+    , 0.7823424436625966
+    , 0.6464588188586459
+    ]
+  , [ -0.5725795213922534 , 0.8790071413593897 , 0.6567496895904281 ]
+  , [ -0.5539859487435899 , 0.7828729928425695 , 0.5539859487435899 ]
+  , [ -0.6646325123013266 , 1.5161703861218 , 0.5499408317491791 ]
+  , [ -0.7304675058910848
+    , 1.4717742047093716
+    , 0.49818544023253314
+    ]
+  , [ -0.7584440485557876 , 0.9085520538404273 , 0.4768060384239584 ]
+  , [ -0.7728379997723258 , 0.8781995871178616 , 0.3991852390173557 ]
+  , [ 0.8562836943885955
+    , 1.5174415681823774
+    , -9.015875195792439e-2
+    ]
+  , [ 0.8390008746485779
+    , 1.547398454632929
+    , 2.7755575615628914e-17
+    ]
+  , [ 0.16621776154382692
+    , 1.9776053853655182
+    , 0.16621776154382692
+    ]
+  , [ 0.34408768727411854
+    , 1.8775138180920767
+    , 0.28526260180018986
+    ]
+  , [ 0.15197602557430878
+    , 1.9868541707659964
+    , 0.15197602557430875
+    ]
+  , [ 0.3239549649324688 , 1.871257263189551 , 0.3239549649324688 ]
+  , [ 0.3923832499457651 , 1.877827270100134 , 0.21271144734580563 ]
+  , [ 0.4204153875105169 , 1.8716132855102106 , 0.18017138946943928 ]
+  , [ 0.378855443694696 , 1.8714260967581884 , 0.25711846035405095 ]
+  , [ 0.21242895637465672
+    , 1.9790924197542168
+    , 9.226906344799235e-2
+    ]
+  , [ 0.42437151237708004
+    , 1.8784630967211673
+    , 0.13411685349983243
+    ]
+  , [ 0.19692078525583517
+    , 1.9867703444520366
+    , 8.589118029151296e-2
+    ]
+  , [ 0.15978142628509898 , 1.6096835638600524 , 0.7673351760829068 ]
+  , [ 0.17073564248146567 , 1.545840711414466 , 0.8228126117740738 ]
+  , [ 7.668380969626697e-2
+    , 1.6481991362041954
+    , 0.7417373916617902
+    ]
+  , [ 0.3300998702021285 , 1.543449628093767 , 0.7750736880510631 ]
+  , [ 0.4468960748716194 , 1.1765076558719516 , 0.8091216099094026 ]
+  , [ 0.3581881925466474 , 0.9124253481133149 , 0.8235344289369642 ]
+  , [ -2.215103618775549e-2
+    , 2.533177041297907e-2
+    , -2.2151036187755047e-2
+    ]
+  , [ -0.6710598534195159
+    , 0.7297971305995978
+    , -0.2918249009919692
+    ]
+  , [ -0.7584440485557876
+    , 0.9085520538404273
+    , -0.4768060384239584
+    ]
+  , [ -0.7728379997723258
+    , 0.8781995871178616
+    , -0.3991852390173557
+    ]
+  , [ 0.5717139272637514 , 0.5968790484510275 , 0.18667466541971128 ]
+  , [ 1.7990621802653095e-2
+    , 1.986082558914795e-2
+    , 1.7990621802653206e-2
+    ]
+  , [ 0.6472961213751481 , 0.6980028758147817 , 0.26667276723840716 ]
+  , [ 0.6469996697760713 , 0.7079075212097802 , 0.2926042880159187 ]
+  , [ 0.6357399515521689 , 0.8593528035528301 , 0.568778121333005 ]
+  , [ 0.6405215643858786 , 0.7748397465927047 , 0.4372196236016949 ]
+  , [ 0.6045930611639507 , 0.7294381763744875 , 0.41317238743168894 ]
+  , [ 0.5181196004900488 , 0.7295797909722026 , 0.5181196004900487 ]
+  , [ 0.5321478467750966 , 0.908850927324427 , 0.7213170770599068 ]
+  , [ 0.6097884029414722 , 0.9088511267237062 , 0.6572150990573598 ]
+  , [ 0.5073894465820619 , 0.9128065261969955 , 0.7423359031749057 ]
+  , [ 0.5388137355108445 , 0.8790068974537643 , 0.6846273996141601 ]
+  , [ 0.518569427022487 , 1.0903982826657406 , 0.7590404339629727 ]
+  , [ 0.4768060384239586 , 0.9085520538404275 , 0.7584440485557873 ]
+  , [ 0.44008056524223993 , 0.8781985739488954 , 0.7505675479671516 ]
+  , [ 0.44119347577210555 , 0.7823424436625965 , 0.646458818858646 ]
+  , [ 0.3991852390173557 , 0.8781995871178615 , 0.772837999772326 ]
+  , [ 0.3856172858664447 , 0.908552060323908 , 0.8081008260732252 ]
+  , [ -0.6572150990573598 , 0.9088511267237064 , -0.609788402941472 ]
+  , [ -0.6360954419937749 , 0.912960352086011 , -0.6360954419937749 ]
+  , [ -0.7213170770599066
+    , 0.9088509273244272
+    , -0.5321478467750965
+    ]
+  , [ -0.7423359031749055
+    , 0.9128065261969955
+    , -0.5073894465820623
+    ]
+  , [ -0.3921629727500072
+    , 0.6074273900130303
+    , -0.4720306023200156
+    ]
+  , [ -2.3194160942469955e-2
+    , 2.6785629054825896e-2
+    , -2.3194160942469955e-2
+    ]
+  , [ -0.41317238743168894
+    , 0.7294381763744873
+    , -0.6045930611639508
+    ]
+  , [ -2.21510361877556e-2
+    , 2.5331770412978235e-2
+    , -2.215103618775549e-2
+    ]
+  , [ -1.7990621802653206e-2
+    , 1.986082558914795e-2
+    , -1.7990621802653095e-2
+    ]
+  , [ -0.2918249009919691
+    , 0.7297971305995977
+    , -0.6710598534195157
+    ]
+  , [ 9.869506660256812e-2
+    , 1.1894267902510014
+    , -0.9173511809757207
+    ]
+  , [ 0.192823289924143 , 1.0886815800201057 , -0.8962126634940769 ]
+  , [ 9.87971813131522e-2
+    , 1.2576022758257417
+    , -0.9183913329068394
+    ]
+  , [ -2.624567824552988e-17
+    , 1.0862351104914478
+    , -0.9157767877158495
+    ]
+  , [ 6.169852060031459e-17
+    , 0.912056858034264
+    , -0.8959767931433186
+    ]
+  , [ -9.86950666025681e-2
+    , 1.1894267902510014
+    , -0.9173511809757204
+    ]
+  , [ -0.6097884029414723
+    , 0.9088511267237063
+    , -0.6572150990573598
+    ]
+  , [ -0.6502787934703502
+    , 1.0902677960073714
+    , -0.6502787934703501
+    ]
+  , [ -2.7755575615628914e-17
+    , 0.7373497776866849
+    , -0.7373497776866843
+    ]
+  , [ -7.812642760551656e-3
+    , 7.812642760551715e-3
+    , -7.81264276055138e-3
+    ]
+  , [ 0.640521564385879 , 0.7748397465927045 , -0.43721962360169503 ]
+  , [ 0.6490953253086555 , 0.7194452653409784 , -0.3116338700316015 ]
+  , [ 0.6419080894398494 , 0.7908779235632781 , -0.45940727414369 ]
+  , [ 0.6045930611639505 , 0.7294381763744877 , -0.4131723874316888 ]
+  , [ 0.7708735521448064
+    , 0.8625025153160115
+    , -0.10873936568444004
+    ]
+  , [ 0.6357399515521689 , 0.8593528035528301 , -0.568778121333005 ]
+  , [ 0.7391783489851428 , 1.1731157788543423 , -0.5501647991014558 ]
+  , [ 0.771074187418324 , 0.8625025153160115 , -0.10800437930799994 ]
+  , [ 0.7733308622857775 , 1.287477942063759 , -0.5027968141957061 ]
+  , [ 0.7271032447312008 , 1.1472482940426965 , -0.5673075249989148 ]
+  , [ 0.6626047492685793 , 0.9655545808978973 , -0.6132901903841047 ]
+  , [ 0.7710778571614444 , 0.862498015165331 , -0.10798328513535704 ]
+  , [ 9.11422675134686e-2 , 1.5000742593050618 , 0.8663032960314866 ]
+  , [ 8.706652684736651e-17
+    , 1.5473984546329287
+    , 0.839000874648578
+    ]
+  , [ 9.015875195792442e-2
+    , 1.5174415681823772
+    , 0.8562836943885956
+    ]
+  , [ 0.0 , 1.6095711978718583 , 0.7838769658257825 ]
+  , [ -9.01587519579244e-2 , 1.517441568182377 , 0.8562836943885954 ]
+  , [ -0.1707356424814655 , 1.5458407114144659 , 0.8228126117740736 ]
+  , [ 9.3613324164243e-2 , 1.871978516403293 , 0.4470200027098403 ]
+  , [ 6.613605971990155e-2
+    , 1.7466998928928852
+    , 0.6342820077896054
+    ]
+  , [ 0.13411685349983243 , 1.878463096721167 , 0.42437151237708 ]
+  , [ 4.4461106873143405e-2
+    , 1.8791795052256692
+    , 0.4414598402161806
+    ]
+  , [ -7.668380969626695e-2
+    , 1.6481991362041957
+    , 0.7417373916617903
+    ]
+  , [ 0.0 , 1.764488100164783 , 0.6148766884290562 ]
+  , [ -0.4146046771732797 , 1.5166611367705873 , 0.755872347832471 ]
+  , [ -0.5499408317491791 , 1.5161703861218003 , 0.6646325123013266 ]
+  , [ 9.226906344799235e-2
+    , 1.979092419754217
+    , 0.21242895637465672
+    ]
+  , [ 8.589118029151292e-2
+    , 1.9867703444520366
+    , 0.19692078525583512
+    ]
+  , [ 0.4921333979229422 , 1.74614655744181 , 0.40683581887655035 ]
+  , [ 0.5796425375805216 , 1.6423746127113557 , 0.4787714299468666 ]
+  , [ 0.2162856218653722
+    , 1.986091229359059
+    , 1.3877787807814457e-17
+    ]
+  , [ 0.4414598402161807
+    , 1.8791795052256695
+    , 4.4461106873143405e-2
+    ]
+  , [ 0.4414598402161807
+    , 1.8791795052256695
+    , -4.4461106873143405e-2
+    ]
+  , [ 0.21397126895807833 , 1.9871467005290857 , 0.0 ]
+  , [ -0.7590404339629727
+    , 1.0903982826657406
+    , -0.5185694270224871
+    ]
+  , [ -0.7119923208428119
+    , 1.1741465971067808
+    , -0.5903677161648285
+    ]
+  , [ -0.8091216099094026
+    , 1.1765076558719516
+    , -0.4468960748716195
+    ]
+  , [ -0.8081008260732252
+    , 0.9085520603239079
+    , -0.3856172858664447
+    ]
+  , [ 0.3581881925466478 , 0.912425348113315 , -0.823534428936964 ]
+  , [ 0.18893822974950705
+    , 0.9120771417338429
+    , -0.8765366554024404
+    ]
+  , [ 0.5911999246804508 , 1.2599845756664991 , -0.7130046626566298 ]
+  , [ 0.5582878618142291 , 1.4934020335516496 , -0.6747863108748675 ]
+  , [ 0.7119923208428119 , 1.1741465971067808 , -0.5903677161648284 ]
+  , [ 0.6363011668042756 , 0.9129203244659402 , -0.635839187329811 ]
+  , [ 0.4720306023200157 , 0.60742739001303 , -0.3921629727500073 ]
+  , [ 2.3194160942469733e-2
+    , 2.6785629054825906e-2
+    , -2.3194160942469733e-2
+    ]
+  , [ 0.5181196004900488 , 0.7295797909722026 , -0.5181196004900487 ]
+  , [ 0.5329131536178869 , 0.6041132479674722 , -0.2962999185904397 ]
+  , [ 0.6469996697760714
+    , 0.7079075212097801
+    , -0.29260428801591876
+    ]
+  , [ 2.215103618775549e-2
+    , 2.533177041297907e-2
+    , -2.2151036187755047e-2
+    ]
+  , [ 2.2151036187755713e-2
+    , 2.5331770412978624e-2
+    , -2.215103618775549e-2
+    ]
+  , [ 0.39216297275000744 , 0.60742739001303 , -0.4720306023200158 ]
+  , [ -0.8796369493525764
+    , 0.9074581172580761
+    , -0.15370753430700962
+    ]
+  , [ -0.896212663494077
+    , 1.0886815800201057
+    , -0.19282328992414308
+    ]
+  , [ -0.6572150990573599 , 0.9088511267237063 , 0.6097884029414724 ]
+  , [ -0.6360954419937749 , 0.912960352086011 , 0.6360954419937749 ]
+  , [ -0.512501037698028 , 1.7617035212112466 , 0.34656596334419265 ]
+  , [ -0.378855443694696 , 1.8714260967581886 , 0.2571184603540509 ]
+  , [ -0.4921333979229422 , 1.74614655744181 , 0.4068358188765504 ]
+  , [ -0.5599378049841232
+    , 1.7463793702756347
+    , 0.30618459927572234
+    ]
+  , [ -0.6747863108748675 , 1.4934020335516496 , -0.558287861814229 ]
+  , [ -0.755872347832471 , 1.5166611367705873 , -0.4146046771732797 ]
+  , [ -0.8081008260732245
+    , 0.9085520603239081
+    , 0.38561728586644495
+    ]
+  , [ -0.8335353871385288
+    , 0.9080154562527454
+    , 0.32461261897613114
+    ]
+  , [ 0.7271032447312005 , 1.1472482940426967 , 0.5673075249989148 ]
+  , [ 0.6363011668042756 , 0.9129203244659402 , 0.635839187329811 ]
+  , [ 0.8105000296381791 , 1.4727320843657137 , 0.3507821217020296 ]
+  , [ 0.7670726439144389 , 1.4946619231456855 , 0.4206163489045974 ]
+  , [ 0.7838769658257825 , 1.6095711978718585 , 0.0 ]
+  , [ 0.7417373916617903
+    , 1.6481991362041957
+    , 7.668380969626695e-2
+    ]
+  , [ 0.7417373916617902 , 1.6481991362041954 , -7.6683809696267e-2 ]
+  , [ 0.7673351760829068
+    , 1.6096835638600524
+    , -0.15978142628509898
+    ]
+  , [ 0.6342820077896054
+    , 1.7466998928928852
+    , -6.613605971990152e-2
+    ]
+  , [ 0.8228126117740736 , 1.5458407114144659 , -0.1707356424814655 ]
+  , [ 0.42061634890459737 , 1.4946619231456855 , 0.7670726439144389 ]
+  , [ 0.26432271823494774 , 1.4970509470350617 , 0.8320774835437867 ]
+  , [ 0.4981854402325333 , 1.4717742047093716 , 0.7304675058910848 ]
+  , [ 0.41460467717327976 , 1.5166611367705878 , 0.7558723478324711 ]
+  , [ 0.4731627371704971 , 1.5418687589859696 , 0.6987516862837263 ]
+  , [ 0.2608962848335599 , 1.5173670225190703 , 0.8208298543940764 ]
+  , [ -0.5717139272637514
+    , 0.5968790484510275
+    , -0.18667466541971128
+    ]
+  , [ -1.7990621802653317e-2
+    , 1.9860825589147875e-2
+    , -1.799062180265354e-2
+    ]
+  , [ -0.7170049842821663
+    , 0.7326922941987838
+    , -0.15441948465438157
+    ]
+  , [ -0.7575092969305981
+    , 0.7759039565328365
+    , -0.16241732047101434
+    ]
+  , [ -0.8180448726617309 , 0.876840812979899 , -0.289831893259673 ]
+  , [ -0.715603493434698 , 0.7803637144380808 , -0.310292980977805 ]
+  , [ -0.823534428936964 , 0.912425348113315 , -0.3581881925466473 ]
+  , [ -0.8421531448806922
+    , 1.0902530151825576
+    , -0.36590860085651855
+    ]
+  , [ 0.5329131536178869 , 0.6041132479674723 , 0.2962999185904397 ]
+  , [ 0.5539859487435901 , 0.7828729928425694 , 0.55398594874359 ]
+  , [ 0.6360954419937748 , 0.9129603520860108 , 0.6360954419937748 ]
+  , [ 0.5725795213922534 , 0.8790071413593897 , 0.656749689590428 ]
+  , [ 0.6502787934703501 , 1.0902677960073712 , 0.6502787934703502 ]
+  , [ 0.7558723478324709
+    , 1.5166611367705873
+    , -0.41460467717327976
+    ]
+  , [ 0.7670726439144389 , 1.4946619231456852 , -0.4206163489045974 ]
+  , [ 0.7750736880510629 , 1.543449628093767 , -0.3300998702021285 ]
+  , [ 0.6987516862837263
+    , 1.5418687589859699
+    , -0.47316273717049717
+    ]
+  , [ -0.19282328992414305
+    , 1.0886815800201057
+    , -0.896212663494077
+    ]
+  , [ -0.18893822974950705
+    , 0.9120771417338428
+    , -0.8765366554024404
+    ]
+  , [ -0.283130810168734 , 1.1814109441614336 , -0.8788805908385667 ]
+  , [ -0.28347840420931564
+    , 1.2593473684859822
+    , -0.8800216675702618
+    ]
+  , [ -0.36590860085651855
+    , 1.0902530151825576
+    , -0.8421531448806925
+    ]
+  , [ -9.879718131315204e-2
+    , 1.2576022758257417
+    , -0.9183913329068394
+    ]
+  , [ -0.5388137355108445
+    , 0.8790068974537645
+    , -0.6846273996141601
+    ]
+  , [ -0.5321478467750966 , 0.908850927324427 , -0.7213170770599068 ]
+  , [ -0.358188192546648 , 0.912425348113315 , -0.8235344289369639 ]
+  , [ -0.32461261897613036
+    , 0.9080154562527454
+    , -0.8335353871385288
+    ]
+  , [ -0.3856172858664454
+    , 0.9085520603239081
+    , -0.8081008260732248
+    ]
+  , [ -0.39918523901735564
+    , 0.8781995871178618
+    , -0.7728379997723256
+    ]
+  , [ -0.47680603842395847
+    , 0.9085520538404271
+    , -0.7584440485557874
+    ]
+  , [ -0.4468960748716195
+    , 1.1765076558719516
+    , -0.8091216099094026
+    ]
+  , [ -0.44749843055983474
+    , 1.2598574705147236
+    , -0.8102438227139428
+    ]
+  , [ -0.518569427022487 , 1.0903982826657406 , -0.7590404339629725 ]
+  , [ -0.5073894465820619
+    , 0.9128065261969955
+    , -0.7423359031749057
+    ]
+  , [ -0.5903677161648284
+    , 1.1741465971067808
+    , -0.7119923208428119
+    ]
+  , [ -0.2962999185904398 , 0.6041132479674721 , -0.532913153617887 ]
+  , [ -0.44119347577210544
+    , 0.7823424436625968
+    , -0.6464588188586459
+    ]
+  , [ -0.31029298097780494
+    , 0.7803637144380812
+    , -0.7156034934346979
+    ]
+  , [ -0.44008056524224 , 0.8781985739488956 , -0.7505675479671517 ]
+  , [ -0.28983189325967296
+    , 0.8768408129798992
+    , -0.8180448726617306
+    ]
+  , [ -0.23969991808709237
+    , 0.8768406254951662
+    , -0.8337562524679074
+    ]
+  , [ -0.21959663365004273
+    , 0.9080153576084198
+    , -0.8664476852626977
+    ]
+  , [ 0.18424327753183933
+    , 1.4736973001288365
+    , -0.8624748737615431
+    ]
+  , [ -2.7755575615628914e-17
+    , 1.3011746498142651
+    , -0.9190561156885906
+    ]
+  , [ 6.448622334243023e-2
+    , 0.5851752797903262
+    , -0.5851752797903267
+    ]
+  , [ 0.0 , 0.7712853278687584 , -0.7698054940256471 ]
+  , [ 0.6646325123013265 , 1.5161703861218 , -0.549940831749179 ]
+  , [ 0.6493805393140206 , 1.6091658630935881 , -0.4401190932578934 ]
+  , [ 0.6590455307424883 , 1.643431115641555 , -0.3593826437345331 ]
+  , [ 0.5796425375805215
+    , 1.6423746127113557
+    , -0.47877142994686667
+    ]
+  , [ 0.0 , 1.9860912293590587 , 0.21628562186537217 ]
+  , [ 0.0 , 1.8719852675758741 , 0.4566121245194575 ]
+  , [ 0.0 , 2.0282480595325483 , 8.294756480297713e-2 ]
+  , [ 1.6023498781971006e-2
+    , 2.0298344402390818
+    , 7.461906659600522e-2
+    ]
+  , [ -4.163336342344337e-17
+    , 1.9871467005290855
+    , 0.21397126895807828
+    ]
+  , [ -1.6023498781971006e-2
+    , 2.0298344402390818
+    , 7.461906659600522e-2
+    ]
+  , [ 0.15197602557430875
+    , 1.9868541707659964
+    , 0.15197602557430875
+    ]
+  , [ 8.352331765507048e-2
+    , 1.9874788959388558
+    , 0.19585993121305542
+    ]
+  , [ 3.459146906505174e-2
+    , 2.028192844530145
+    , 7.606128724075058e-2
+    ]
+  , [ 1.3877787807814457e-17
+    , 1.9871467005290855
+    , 0.2139712689580783
+    ]
+  , [ -0.5851752797903265
+    , 0.5851752797903264
+    , -6.448622334243026e-2
+    ]
+  , [ -7.812642760551698e-3
+    , 7.81264276055331e-3
+    , -7.812642760553379e-3
+    ]
+  , [ -0.8631058252409901
+    , 0.8753821429287594
+    , -6.080525584912344e-2
+    ]
+  , [ -0.8567258893994794
+    , 0.8753822495491729
+    , -0.12280505144146825
+    ]
+  , [ -0.4731627371704971 , 1.5418687589859696 , 0.6987516862837264 ]
+  , [ -0.5970610762747673 , 1.5413389851975459 , 0.5970610762747673 ]
+  , [ -0.6254774679047082
+    , 1.4713967749278507
+    , -0.6254774679047082
+    ]
+  , [ -0.558287861814229 , 1.4934020335516496 , -0.6747863108748675 ]
+  , [ -0.6524829343907664
+    , 1.2954115625390261
+    , -0.6524829343907664
+    ]
+  , [ -0.6646325123013265 , 1.5161703861218 , -0.549940831749179 ]
+  , [ 0.6342820077896054
+    , 1.7466998928928852
+    , 6.613605971990152e-2
+    ]
+  , [ 0.6080853167177859 , 1.7467019433064042 , 0.19212615972377467 ]
+  , [ 0.5548468717518389 , 1.6090000296403644 , 0.5548468717518389 ]
+  , [ 0.6987516862837264 , 1.5418687589859699 , 0.47316273717049717 ]
+  , [ 0.7304675058910848 , 1.4717742047093716 , 0.4981854402325333 ]
+  , [ 0.6646325123013265 , 1.5161703861218 , 0.5499408317491791 ]
+  , [ 0.6747863108748674 , 1.4934020335516496 , 0.5582878618142292 ]
+  , [ 0.5970610762747673 , 1.5413389851975459 , 0.5970610762747673 ]
+  , [ 0.6524829343907664 , 1.2954115625390261 , -0.6524829343907664 ]
+  , [ 0.5903677161648287 , 1.1741465971067808 , -0.7119923208428119 ]
+  , [ 0.7130046626566298 , 1.2599845756664991 , -0.5911999246804508 ]
+  , [ 0.6254774679047082 , 1.4713967749278507 , -0.6254774679047082 ]
+  , [ 0.7622224951613341 , 1.2957898827631247 , -0.5194379814088045 ]
+  , [ 0.6502787934703502 , 1.0902677960073714 , -0.6502787934703502 ]
+  , [ 0.5499408317491791 , 1.5161703861218003 , 0.6646325123013266 ]
+  , [ 0.5582878618142292 , 1.4934020335516496 , 0.6747863108748676 ]
+  , [ -0.8306211796074129 , 1.5568496825908318 , 0.0 ]
+  , [ -0.7838769658257825 , 1.6095711978718583 , 0.0 ]
+  , [ -0.8338378098771851
+    , 1.5431253932352957
+    , 8.699039391932935e-2
+    ]
+  , [ -0.8338378098771851
+    , 1.5431253932352957
+    , -8.699039391932935e-2
+    ]
+  , [ -0.8788805908385666
+    , 1.1814109441614336
+    , -0.28313081016873404
+    ]
+  , [ -0.9170605544812714
+    , 1.1880416776333664
+    , -9.998920445288839e-2
+    ]
+  , [ -0.9058201893253578
+    , 1.2360005696799088
+    , -0.15764435612292219
+    ]
+  , [ -0.9173135774468576
+    , 1.1869621129641121
+    , -9.633778848140394e-2
+    ]
+  , [ -0.7130046626566299 , 1.2599845756664991 , 0.5911999246804511 ]
+  , [ -0.6502787934703503 , 1.0902677960073714 , 0.6502787934703503 ]
+  , [ -0.7417373916617903
+    , 1.6481991362041957
+    , 7.668380969626695e-2
+    ]
+  , [ -0.7417373916617903
+    , 1.6481991362041957
+    , -7.668380969626695e-2
+    ]
+  , [ -0.6342820077896054
+    , 1.7466998928928854
+    , 6.613605971990152e-2
+    ]
+  , [ -0.7673351760829068
+    , 1.6096835638600524
+    , 0.15978142628509898
+    ]
+  , [ -0.6031237664636153 , 1.763801627319081 , 0.12443603223298116 ]
+  , [ -0.6148766884290562 , 1.764488100164783 , 0.0 ]
+  , [ -0.4566121245194575
+    , 1.871985267575874
+    , -1.830024316166545e-17
+    ]
+  , [ -0.6342820077896054
+    , 1.7466998928928852
+    , -6.613605971990157e-2
+    ]
+  , [ -0.34408768727411854
+    , 1.877513818092077
+    , 0.28526260180018986
+    ]
+  , [ -0.3923832499457651 , 1.877827270100134 , 0.2127114473458056 ]
+  , [ -0.19692078525583515
+    , 1.9867703444520366
+    , 8.589118029151296e-2
+    ]
+  , [ -0.15197602557430873
+    , 1.9868541707659966
+    , 0.15197602557430875
+    ]
+  , [ -0.19585993121305542
+    , 1.9874788959388558
+    , 8.35233176550705e-2
+    ]
+  , [ -0.21242895637465672
+    , 1.979092419754217
+    , 9.226906344799235e-2
+    ]
+  , [ -0.7140470378267627 , 1.645484159660599 , 0.22440550617121358 ]
+  , [ -0.8228126117740738 , 1.545840711414466 , 0.17073564248146567 ]
+  , [ -0.7558723478324709
+    , 1.5166611367705873
+    , 0.41460467717327976
+    ]
+  , [ -0.6987516862837263
+    , 1.5418687589859699
+    , 0.47316273717049717
+    ]
+  , [ -0.7750736880510629 , 1.543449628093767 , 0.3300998702021285 ]
+  , [ -0.7670726439144389 , 1.4946619231456852 , 0.4206163489045974 ]
+  , [ -0.7210973557556136
+    , 1.6095465768220536
+    , 0.30771930990769886
+    ]
+  , [ -0.8208298543940764 , 1.5173670225190703 , 0.2608962848335599 ]
+  , [ -0.5911999246804508
+    , 1.2599845756664991
+    , -0.7130046626566298
+    ]
+  , [ -0.5194379814088046
+    , 1.2957898827631247
+    , -0.7622224951613342
+    ]
+  , [ 0.6254774679047082 , 1.4713967749278507 , 0.625477467904708 ]
+  , [ 0.7622224951613341 , 1.2957898827631245 , 0.5194379814088045 ]
+  , [ 0.5599378049841232
+    , 1.7463793702756347
+    , -0.30618459927572234
+    ]
+  , [ 0.568522694152947 , 1.7624263437337935 , -0.2414067196592598 ]
+  , [ 0.512501037698028 , 1.7617035212112468 , -0.34656596334419265 ]
+  , [ 0.7210973557556135 , 1.6095465768220536 , -0.3077193099076989 ]
+  , [ 0.4204153875105169
+    , 1.8716132855102106
+    , -0.18017138946943928
+    ]
+  , [ 0.42437151237708004
+    , 1.8784630967211673
+    , -0.13411685349983243
+    ]
+  , [ 0.3923832499457651 , 1.877827270100134 , -0.2127114473458056 ]
+  , [ 0.4470200027098403 , 1.871978516403293 , -9.3613324164243e-2 ]
+  , [ 0.21242895637465675
+    , 1.979092419754217
+    , -9.22690634479924e-2
+    ]
+  , [ 0.6080853167177859 , 1.7467019433064044 , -0.1921261597237747 ]
+  , [ 0.7140470378267627
+    , 1.6454841596605991
+    , -0.22440550617121363
+    ]
+  , [ 0.6031237664636153
+    , 1.7638016273190815
+    , -0.12443603223298119
+    ]
+  , [ 0.6524829343907664 , 1.2954115625390261 , 0.6524829343907664 ]
+  , [ 0.5194379814088046 , 1.2957898827631245 , 0.7622224951613343 ]
+  , [ -0.7622224951613341 , 1.2957898827631245 , 0.5194379814088045 ]
+  , [ -0.8105000296381791 , 1.4727320843657137 , 0.3507821217020296 ]
+  , [ -0.1228050514414682
+    , 0.8753822495491729
+    , -0.8567258893994794
+    ]
+  , [ -6.080525584912345e-2
+    , 0.8753821429287596
+    , -0.86310582524099
+    ]
+  , [ -0.15370753430700937
+    , 0.907458117258076
+    , -0.8796369493525762
+    ]
+  , [ -3.497482895265056e-2
+    , 0.9074580789365942
+    , -0.8918549800719824
+    ]
+  , [ 9.114226751346854e-2
+    , 1.5000742593050618
+    , -0.8663032960314866
+    ]
+  , [ 0.2608962848335599 , 1.5173670225190703 , -0.8208298543940764 ]
+  , [ 0.3300998702021285 , 1.5434496280937666 , -0.7750736880510629 ]
+  , [ 0.17073564248146547 , 1.545840711414466 , -0.8228126117740737 ]
+  , [ -0.1916029836825906 , 1.29931244670657 , -0.8997455590151622 ]
+  , [ 5.276171965179255e-17
+    , 1.4739386059327377
+    , -0.8813815581455101
+    ]
+  , [ 6.080525584912359e-2
+    , 0.8753821429287593
+    , -0.8631058252409899
+    ]
+  , [ 3.497482895265053e-2
+    , 0.9074580789365946
+    , -0.8918549800719818
+    ]
+  , [ 0.15441948465438154 , 0.732692294198784 , -0.7170049842821665 ]
+  , [ 7.812642760551658e-3
+    , 7.8126427605515e-3
+    , -7.812642760551602e-3
+    ]
+  , [ 0.4131723874316888 , 0.7294381763744876 , -0.6045930611639508 ]
+  , [ 0.5539859487435899 , 0.7828729928425695 , -0.5539859487435899 ]
+  , [ 0.44119347577210555
+    , 0.7823424436625966
+    , -0.6464588188586459
+    ]
+  , [ 0.2962999185904397 , 0.6041132479674723 , -0.5329131536178867 ]
+  , [ 0.7304675058910848
+    , 1.4717742047093716
+    , -0.49818544023253314
+    ]
+  , [ 0.8239288970287512
+    , 1.4058726252835607
+    , -0.35635039483568703
+    ]
+  , [ 0.6747863108748675 , 1.4934020335516496 , -0.558287861814229 ]
+  , [ 0.8105000296381791 , 1.4727320843657137 , -0.3507821217020296 ]
+  , [ -0.8228126117740736 , 1.545840711414466 , -0.1707356424814655 ]
+  , [ -0.7673351760829068
+    , 1.6096835638600524
+    , -0.15978142628509898
+    ]
+  , [ -0.8208298543940763 , 1.5173670225190703 , -0.26089628483356 ]
+  , [ -0.8254897540595187
+    , 1.5435692417971658
+    , -0.16429080162183143
+    ]
+  , [ -0.7750736880510629 , 1.543449628093767 , -0.3300998702021285 ]
+  , [ -0.8228724369633584
+    , 1.5136775995208986
+    , -0.26151852913476525
+    ]
+  , [ -0.2414067196592598 , 1.7624263437337935 , 0.568522694152947 ]
+  , [ -0.18017138946943925
+    , 1.8716132855102106
+    , 0.4204153875105169
+    ]
+  , [ -0.19212615972377473 , 1.746701943306404 , 0.6080853167177859 ]
+  , [ -0.3061845992757223 , 1.7463793702756347 , 0.5599378049841232 ]
+  , [ -0.13411685349983243 , 1.878463096721167 , 0.42437151237708 ]
+  , [ -0.2127114473458056 , 1.877827270100134 , 0.3923832499457651 ]
+  , [ -0.12443603223298119 , 1.763801627319081 , 0.6031237664636153 ]
+  , [ -0.22440550617121352
+    , 1.6454841596605991
+    , 0.7140470378267627
+    ]
+  , [ -6.613605971990154e-2
+    , 1.7466998928928852
+    , 0.6342820077896054
+    ]
+  , [ -0.15978142628509898
+    , 1.6096835638600524
+    , 0.7673351760829068
+    ]
+  , [ -4.4461106873143405e-2
+    , 1.8791795052256695
+    , 0.4414598402161806
+    ]
+  , [ -9.361332416424303e-2
+    , 1.871978516403293
+    , 0.4470200027098403
+    ]
+  , [ -5.957385788325474e-2
+    , 2.0280904764199947
+    , 5.957385788325473e-2
+    ]
+  , [ -4.433393286244257e-2
+    , 2.0294496432112377
+    , 6.476360671202533e-2
+    ]
+  , [ -6.476360671202533e-2
+    , 2.0294496432112377
+    , 4.433393286244257e-2
+    ]
+  , [ -0.15197602557430875
+    , 1.9868541707659964
+    , 0.15197602557430875
+    ]
+  , [ -8.352331765507041e-2
+    , 1.9874788959388558
+    , 0.19585993121305537
+    ]
+  , [ -3.459146906505173e-2
+    , 2.028192844530145
+    , 7.606128724075058e-2
+    ]
+  , [ -8.589118029151296e-2
+    , 1.9867703444520366
+    , 0.19692078525583517
+    ]
+  , [ -9.226906344799235e-2
+    , 1.9790924197542166
+    , 0.21242895637465672
+    ]
+  , [ 0.19585993121305542
+    , 1.9874788959388558
+    , 8.352331765507048e-2
+    ]
+  , [ 7.606128724075059e-2
+    , 2.028192844530145
+    , 3.459146906505173e-2
+    ]
+  , [ 1.607119023392417e-3
+    , 2.0437414787107526
+    , 1.6071190233924448e-3
+    ]
+  , [ 4.433393286244257e-2
+    , 2.0294496432112377
+    , 6.476360671202529e-2
+    ]
+  , [ -0.43782916277770734 , 1.761318060465615 , 0.4378291627777074 ]
+  , [ -0.3239549649324688 , 1.871257263189551 , 0.3239549649324688 ]
+  , [ -0.4068358188765504 , 1.74614655744181 , 0.4921333979229422 ]
+  , [ -0.5796425375805216 , 1.6423746127113557 , 0.4787714299468666 ]
+  , [ -0.3300998702021285 , 1.543449628093767 , 0.7750736880510629 ]
+  , [ -0.44011909325789345
+    , 1.6091658630935883
+    , 0.6493805393140207
+    ]
+  , [ -0.6493805393140206 , 1.6091658630935881 , 0.4401190932578934 ]
+  , [ -0.5548468717518389 , 1.6090000296403644 , 0.5548468717518389 ]
+  , [ -0.6590455307424883 , 1.643431115641555 , 0.3593826437345331 ]
+  , [ -0.47877142994686667
+    , 1.6423746127113557
+    , 0.5796425375805216
+    ]
+  , [ -0.5499408317491791
+    , 1.5161703861218003
+    , -0.6646325123013266
+    ]
+  , [ -0.49818544023253325
+    , 1.4717742047093716
+    , -0.7304675058910849
+    ]
+  , [ -0.36541277208493106
+    , 1.2970587028449376
+    , -0.8457844093935878
+    ]
+  , [ -0.4206163489045974
+    , 1.4946619231456855
+    , -0.7670726439144389
+    ]
+  , [ 0.6031237664636153 , 1.7638016273190815 , 0.12443603223298119 ]
+  , [ 0.4470200027098403 , 1.871978516403293 , 9.3613324164243e-2 ]
+  , [ 0.1958599312130554
+    , 1.9874788959388558
+    , -8.352331765507048e-2
+    ]
+  , [ 0.2139712689580783 , 1.9871467005290855 , 0.0 ]
+  , [ 0.19692078525583515
+    , 1.9867703444520366
+    , -8.589118029151296e-2
+    ]
+  , [ 7.606128724075059e-2
+    , 2.028192844530145
+    , -3.459146906505173e-2
+    ]
+  , [ 7.46190665960052e-2
+    , 2.0298344402390818
+    , -1.6023498781971e-2
+    ]
+  , [ 6.47636067120253e-2
+    , 2.0294496432112377
+    , -4.433393286244257e-2
+    ]
+  , [ 0.5548468717518389 , 1.6090000296403646 , -0.5548468717518389 ]
+  , [ 0.5970610762747675 , 1.5413389851975459 , -0.5970610762747675 ]
+  , [ 0.47877142994686667
+    , 1.6423746127113557
+    , -0.5796425375805215
+    ]
+  , [ 0.4921333979229422 , 1.74614655744181 , -0.40683581887655035 ]
+  , [ 0.44011909325789345
+    , 1.6091658630935881
+    , -0.6493805393140206
+    ]
+  , [ 0.40683581887655035 , 1.74614655744181 , -0.4921333979229422 ]
+  , [ -0.41460467717327976
+    , 1.5166611367705878
+    , -0.7558723478324711
+    ]
+  , [ -0.3300998702021285 , 1.543449628093767 , -0.7750736880510629 ]
+  , [ -0.4731627371704971
+    , 1.5418687589859696
+    , -0.6987516862837263
+    ]
+  , [ -0.3507821217020296
+    , 1.4727320843657137
+    , -0.8105000296381792
+    ]
+  , [ 0.2244055061712136 , 1.6454841596605991 , -0.7140470378267627 ]
+  , [ 0.3077193099076989 , 1.6095465768220536 , -0.7210973557556135 ]
+  , [ 0.19212615972377467 , 1.7467019433064042 , -0.608085316717786 ]
+  , [ 0.15978142628509895
+    , 1.6096835638600524
+    , -0.7673351760829068
+    ]
+  , [ 0.4731627371704971 , 1.5418687589859696 , -0.6987516862837264 ]
+  , [ 0.549940831749179 , 1.5161703861218 , -0.6646325123013266 ]
+  , [ 0.35938264373453305 , 1.643431115641555 , -0.6590455307424883 ]
+  , [ 0.30618459927572234
+    , 1.7463793702756347
+    , -0.5599378049841232
+    ]
+  , [ -0.886198300038301
+    , 1.2718626269690558
+    , -0.25470716973839896
+    ]
+  , [ -0.880021667570262
+    , 1.2593473684859824
+    , -0.28347840420931564
+    ]
+  , [ -0.7140470378267627
+    , 1.6454841596605991
+    , -0.2244055061712136
+    ]
+  , [ -0.6080853167177859
+    , 1.7467019433064042
+    , -0.19212615972377467
+    ]
+  , [ -0.7210973557556135
+    , 1.6095465768220534
+    , -0.3077193099076989
+    ]
+  , [ -0.6031237664636153
+    , 1.7638016273190813
+    , -0.12443603223298118
+    ]
+  , [ -0.25711846035405095
+    , 1.8714260967581882
+    , 0.3788554436946959
+    ]
+  , [ -0.28526260180018986
+    , 1.8775138180920772
+    , 0.34408768727411854
+    ]
+  , [ -0.34656596334419265
+    , 1.7617035212112468
+    , 0.5125010376980281
+    ]
+  , [ -0.4204153875105169
+    , 1.8716132855102106
+    , 0.18017138946943928
+    ]
+  , [ -0.42437151237708004
+    , 1.8784630967211673
+    , 0.13411685349983243
+    ]
+  , [ -0.6080853167177859 , 1.7467019433064044 , 0.1921261597237747 ]
+  , [ -0.568522694152947 , 1.7624263437337935 , 0.2414067196592598 ]
+  , [ 0.7130046626566298 , 1.2599845756664991 , 0.5911999246804508 ]
+  , [ 0.7119923208428119 , 1.1741465971067808 , 0.5903677161648285 ]
+  , [ 0.45661212451945754 , 1.8719852675758741 , 0.0 ]
+  , [ 0.6148766884290562 , 1.764488100164783 , 0.0 ]
+  , [ 0.5911999246804508 , 1.2599845756664991 , 0.7130046626566298 ]
+  , [ 0.5903677161648285 , 1.1741465971067808 , 0.7119923208428119 ]
+  , [ -0.9058201893253579
+    , 1.2360005696799088
+    , 0.15764435612292219
+    ]
+  , [ -0.8861983000383011
+    , 1.2718626269690558
+    , 0.25470716973839896
+    ]
+  , [ -0.9170605544812716
+    , 1.1880416776333662
+    , 9.998920445288842e-2
+    ]
+  , [ -0.8254897540595187
+    , 1.5435692417971658
+    , 0.16429080162183185
+    ]
+  , [ -0.9173135774468574
+    , 1.1869621129641121
+    , 9.633778848140408e-2
+    ]
+  , [ -0.9157767877158494 , 1.0862351104914478 , 0.0 ]
+  , [ -0.8765366554024407
+    , 0.9120771417338429
+    , 0.18893822974950678
+    ]
+  , [ -0.8235344289369638 , 0.9124253481133151 , 0.358188192546648 ]
+  , [ -0.3077193099076989 , 1.6095465768220536 , 0.7210973557556136 ]
+  , [ -0.35938264373453305 , 1.643431115641555 , 0.6590455307424883 ]
+  , [ 0.8208298543940763
+    , 1.5173670225190703
+    , -0.26089628483355987
+    ]
+  , [ 0.8320774835437867
+    , 1.4970509470350615
+    , -0.26432271823494774
+    ]
+  , [ 0.29182490099196934
+    , 0.7297971305995975
+    , -0.6710598534195162
+    ]
+  , [ 0.47680603842395847
+    , 0.9085520538404271
+    , -0.7584440485557874
+    ]
+  , [ 0.5073894465820622 , 0.9128065261969955 , -0.7423359031749056 ]
+  , [ 0.3856172858664447 , 0.908552060323908 , -0.8081008260732252 ]
+  , [ 0.44008056524223993
+    , 0.8781985739488957
+    , -0.7505675479671516
+    ]
+  , [ 0.3991852390173557 , 0.8781995871178615 , -0.772837999772326 ]
+  , [ 0.5388137355108445 , 0.8790068974537643 , -0.6846273996141601 ]
+  , [ 9.01587519579244e-2 , 1.517441568182377 , -0.8562836943885954 ]
+  , [ 7.668380969626695e-2
+    , 1.6481991362041957
+    , -0.7417373916617903
+    ]
+  , [ 0.6360954419937748 , 0.9129603520860109 , -0.636095441993775 ]
+  , [ 0.5321478467750965 , 0.9088509273244271 , -0.7213170770599069 ]
+  , [ 5.957385788325474e-2
+    , 2.0280904764199947
+    , 5.957385788325473e-2
+    ]
+  , [ 6.476360671202533e-2
+    , 2.0294496432112377
+    , 4.433393286244257e-2
+    ]
+  , [ 2.988199908178335e-3
+    , 2.0433955508208292
+    , 2.988199908178321e-3
+    ]
+  , [ -0.8918549800719823
+    , 0.9074580789365944
+    , -3.497482895265047e-2
+    ]
+  , [ -0.8959767931433185
+    , 0.912056858034264
+    , -2.7755575615628914e-17
+    ]
+  , [ -0.4470200027098403
+    , 1.871978516403293
+    , -9.361332416424299e-2
+    ]
+  , [ -0.42437151237708 , 1.878463096721167 , -0.13411685349983246 ]
+  , [ -0.4414598402161806
+    , 1.8791795052256695
+    , -4.4461106873143384e-2
+    ]
+  , [ -0.568522694152947 , 1.7624263437337935 , -0.2414067196592598 ]
+  , [ -0.18424327753183928
+    , 1.4736973001288365
+    , -0.8624748737615431
+    ]
+  , [ -0.26432271823494774
+    , 1.4970509470350615
+    , -0.8320774835437866
+    ]
+  , [ -9.114226751346857e-2
+    , 1.5000742593050618
+    , -0.8663032960314865
+    ]
+  , [ -0.26089628483355987
+    , 1.5173670225190703
+    , -0.8208298543940763
+    ]
+  , [ 0.34656596334419265 , 1.7617035212112468 , -0.512501037698028 ]
+  , [ 0.2414067196592598 , 1.7624263437337935 , -0.568522694152947 ]
+  , [ 0.15197602557430875
+    , 1.9868541707659966
+    , -0.15197602557430875
+    ]
+  , [ 0.378855443694696 , 1.8714260967581884 , -0.25711846035405095 ]
+  , [ -0.3077193099076989
+    , 1.6095465768220534
+    , -0.7210973557556135
+    ]
+  , [ -0.17073564248146547
+    , 1.545840711414466
+    , -0.8228126117740737
+    ]
+  , [ -0.2244055061712136
+    , 1.6454841596605991
+    , -0.7140470378267627
+    ]
+  , [ -0.35938264373453305
+    , 1.643431115641555
+    , -0.6590455307424883
+    ]
+  , [ 4.446110687314339e-2
+    , 1.8791795052256695
+    , -0.4414598402161806
+    ]
+  , [ 9.361332416424298e-2
+    , 1.871978516403293
+    , -0.4470200027098403
+    ]
+  , [ 1.3877787807814457e-17
+    , 1.986091229359059
+    , -0.2162856218653722
+    ]
+  , [ 0.0 , 1.8719852675758741 , -0.45661212451945754 ]
+  , [ -0.16621776154382692
+    , 1.9776053853655182
+    , 0.16621776154382692
+    ]
+  , [ -0.4470200027098403
+    , 1.871978516403293
+    , 9.361332416424298e-2
+    ]
+  , [ -0.4414598402161806
+    , 1.8791795052256695
+    , 4.446110687314339e-2
+    ]
+  , [ 8.29475648029771e-2 , 2.0282480595325483 , 0.0 ]
+  , [ 7.461906659600517e-2
+    , 2.0298344402390813
+    , 1.6023498781971006e-2
+    ]
+  , [ 1.6071190233923754e-3
+    , 2.0437414787107526
+    , -1.607119023392417e-3
+    ]
+  , [ -2.988199908178335e-3
+    , 2.0433955508208292
+    , 2.988199908178321e-3
+    ]
+  , [ -1.607119023392417e-3
+    , 2.0437414787107526
+    , 1.6071190233924448e-3
+    ]
+  , [ -0.822872436963358 , 1.5136775995208986 , 0.26151852913476537 ]
+  , [ -0.8260665185299888 , 1.4902762907980336 , 0.2884082499027695 ]
+  , [ -0.8962126634940768 , 1.0886815800201057 , 0.192823289924143 ]
+  , [ -0.8788805908385666 , 1.1814109441614336 , 0.2831308101687341 ]
+  , [ -0.8800216675702618 , 1.2593473684859822 , 0.2834784042093155 ]
+  , [ -0.8421531448806925
+    , 1.0902530151825576
+    , 0.36590860085651855
+    ]
+  , [ 2.7755575615628914e-17
+    , 1.547398454632929
+    , -0.8390008746485779
+    ]
+  , [ -9.015875195792439e-2
+    , 1.5174415681823774
+    , -0.8562836943885955
+    ]
+  , [ 0.310292980977805 , 0.7803637144380807 , -0.7156034934346982 ]
+  , [ 0.3246126189761304 , 0.9080154562527455 , -0.8335353871385288 ]
+  , [ 0.2898318932596732 , 0.8768408129798986 , -0.8180448726617308 ]
+  , [ 0.21959663365004273
+    , 0.9080153576084198
+    , -0.8664476852626977
+    ]
+  , [ 0.15370753430700937 , 0.907458117258076 , -0.8796369493525762 ]
+  , [ 0.1228050514414682 , 0.8753822495491729 , -0.8567258893994794 ]
+  , [ 0.5725795213922535 , 0.8790071413593896 , -0.6567496895904281 ]
+  , [ 0.6097884029414725 , 0.9088511267237063 , -0.65721509905736 ]
+  , [ -0.4401190932578934
+    , 1.6091658630935881
+    , -0.6493805393140207
+    ]
+  , [ -0.5970610762747673
+    , 1.5413389851975459
+    , -0.5970610762747673
+    ]
+  , [ -0.30618459927572234
+    , 1.7463793702756347
+    , -0.5599378049841232
+    ]
+  , [ -0.47877142994686667
+    , 1.6423746127113557
+    , -0.5796425375805215
+    ]
+  , [ -0.40683581887655035 , 1.74614655744181 , -0.4921333979229422 ]
+  , [ -0.5548468717518389
+    , 1.6090000296403644
+    , -0.5548468717518389
+    ]
+  , [ -0.34656596334419265
+    , 1.7617035212112468
+    , -0.512501037698028
+    ]
+  , [ -0.4378291627777074
+    , 1.7613180604656151
+    , -0.43782916277770734
+    ]
+  , [ -0.21242895637465672
+    , 1.9790924197542168
+    , -9.226906344799235e-2
+    ]
+  , [ -0.39238324994576507
+    , 1.8778272701001337
+    , -0.21271144734580563
+    ]
+  , [ -0.19692078525583517
+    , 1.9867703444520366
+    , -8.589118029151296e-2
+    ]
+  , [ -0.4204153875105169
+    , 1.8716132855102106
+    , -0.18017138946943928
+    ]
+  , [ 0.12443603223298116 , 1.763801627319081 , -0.6031237664636153 ]
+  , [ 0.18017138946943925
+    , 1.8716132855102106
+    , -0.42041538751051694
+    ]
+  , [ 6.613605971990155e-2
+    , 1.7466998928928852
+    , -0.6342820077896054
+    ]
+  , [ 0.13411685349983243 , 1.878463096721167 , -0.42437151237708 ]
+  , [ 0.0 , 1.6095711978718583 , -0.7838769658257825 ]
+  , [ -0.15978142628509895
+    , 1.6096835638600524
+    , -0.7673351760829068
+    ]
+  , [ 0.0 , 1.764488100164783 , -0.6148766884290562 ]
+  , [ -4.4461106873143405e-2
+    , 1.8791795052256695
+    , -0.4414598402161806
+    ]
+  , [ -7.606128724075058e-2
+    , 2.028192844530145
+    , 3.459146906505172e-2
+    ]
+  , [ -0.21397126895807833 , 1.9871467005290857 , 0.0 ]
+  , [ -8.29475648029771e-2 , 2.0282480595325483 , 0.0 ]
+  , [ -7.461906659600517e-2
+    , 2.0298344402390813
+    , 1.6023498781971006e-2
+    ]
+  , [ -7.461906659600517e-2
+    , 2.0298344402390813
+    , -1.6023498781971006e-2
+    ]
+  , [ -0.21397126895807833 , 1.987146700529086 , 0.0 ]
+  , [ -0.2162856218653722
+    , 1.9860912293590587
+    , -4.293150814092422e-18
+    ]
+  , [ -1.6023498781971048e-2
+    , 2.0298344402390818
+    , -7.461906659600523e-2
+    ]
+  , [ 0.0 , 2.0282480595325483 , -8.294756480297713e-2 ]
+  , [ -1.607119023392417e-3
+    , 2.0437414787107526
+    , -1.6071190233923754e-3
+    ]
+  , [ -3.459146906505177e-2
+    , 2.028192844530145
+    , -7.606128724075062e-2
+    ]
+  , [ -8.352331765507041e-2
+    , 1.9874788959388558
+    , -0.19585993121305537
+    ]
+  , [ -4.433393286244257e-2
+    , 2.0294496432112377
+    , -6.47636067120253e-2
+    ]
+  , [ 1.6071190233924448e-3
+    , 2.0437414787107526
+    , 1.6071190233924448e-3
+    ]
+  , [ 2.988199908178335e-3
+    , 2.0433955508208292
+    , -2.988199908178363e-3
+    ]
+  , [ 4.4333932862442554e-2
+    , 2.029449643211237
+    , -6.476360671202536e-2
+    ]
+  , [ 5.957385788325471e-2
+    , 2.0280904764199947
+    , -5.95738578832547e-2
+    ]
+  , [ -0.8457844093935878
+    , 1.2970587028449376
+    , 0.36541277208493106
+    ]
+  , [ -0.8102438227139426 , 1.2598574705147234 , 0.4474984305598345 ]
+  , [ -0.8091216099094026 , 1.1765076558719514 , 0.4468960748716195 ]
+  , [ -0.7119923208428118 , 1.1741465971067808 , 0.5903677161648285 ]
+  , [ 0.18667466541971134
+    , 0.5968790484510273
+    , -0.5717139272637516
+    ]
+  , [ 1.7990621802653206e-2
+    , 1.986082558914795e-2
+    , -1.7990621802653095e-2
+    ]
+  , [ 0.2396999180870924 , 0.8768406254951663 , -0.8337562524679073 ]
+  , [ 0.16241732047101426
+    , 0.7759039565328367
+    , -0.7575092969305981
+    ]
+  , [ -0.6987516862837264
+    , 1.5418687589859696
+    , -0.4731627371704971
+    ]
+  , [ -0.5796425375805216
+    , 1.6423746127113557
+    , -0.4787714299468666
+    ]
+  , [ -0.4921333979229422 , 1.74614655744181 , -0.40683581887655035 ]
+  , [ -0.6493805393140207
+    , 1.6091658630935883
+    , -0.44011909325789345
+    ]
+  , [ -0.16621776154382692
+    , 1.9776053853655182
+    , -0.16621776154382692
+    ]
+  , [ -0.28526260180018986
+    , 1.877513818092077
+    , -0.34408768727411854
+    ]
+  , [ -0.15197602557430878
+    , 1.9868541707659964
+    , -0.15197602557430875
+    ]
+  , [ -0.34408768727411854
+    , 1.8775138180920767
+    , -0.28526260180018986
+    ]
+  , [ -0.2571184603540509
+    , 1.8714260967581884
+    , -0.3788554436946959
+    ]
+  , [ -0.3239549649324688 , 1.871257263189551 , -0.3239549649324688 ]
+  , [ -0.378855443694696
+    , 1.8714260967581884
+    , -0.25711846035405095
+    ]
+  , [ -0.5599378049841232
+    , 1.746379370275635
+    , -0.30618459927572234
+    ]
+  , [ -0.512501037698028
+    , 1.7617035212112468
+    , -0.34656596334419265
+    ]
+  , [ -0.6590455307424883
+    , 1.643431115641555
+    , -0.35938264373453305
+    ]
+  , [ -0.18017138946943928
+    , 1.8716132855102106
+    , -0.4204153875105169
+    ]
+  , [ -0.13411685349983246 , 1.878463096721167 , -0.42437151237708 ]
+  , [ -0.2127114473458056
+    , 1.8778272701001337
+    , -0.39238324994576507
+    ]
+  , [ -0.2414067196592598 , 1.7624263437337935 , -0.568522694152947 ]
+  , [ -0.1921261597237747
+    , 1.7467019433064042
+    , -0.6080853167177859
+    ]
+  , [ -9.226906344799232e-2
+    , 1.979092419754217
+    , -0.21242895637465659
+    ]
+  , [ -0.19585993121305542
+    , 1.9874788959388558
+    , -8.352331765507048e-2
+    ]
+  , [ -7.606128724075059e-2
+    , 2.028192844530145
+    , -3.459146906505173e-2
+    ]
+  , [ 9.226906344799235e-2
+    , 1.979092419754217
+    , -0.21242895637465672
+    ]
+  , [ 0.2127114473458056 , 1.877827270100134 , -0.3923832499457651 ]
+  , [ 8.589118029151292e-2
+    , 1.9867703444520366
+    , -0.19692078525583512
+    ]
+  , [ 0.25711846035405095 , 1.8714260967581884 , -0.378855443694696 ]
+  , [ 0.15197602557430875
+    , 1.9868541707659966
+    , -0.15197602557430878
+    ]
+  , [ 8.35233176550705e-2
+    , 1.9874788959388558
+    , -0.19585993121305542
+    ]
+  , [ 3.459146906505177e-2
+    , 2.028192844530145
+    , -7.606128724075062e-2
+    ]
+  , [ 0.34408768727411854
+    , 1.877513818092077
+    , -0.28526260180018986
+    ]
+  , [ 0.43782916277770734
+    , 1.7613180604656151
+    , -0.4378291627777074
+    ]
+  , [ -7.668380969626697e-2
+    , 1.6481991362041954
+    , -0.7417373916617902
+    ]
+  , [ -0.1244360322329812 , 1.763801627319081 , -0.6031237664636153 ]
+  , [ -6.613605971990157e-2
+    , 1.746699892892885
+    , -0.6342820077896053
+    ]
+  , [ -9.361332416424303e-2
+    , 1.871978516403293
+    , -0.4470200027098403
+    ]
+  , [ -1.6071190233924448e-3
+    , 2.0437414787107526
+    , 1.6071190233924448e-3
+    ]
+  , [ 1.602349878197104e-2
+    , 2.0298344402390813
+    , -7.461906659600523e-2
+    ]
+  , [ 1.6071190233924448e-3
+    , 2.0437414787107526
+    , -1.6071190233924448e-3
+    ]
+  , [ 4.163336342344337e-17
+    , 1.9871467005290857
+    , -0.2139712689580784
+    ]
+  , [ 1.3877787807814457e-17
+    , 1.9871467005290855
+    , -0.2139712689580783
+    ]
+  , [ -8.589118029151295e-2
+    , 1.9867703444520366
+    , -0.19692078525583515
+    ]
+  , [ -0.15197602557430875
+    , 1.9868541707659964
+    , -0.15197602557430875
+    ]
+  , [ -0.7590404339629725 , 1.0903982826657406 , 0.518569427022487 ]
+  , [ -0.7423359031749055 , 0.9128065261969956 , 0.5073894465820623 ]
+  , [ -5.957385788325474e-2
+    , 2.0280904764199947
+    , -5.957385788325473e-2
+    ]
+  , [ -6.476360671202533e-2
+    , 2.0294496432112377
+    , -4.433393286244257e-2
+    ]
+  , [ -2.988199908178335e-3
+    , 2.0433955508208292
+    , -2.988199908178321e-3
+    ]
+  , [ -1.6071190233924448e-3
+    , 2.0437414787107526
+    , -1.6071190233924448e-3
+    ]
+  , [ 0.16621776154382692
+    , 1.9776053853655182
+    , -0.16621776154382692
+    ]
+  , [ 0.2852626018001899 , 1.8775138180920772 , -0.3440876872741186 ]
+  , [ 0.3239549649324688 , 1.871257263189551 , -0.3239549649324688 ]
+  , [ 0.0 , 0.0 , 0.0 ]
+  , [ 2.7755575615628914e-17 , 2.0440475966384333 , 0.0 ] ]
diff --git a/src/ConvexHull/OmniTruncated120Cell.hs b/src/ConvexHull/OmniTruncated120Cell.hs
new file mode 100644
--- /dev/null
+++ b/src/ConvexHull/OmniTruncated120Cell.hs
@@ -0,0 +1,108 @@
+module ConvexHull.OmniTruncated120Cell (vs120omnitrunc)
+  where
+import           Data.List
+import           Math.Combinat.Permutations as P
+-- http://eusebeia.dyndns.org/4d/trunc120cell
+-- http://mathworld.wolfram.com/120-Cell.html
+
+vertices :: ([Double], Bool) -> [[Double]]
+vertices (coords, allperms) =
+  map (map (/ sqrt 6376.10896088)) $ signsAll $
+  nub $ zipWith permuteList perms (replicate 24 coords)
+  where
+  perms = filter (if allperms then const True else isEvenPermutation) (P.permutations 4)
+  signsAll :: (Eq a, Num a) => [[a]] -> [[a]]
+  signsAll = concatMap signs
+    where
+    signs :: (Eq a, Num a) => [a] -> [[a]]
+    signs = mapM (\x -> nub [x,-x])
+
+vs120omnitrunc :: [[Double]]
+vs120omnitrunc = concatMap vertices [
+    ([1, 1, 1+6*phi, 7+10*phi], True),
+    ([1, 1, 3+8*phi, 7+8*phi], True),
+    ([1, 1, 1+4*phi, 5+12*phi], True),
+    ([1, 3, phi6, phi6], True),
+    ([2, 2, 4*phi3, 6+8*phi], True),
+    ([2*phi2, 4+2*phi, 4*phi3, 4*phi3], True),
+    ([3+2*phi, 3+2*phi, 3+8*phi, phi6], True),
+    ([1+4*phi, 3+4*phi, 3+8*phi, 3+8*phi], True),
+    ([2*phi3, 2*phi3, 2+8*phi, 4*phi3], True),
+    ([1, 5*phi2, 4+7*phi, 6*phi2], False),
+    ([1, 2*phi4, 5+7*phi, 6+5*phi], False),
+    ([1, 2, 1+5*phi, 6+11*phi], False),
+    ([1, phi2, 6+9*phi, 2+8*phi], False),
+    ([1, phi2, 8+9*phi, 2+6*phi], False),
+    ([1, 2*phi, 7+9*phi, 2+7*phi], False),
+    ([1, phi3, 5+12*phi, 3+2*phi], False),
+    ([1, 3+phi, 4+9*phi, 4*phi3], False),
+    ([1, 1+3*phi, 8+9*phi, 4*phi2], False),
+    ([1, 1+3*phi, 6+11*phi, 4+2*phi], False),
+    ([1, 4*phi, 7+9*phi, 4+5*phi], False),
+    ([1, 3*phi2, 4+9*phi, 6*phi2], False),
+    ([1, 2*phi3, 5+9*phi, 6+5*phi], False),
+    ([2, phi2, 5+12*phi, phi4], False),
+    ([2, 2+phi, 5+9*phi, 3+8*phi], False),
+    ([2, phi3, 8+9*phi, phi5], False),
+    ([2, 3*phi, 7+9*phi, 3*phi3], False),
+    ([2, 1+3*phi, 7+10*phi, 4+3*phi], False),
+    ([2, 3+2*phi, 4+9*phi, 5+7*phi], False),
+    ([2, 1+4*phi, 6+9*phi, 5*phi2], False),
+    ([phi2, 4+5*phi, 3+8*phi, 6*phi2], False),
+    ([phi2, 3*phi3, 4*phi3, 6+5*phi], False),
+    ([phi2, 3, 2*phi3, 6+11*phi], False),
+    ([phi2, 3*phi, 5+12*phi, 2*phi2], False),
+    ([phi2, 3+2*phi, 4*phi, 6+11*phi], False),
+    ([phi2, 4+3*phi, phi6, 6*phi2], False),
+    ([phi2, 3+4*phi, 6+8*phi, 6+5*phi], False),
+    ([3, phi3, 7+10*phi, 3+4*phi], False),
+    ([3, 2*phi2, 5+9*phi, 4+7*phi], False),
+    ([3, 1+3*phi, 6+9*phi, 2*phi4], False),
+    ([2*phi, 4*phi2, 4*phi3, 6*phi2], False),
+    ([2*phi, phi5, phi6, 6+5*phi], False),
+    ([2*phi, 2+phi, 1+3*phi, 5+12*phi], False),
+    ([2*phi, 3+phi, 1+4*phi, 6+11*phi], False),
+    ([2+phi, 4*phi, 7+10*phi, 3*phi2], False),
+    ([2+phi, 4+2*phi, phi6, 5+7*phi], False),
+    ([2+phi, 2*phi3, 7+8*phi, 5*phi2], False),
+    ([phi3, 4+5*phi, 2+8*phi, 5+7*phi], False),
+    ([phi3, 5*phi2, 2+7*phi, 4*phi3], False),
+    ([3+phi, 3*phi, 7+10*phi, 2*phi3], False),
+    ([3+phi, 3+2*phi, 6+8*phi, 4+7*phi], False),
+    ([3+phi, phi4, 7+8*phi, 2*phi4], False),
+    ([3*phi, 4*phi2, 3+8*phi, 5+7*phi], False),
+    ([3*phi, 2+6*phi, phi6, 5*phi2], False),
+    ([2*phi2, 1+4*phi, 8+9*phi, 3*phi2], False),
+    ([2*phi2, 1+5*phi, 7+8*phi, 4+5*phi], False),
+    ([1+3*phi, 1+6*phi, 6+8*phi, 4+5*phi], False),
+    ([1+3*phi, 3*phi3, 2+8*phi, 4+7*phi], False),
+    ([1+3*phi, 2+7*phi, 3+8*phi, 2*phi4], False),
+    ([1+3*phi, 3+2*phi, 2*phi3, 8+9*phi], False),
+    ([1+3*phi, 4+3*phi, 3+8*phi, 4*phi3], False),
+    ([3+2*phi, 1+4*phi, 7+8*phi, 3*phi3], False),
+    ([3+2*phi, 2*phi3, 7+9*phi, 4+3*phi], False),
+    ([3+2*phi, 1+5*phi, 6+9*phi, 4*phi2], False),
+    ([4*phi, phi5, 3+8*phi, 4+7*phi], False),
+    ([4*phi, 2+6*phi, 4*phi3, 2*phi4], False),
+    ([phi4, 4*phi2, 1+6*phi, 5+9*phi], False),
+    ([phi4, 4+2*phi, 3+4*phi, 7+9*phi], False),
+    ([phi4, 3*phi2, 2+8*phi, phi6], False),
+    ([4+2*phi, 1+4*phi, 6+9*phi, phi5], False),
+    ([1+4*phi, 1+6*phi, phi6, 3*phi3], False),
+    ([1+4*phi, 3*phi2, 2+7*phi, 6+8*phi], False),
+    ([1+4*phi, 4+3*phi, 2+6*phi, 5+9*phi], False),
+    ([2*phi3, 1+6*phi, 4+9*phi, phi5], False),
+    ([2*phi3, 1+5*phi, phi6, 2+7*phi], False),
+    ([1+5*phi, 3+4*phi, 2+6*phi, 4+9*phi], False)
+    ]
+    where
+        phi = (1+sqrt 5) / 2
+        phi2 = phi*phi
+        phi3 = phi2*phi
+        phi4 = phi3*phi
+        phi5 = phi4*phi
+        phi6 = phi5*phi
+
+
+
+
diff --git a/src/ConvexHull/R.hs b/src/ConvexHull/R.hs
new file mode 100644
--- /dev/null
+++ b/src/ConvexHull/R.hs
@@ -0,0 +1,55 @@
+module ConvexHull.R
+  where
+import           Control.Monad              (when)
+import           ConvexHull
+import qualified Data.HashMap.Strict.InsOrd as H
+import qualified Data.IntMap.Strict         as IM
+import           Data.List.Index            (iconcatMap)
+import           Data.Maybe
+import           Data.Tuple.Extra           (snd3)
+-- import           System.IO                  (writeFile)
+
+convexHull3DrglCode :: [[Double]] -> Bool -> Maybe FilePath -> IO String
+convexHull3DrglCode points rainbow file = do
+  -- get edges --
+  hull1 <- convexHull points False False Nothing
+  let edges = H.elems (_hedges hull1)
+  -- get triangles --
+  hull2 <- convexHull points True False Nothing
+  let grpFaces = groupedFacets hull2
+  let triangles = map (map IM.elems . snd3) grpFaces
+  -- code for edges --
+  let code1 = concatMap rglSegment edges
+  -- color palette --
+  let code_colors = if rainbow
+                      then "colors <- rainbow(" ++ show (length grpFaces + 1) ++
+                           ", alpha=0.5)\n"
+                      else "colors <- rep(\"blue\", " ++
+                           show (length grpFaces + 1) ++ ")\n"
+  -- code for triangles --
+  let code2 = iconcatMap (\i x -> concatMap (rglTriangle i) x) triangles
+  -- whole code --
+  let code = "library(rgl)\n" ++ code_colors ++ code1 ++ code2
+  -- write file --
+  when (isJust file) $
+    writeFile (fromJust file) code
+  -- --
+  return code
+  -- auxiliary functions --
+  where
+    asTriplet p = (p!!0, p!!1, p!!2)
+    rglSegment :: ([Double], [Double]) -> String
+    rglSegment (p1', p2') =
+      let p1 = asTriplet p1' in
+      let p2 = asTriplet p2' in
+      "segments3d(rbind(c" ++ show p1 ++ ", c" ++ show p2 ++
+        "), color=\"black\")\n"
+    rglTriangle :: Int -> [[Double]] -> String
+    rglTriangle i threepoints =
+      "triangles3d(rbind(c" ++ show p1 ++ ", c" ++ show p2 ++
+      ", c" ++ show p3 ++ "), color=colors[" ++ show (i+1) ++ "]" ++
+      ", alpha=0.75)\n"
+      where
+        p1 = asTriplet $ threepoints!!0
+        p2 = asTriplet $ threepoints!!1
+        p3 = asTriplet $ threepoints!!2
diff --git a/src/ConvexHull/SnubDodecahedron/SnubDodecahedron.hs b/src/ConvexHull/SnubDodecahedron/SnubDodecahedron.hs
new file mode 100644
--- /dev/null
+++ b/src/ConvexHull/SnubDodecahedron/SnubDodecahedron.hs
@@ -0,0 +1,31 @@
+module ConvexHull.SnubDodecahedron.SnubDodecahedron where
+import           Data.List                  hiding (permutations)
+import           Math.Combinat.Permutations
+
+signsAll2 :: (Eq a, Num a) => [[a]] -> [[a]]
+signsAll2 = concatMap signs
+  where
+    signs :: (Eq a, Num a) => [a] -> [[a]]
+    signs [x,y,z] = nub [[x,y,-z], [x,-y,z], [-x,y,z], [-x,-y,-z]]
+
+
+vertices :: ([Double], Bool) -> [[Double]]
+vertices (coords, allperms) =
+  -- map (map (/ sqrt 128.9619)) $
+  signsAll2 $
+  nub $ zipWith permuteList perms (replicate 12 coords)
+  where perms = filter (if allperms then const True else isEvenPermutation) (permutations 3)
+
+snubDodecahedron :: [[Double]]
+snubDodecahedron = concatMap vertices
+  [ ([2*alpha, 2, 2*beta], False)
+   , ([alpha + beta/phi + phi, -alpha*phi + beta + 1/phi, alpha/phi + beta*phi-1], False)
+   , ([alpha + beta/phi - phi, alpha*phi - beta + 1/phi, alpha/phi + beta*phi+1], False)
+   , ([-alpha/phi + beta*phi+1, -alpha + beta/phi - phi, alpha*phi + beta - 1/phi], False)
+   , ([-alpha/phi + beta*phi-1, alpha - beta/phi - phi, alpha*phi + beta + 1/phi], False) ]
+  where
+    alpha = xi - 1/xi
+    beta = xi*phi + phi*phi + phi/xi
+    xi = 1.7155615
+    phi = (1 + sqrt 5)/2
+
diff --git a/src/ConvexHull/Truncated120Cell3.hs b/src/ConvexHull/Truncated120Cell3.hs
new file mode 100644
--- /dev/null
+++ b/src/ConvexHull/Truncated120Cell3.hs
@@ -0,0 +1,43 @@
+module ConvexHull.Truncated120Cell3
+  where
+import           Data.List
+import           Math.Combinat.Permutations as P
+-- http://eusebeia.dyndns.org/4d/trunc120cell
+-- http://mathworld.wolfram.com/120-Cell.html
+
+signs :: (Eq a, Num a) => [a] -> [[a]]
+signs = mapM (\x -> nub [x,-x])
+
+signsAll :: (Eq a, Num a) => [[a]] -> [[a]]
+signsAll = concatMap signs
+
+vertices :: ([Double], Bool) -> [[Double]]
+vertices (coords, allperms) =
+  map (map (/ sqrt 270.1640786)) $ signsAll $
+  nub $ zipWith permuteList perms (replicate 24 coords)
+  where perms = filter (if allperms then const True else isEvenPermutation) (P.permutations 4)
+
+vs120trunc = concatMap vertices [([1, 3+4*phi, 3+4*phi, 3+4*phi], True)
+         , ([phi3, phi3, phi3, 5+6*phi], True)
+         , ([2*phi2, 2*phi2, 2*phi2, 2*phi4], True)
+         , ([0, 1, 4+5*phi, phi5] ,False)
+         , ([0, 1, 4+7*phi, 1+3*phi], False)
+         , ([0, phi2, 3*phi3, 2+5*phi], False)
+         , ([0, phi2, 5+6*phi, phi4], False)
+         , ([0, 2*phi, 2*phi4, 2*phi3], False)
+         , ([1, phi2, 4+7*phi, 2*phi2], False)
+         , ([1, phi3, 3*phi2, 3+4*phi], False)
+         , ([phi2, 2*phi, 4+7*phi, phi3], False)
+         , ([phi2, 2*phi2, 4+5*phi, 3+4*phi], False)
+         , ([phi2, 2*phi3, 2+5*phi, 3+4*phi], False)
+         , ([2*phi, phi4, phi5, 3+4*phi], False)
+         , ([phi3, 2*phi2, phi5, 2+5*phi], False)
+         , ([phi3, 1+3*phi, 4+5*phi, 2*phi3], False)
+         , ([2*phi2, 1+3*phi, 3*phi3, phi4], False)
+         ]
+  where
+     phi = (1+sqrt 5) / 2
+     phi3 = phi*phi*phi
+     phi2 = phi*phi
+     phi4 = phi*phi*phi*phi
+     phi5 = phi*phi*phi*phi*phi
diff --git a/src/ConvexHull/Types.hs b/src/ConvexHull/Types.hs
new file mode 100644
--- /dev/null
+++ b/src/ConvexHull/Types.hs
@@ -0,0 +1,69 @@
+module ConvexHull.Types
+  where
+import           Data.IntMap.Strict (IntMap)
+import qualified Data.IntMap.Strict as IM
+import           Data.IntSet        (IntSet)
+import           Qhull.Types
+
+data Vertex = Vertex {
+    _point         :: [Double]
+  , _neighfacets   :: IntSet
+  , _neighvertices :: IndexSet
+  , _neighridges   :: IntSet
+} deriving Show
+
+data Ridge = Ridge {
+    _rvertices :: IndexMap [Double]
+  , _ridgeOf   :: IntSet
+  , _redges    :: EdgeMap
+} deriving Show
+
+instance HasVertices Ridge where
+  _vertices = _rvertices
+
+instance HasEdges Ridge where
+  _edges = _redges
+
+data Facet = Facet {
+    _fvertices :: IndexMap [Double]
+  , _fridges   :: IntSet
+  , _centroid  :: [Double]
+  , _normal'   :: [Double]
+  , _offset'   :: Double
+  , _area      :: Double
+  , _neighbors :: IntSet
+  , _family'   :: Family
+  , _fedges    :: EdgeMap
+} deriving Show
+
+instance HasCenter Facet where
+  _center = _centroid
+
+instance HasEdges Facet where
+  _edges = _fedges
+
+instance HasVertices Facet where
+  _vertices = _fvertices
+
+instance HasNormal Facet where
+  _normal = _normal'
+  _offset = _offset'
+
+instance HasVolume Facet where
+  _volume = _area
+
+instance HasFamily Facet where
+  _family = _family'
+
+data ConvexHull = ConvexHull {
+    _hvertices :: IndexMap Vertex
+  , _hfacets   :: IntMap Facet
+  , _hridges   :: IntMap Ridge
+  , _hedges    :: EdgeMap
+} deriving Show
+
+instance HasEdges ConvexHull where
+  _edges = _hedges
+
+instance HasVertices ConvexHull where
+  _vertices hull = IM.map _point (_hvertices hull)
diff --git a/src/Delaunay.hs b/src/Delaunay.hs
new file mode 100644
--- /dev/null
+++ b/src/Delaunay.hs
@@ -0,0 +1,7 @@
+module Delaunay
+  (module X)
+  where
+import           Delaunay.Delaunay as X
+import           Delaunay.Types    as X
+import           Qhull.Shared      as X
+import           Qhull.Types       as X
diff --git a/src/Delaunay/Adjacency.hs b/src/Delaunay/Adjacency.hs
new file mode 100644
--- /dev/null
+++ b/src/Delaunay/Adjacency.hs
@@ -0,0 +1,25 @@
+module Delaunay.Adjacency
+  where
+import qualified Data.IntMap.Strict as IM
+import qualified Data.IntSet        as IS
+import           Delaunay.Types
+
+
+vertices :: [[Double]]
+vertices = [
+            [ -5, -5,  16 ]  -- 0
+          , [ -5,  8,   3 ]  -- 1
+          , [  4, -1,   3 ]  -- 2
+          , [  4, -5,   7 ]  -- 3
+          , [  4, -1, -10 ]  -- 4
+          , [  4, -5, -10 ]  -- 5
+          , [ -5,  8, -10 ]  -- 6
+          , [ -5, -5, -10 ]  -- 7
+                           ]
+
+
+adjMatrix :: Tesselation -> [[Int]]
+adjMatrix tess = map (adjacency tess) (IM.keys ( _sites tess ))
+  where
+  adjacency tess i =
+    map (fromEnum . ((i `IS.member`) . _neighsitesIds) . snd) $ IM.toList $ _sites tess
diff --git a/src/Delaunay/CDelaunay.hs b/src/Delaunay/CDelaunay.hs
new file mode 100644
--- /dev/null
+++ b/src/Delaunay/CDelaunay.hs
@@ -0,0 +1,390 @@
+{-# LINE 1 "delaunay.hsc" #-}
+{-# LANGUAGE ForeignFunctionInterface #-}
+module Delaunay.CDelaunay
+  ( cTesselationToTesselation
+  , c_tesselation )
+  where
+import           Control.Monad              ((<$!>))
+import qualified Data.HashMap.Strict.InsOrd as H
+import           Data.IntMap.Strict         (fromAscList, (!))
+import qualified Data.IntSet                as IS
+import           Data.List
+import           Data.Maybe
+import           Data.Tuple.Extra           (both, fst3, snd3, thd3, (&&&))
+import           Delaunay.Types
+import           Foreign
+import           Foreign.C.Types
+import           Qhull.Types
+
+data CSite = CSite {
+    __id             :: CUInt
+  , __neighsites     :: Ptr CUInt
+  , __nneighsites    :: CUInt
+  , __neighridgesids :: Ptr CUInt
+  , __nneighridges   :: CUInt
+  , __neightiles     :: Ptr CUInt
+  , __nneightiles    :: CUInt
+}
+
+instance Storable CSite where
+    sizeOf    __ = (56)
+{-# LINE 29 "delaunay.hsc" #-}
+    alignment __ = 8
+{-# LINE 30 "delaunay.hsc" #-}
+    peek ptr = do
+      id'              <- (\hsc_ptr -> peekByteOff hsc_ptr 0) ptr
+{-# LINE 32 "delaunay.hsc" #-}
+      neighsites'      <- (\hsc_ptr -> peekByteOff hsc_ptr 8) ptr
+{-# LINE 33 "delaunay.hsc" #-}
+      nneighsites'     <- (\hsc_ptr -> peekByteOff hsc_ptr 16) ptr
+{-# LINE 34 "delaunay.hsc" #-}
+      neighridgesids'  <- (\hsc_ptr -> peekByteOff hsc_ptr 24) ptr
+{-# LINE 35 "delaunay.hsc" #-}
+      nneighridges'    <- (\hsc_ptr -> peekByteOff hsc_ptr 32) ptr
+{-# LINE 36 "delaunay.hsc" #-}
+      neightiles'      <- (\hsc_ptr -> peekByteOff hsc_ptr 40) ptr
+{-# LINE 37 "delaunay.hsc" #-}
+      nneightiles'     <- (\hsc_ptr -> peekByteOff hsc_ptr 48) ptr
+{-# LINE 38 "delaunay.hsc" #-}
+      return CSite { __id             = id'
+                   , __neighsites     = neighsites'
+                   , __nneighsites    = nneighsites'
+                   , __neighridgesids = neighridgesids'
+                   , __nneighridges   = nneighridges'
+                   , __neightiles     = neightiles'
+                   , __nneightiles    = nneightiles'
+                  }
+    poke ptr (CSite r1 r2 r3 r4 r5 r6 r7)
+      = do
+          (\hsc_ptr -> pokeByteOff hsc_ptr 0) ptr r1
+{-# LINE 49 "delaunay.hsc" #-}
+          (\hsc_ptr -> pokeByteOff hsc_ptr 8) ptr r2
+{-# LINE 50 "delaunay.hsc" #-}
+          (\hsc_ptr -> pokeByteOff hsc_ptr 16) ptr r3
+{-# LINE 51 "delaunay.hsc" #-}
+          (\hsc_ptr -> pokeByteOff hsc_ptr 24) ptr r4
+{-# LINE 52 "delaunay.hsc" #-}
+          (\hsc_ptr -> pokeByteOff hsc_ptr 32) ptr r5
+{-# LINE 53 "delaunay.hsc" #-}
+          (\hsc_ptr -> pokeByteOff hsc_ptr 40) ptr r6
+{-# LINE 54 "delaunay.hsc" #-}
+          (\hsc_ptr -> pokeByteOff hsc_ptr 48) ptr r7
+{-# LINE 55 "delaunay.hsc" #-}
+
+cSiteToSite :: [[Double]] -> CSite -> IO (Int, Site, [(Int,Int)])
+cSiteToSite sites csite = do
+  let id'          = fromIntegral $ __id csite
+      nneighsites  = fromIntegral $ __nneighsites csite
+      nneighridges = fromIntegral $ __nneighridges csite
+      nneightiles  = fromIntegral $ __nneightiles csite
+      point        = sites !! id'
+  neighsites <- (<$!>) (map fromIntegral)
+                       (peekArray nneighsites (__neighsites csite))
+  neighridges <- (<$!>) (map fromIntegral)
+                        (peekArray nneighridges (__neighridgesids csite))
+  neightiles <- (<$!>) (map fromIntegral)
+                       (peekArray nneightiles (__neightiles csite))
+  return ( id'
+         , Site {
+                  _point          = point
+                , _neighsitesIds  = IS.fromAscList neighsites
+                , _neighfacetsIds = IS.fromAscList neighridges
+                , _neightilesIds  = IS.fromAscList neightiles
+                }
+         , map (\j -> (id', j)) (filterAscList id' neighsites) )
+  where
+    filterAscList :: Int -> [Int] -> [Int]
+    filterAscList n list =
+      let i = findIndex (>n) list in
+      if isJust i
+        then drop (fromJust i) list
+        else []
+
+data CSimplex = CSimplex {
+    __sitesids :: Ptr CUInt
+  , __center   :: Ptr CDouble
+  , __radius   :: CDouble
+  , __volume   :: CDouble
+}
+
+instance Storable CSimplex where
+    sizeOf    __ = (32)
+{-# LINE 85 "delaunay.hsc" #-}
+    alignment __ = 8
+{-# LINE 86 "delaunay.hsc" #-}
+    peek ptr = do
+      sitesids'    <- (\hsc_ptr -> peekByteOff hsc_ptr 0) ptr
+{-# LINE 88 "delaunay.hsc" #-}
+      center'      <- (\hsc_ptr -> peekByteOff hsc_ptr 8) ptr
+{-# LINE 89 "delaunay.hsc" #-}
+      radius'      <- (\hsc_ptr -> peekByteOff hsc_ptr 16) ptr
+{-# LINE 90 "delaunay.hsc" #-}
+      volume'      <- (\hsc_ptr -> peekByteOff hsc_ptr 24) ptr
+{-# LINE 91 "delaunay.hsc" #-}
+      return CSimplex { __sitesids    = sitesids'
+                      , __center      = center'
+                      , __radius      = radius'
+                      , __volume      = volume'
+                    }
+    poke ptr (CSimplex r1 r2 r3 r4)
+      = do
+          (\hsc_ptr -> pokeByteOff hsc_ptr 0) ptr r1
+{-# LINE 99 "delaunay.hsc" #-}
+          (\hsc_ptr -> pokeByteOff hsc_ptr 8) ptr r2
+{-# LINE 100 "delaunay.hsc" #-}
+          (\hsc_ptr -> pokeByteOff hsc_ptr 16) ptr r3
+{-# LINE 101 "delaunay.hsc" #-}
+          (\hsc_ptr -> pokeByteOff hsc_ptr 24) ptr r4
+{-# LINE 102 "delaunay.hsc" #-}
+
+cSimplexToSimplex :: [[Double]] -> Int -> CSimplex -> IO Simplex
+cSimplexToSimplex sites simplexdim csimplex = do
+  let radius      = cdbl2dbl $ __radius csimplex
+      volume      = cdbl2dbl $ __volume csimplex
+      dim         = length (head sites)
+  sitesids <- (<$!>) (map fromIntegral)
+                     (peekArray simplexdim (__sitesids csimplex))
+  -- putStrLn "cSimplexToSimplex - peek sitesids"
+  let points = fromAscList
+               (zip sitesids (map ((!!) sites) sitesids))
+  center <- (<$!>) (map cdbl2dbl) (peekArray dim (__center csimplex))
+  -- putStrLn "cSimplexToSimplex - peek center"
+  return Simplex { _vertices'       = points
+                 , _circumcenter = center
+                 , _circumradius = radius
+                 , _volume'       = volume }
+  where
+    cdbl2dbl :: CDouble -> Double
+    cdbl2dbl x = if isNaN x then 0/0 else realToFrac x
+
+data CSubTile = CSubTile {
+    __id'        :: CUInt
+  , __subsimplex :: CSimplex
+  , __ridgeOf1   :: CUInt
+  , __ridgeOf2   :: CInt
+  , __normal     :: Ptr CDouble
+  , __offset     :: CDouble
+}
+
+instance Storable CSubTile where
+    sizeOf    __ = (72)
+{-# LINE 132 "delaunay.hsc" #-}
+    alignment __ = 8
+{-# LINE 133 "delaunay.hsc" #-}
+    peek ptr = do
+      id'       <- (\hsc_ptr -> peekByteOff hsc_ptr 0) ptr
+{-# LINE 135 "delaunay.hsc" #-}
+      simplex'  <- (\hsc_ptr -> peekByteOff hsc_ptr 8) ptr
+{-# LINE 136 "delaunay.hsc" #-}
+      ridgeOf1' <- (\hsc_ptr -> peekByteOff hsc_ptr 40) ptr
+{-# LINE 137 "delaunay.hsc" #-}
+      ridgeOf2' <- (\hsc_ptr -> peekByteOff hsc_ptr 44) ptr
+{-# LINE 138 "delaunay.hsc" #-}
+      normal'   <- (\hsc_ptr -> peekByteOff hsc_ptr 48) ptr
+{-# LINE 139 "delaunay.hsc" #-}
+      offset'   <- (\hsc_ptr -> peekByteOff hsc_ptr 56) ptr
+{-# LINE 140 "delaunay.hsc" #-}
+      return CSubTile { __id'        = id'
+                      , __subsimplex = simplex'
+                      , __ridgeOf1   = ridgeOf1'
+                      , __ridgeOf2   = ridgeOf2'
+                      , __normal     = normal'
+                      , __offset     = offset' }
+    poke ptr (CSubTile r1 r2 r3 r4 r5 r6)
+      = do
+          (\hsc_ptr -> pokeByteOff hsc_ptr 0) ptr r1
+{-# LINE 149 "delaunay.hsc" #-}
+          (\hsc_ptr -> pokeByteOff hsc_ptr 8) ptr r2
+{-# LINE 150 "delaunay.hsc" #-}
+          (\hsc_ptr -> pokeByteOff hsc_ptr 40) ptr r3
+{-# LINE 151 "delaunay.hsc" #-}
+          (\hsc_ptr -> pokeByteOff hsc_ptr 44) ptr r4
+{-# LINE 152 "delaunay.hsc" #-}
+          (\hsc_ptr -> pokeByteOff hsc_ptr 48) ptr r5
+{-# LINE 153 "delaunay.hsc" #-}
+          (\hsc_ptr -> pokeByteOff hsc_ptr 56) ptr r6
+{-# LINE 154 "delaunay.hsc" #-}
+
+cSubTiletoTileFacet :: [[Double]] -> CSubTile -> IO (Int, TileFacet)
+cSubTiletoTileFacet points csubtile = do
+  let dim        = length (head points)
+      ridgeOf1   = fromIntegral $ __ridgeOf1 csubtile
+      ridgeOf2   = fromIntegral $ __ridgeOf2 csubtile
+      ridgeOf    = if ridgeOf2 == -1 then [ridgeOf1] else [ridgeOf1, ridgeOf2]
+      id'        = fromIntegral $ __id' csubtile
+      subsimplex = __subsimplex csubtile
+      offset     = realToFrac $ __offset csubtile
+  simplex <- cSimplexToSimplex points dim subsimplex
+  normal <- (<$!>) (map realToFrac) (peekArray dim (__normal csubtile))
+  -- putStrLn "cSubTiletoTileFacet - peek normal"
+  return (id', TileFacet { _subsimplex = simplex
+                         , _facetOf    = IS.fromAscList ridgeOf
+                         , _normal'     = normal
+                         , _offset'     = offset })
+
+data CTile = CTile {
+    __id''        :: CUInt
+  , __simplex     :: CSimplex
+  , __neighbors   :: Ptr CUInt
+  , __nneighbors  :: CUInt
+  , __ridgesids   :: Ptr CUInt
+  , __nridges     :: CUInt
+  , __family      :: CInt
+  , __orientation :: CInt
+}
+
+instance Storable CTile where
+    sizeOf    __ = (80)
+{-# LINE 184 "delaunay.hsc" #-}
+    alignment __ = 8
+{-# LINE 185 "delaunay.hsc" #-}
+    peek ptr = do
+      id'         <- (\hsc_ptr -> peekByteOff hsc_ptr 0) ptr
+{-# LINE 187 "delaunay.hsc" #-}
+      simplex'    <- (\hsc_ptr -> peekByteOff hsc_ptr 8) ptr
+{-# LINE 188 "delaunay.hsc" #-}
+      neighbors'  <- (\hsc_ptr -> peekByteOff hsc_ptr 40) ptr
+{-# LINE 189 "delaunay.hsc" #-}
+      nneighbors' <- (\hsc_ptr -> peekByteOff hsc_ptr 48) ptr
+{-# LINE 190 "delaunay.hsc" #-}
+      ridgesids'  <- (\hsc_ptr -> peekByteOff hsc_ptr 56) ptr
+{-# LINE 191 "delaunay.hsc" #-}
+      nridges'    <- (\hsc_ptr -> peekByteOff hsc_ptr 64) ptr
+{-# LINE 192 "delaunay.hsc" #-}
+      family'     <- (\hsc_ptr -> peekByteOff hsc_ptr 68) ptr
+{-# LINE 193 "delaunay.hsc" #-}
+      orient      <- (\hsc_ptr -> peekByteOff hsc_ptr 72) ptr
+{-# LINE 194 "delaunay.hsc" #-}
+      return CTile { __id''        = id'
+                   , __simplex     = simplex'
+                   , __neighbors   = neighbors'
+                   , __nneighbors  = nneighbors'
+                   , __ridgesids   = ridgesids'
+                   , __nridges     = nridges'
+                   , __family      = family'
+                   , __orientation = orient
+                  }
+    poke ptr (CTile r1 r2 r3 r4 r5 r6 r7 r8)
+      = do
+          (\hsc_ptr -> pokeByteOff hsc_ptr 0) ptr r1
+{-# LINE 206 "delaunay.hsc" #-}
+          (\hsc_ptr -> pokeByteOff hsc_ptr 8) ptr r2
+{-# LINE 207 "delaunay.hsc" #-}
+          (\hsc_ptr -> pokeByteOff hsc_ptr 40) ptr r3
+{-# LINE 208 "delaunay.hsc" #-}
+          (\hsc_ptr -> pokeByteOff hsc_ptr 48) ptr r4
+{-# LINE 209 "delaunay.hsc" #-}
+          (\hsc_ptr -> pokeByteOff hsc_ptr 56) ptr r5
+{-# LINE 210 "delaunay.hsc" #-}
+          (\hsc_ptr -> pokeByteOff hsc_ptr 64) ptr r6
+{-# LINE 211 "delaunay.hsc" #-}
+          (\hsc_ptr -> pokeByteOff hsc_ptr 68) ptr r7
+{-# LINE 212 "delaunay.hsc" #-}
+          (\hsc_ptr -> pokeByteOff hsc_ptr 72) ptr r8
+{-# LINE 213 "delaunay.hsc" #-}
+
+cTileToTile :: [[Double]] -> CTile -> IO (Int, Tile)
+cTileToTile points ctile = do
+  let id'        = fromIntegral $ __id'' ctile
+      csimplex   = __simplex ctile
+      nneighbors = fromIntegral $ __nneighbors ctile
+      nridges    = fromIntegral $ __nridges ctile
+      family     = __family ctile
+      orient     = __orientation ctile
+      dim        = length (head points)
+  -- putStrLn $ "tile id: " ++ show id'
+  simplex <- cSimplexToSimplex points (dim+1) csimplex
+  neighbors <- (<$!>) (map fromIntegral)
+                      (peekArray nneighbors (__neighbors ctile))
+  -- putStrLn "cTileToTile - peek neighbors"
+  ridgesids <- (<$!>) (map fromIntegral)
+                      (peekArray nridges (__ridgesids ctile))
+  -- putStrLn "cTileToTile - peek ridges"
+  return (id', Tile {  _simplex      = simplex
+                     , _neighborsIds = IS.fromAscList neighbors
+                     , _facetsIds    = IS.fromAscList ridgesids
+                     , _family'       = if family == -1
+                                        then None
+                                        else Family (fromIntegral family)
+                     , _toporiented  = orient == 1 })
+
+data CTesselation = CTesselation {
+    __sites     :: Ptr CSite
+  , __tiles     :: Ptr CTile
+  , __ntiles    :: CUInt
+  , __subtiles  :: Ptr CSubTile
+  , __nsubtiles :: CUInt
+}
+
+instance Storable CTesselation where
+    sizeOf    __ = (40)
+{-# LINE 246 "delaunay.hsc" #-}
+    alignment __ = 8
+{-# LINE 247 "delaunay.hsc" #-}
+    peek ptr = do
+      sites'     <- (\hsc_ptr -> peekByteOff hsc_ptr 0) ptr
+{-# LINE 249 "delaunay.hsc" #-}
+      tiles'     <- (\hsc_ptr -> peekByteOff hsc_ptr 8) ptr
+{-# LINE 250 "delaunay.hsc" #-}
+      ntiles'    <- (\hsc_ptr -> peekByteOff hsc_ptr 16) ptr
+{-# LINE 251 "delaunay.hsc" #-}
+      subtiles'  <- (\hsc_ptr -> peekByteOff hsc_ptr 24) ptr
+{-# LINE 252 "delaunay.hsc" #-}
+      nsubtiles' <- (\hsc_ptr -> peekByteOff hsc_ptr 32) ptr
+{-# LINE 253 "delaunay.hsc" #-}
+      return CTesselation {
+                     __sites     = sites'
+                   , __tiles     = tiles'
+                   , __ntiles    = ntiles'
+                   , __subtiles  = subtiles'
+                   , __nsubtiles = nsubtiles'
+                  }
+    poke ptr (CTesselation r1 r2 r3 r4 r5)
+      = do
+          (\hsc_ptr -> pokeByteOff hsc_ptr 0) ptr r1
+{-# LINE 263 "delaunay.hsc" #-}
+          (\hsc_ptr -> pokeByteOff hsc_ptr 8) ptr r2
+{-# LINE 264 "delaunay.hsc" #-}
+          (\hsc_ptr -> pokeByteOff hsc_ptr 16) ptr r3
+{-# LINE 265 "delaunay.hsc" #-}
+          (\hsc_ptr -> pokeByteOff hsc_ptr 24) ptr r4
+{-# LINE 266 "delaunay.hsc" #-}
+          (\hsc_ptr -> pokeByteOff hsc_ptr 32) ptr r5
+{-# LINE 267 "delaunay.hsc" #-}
+
+foreign import ccall unsafe "tesselation" c_tesselation
+  :: Ptr CDouble -- sites
+  -> CUInt       -- dim
+  -> CUInt       -- nsites
+  -> CUInt       -- 0/1, point at infinity
+  -> CUInt       -- 0/1, include degenerate
+  -> CDouble     -- volume threshold
+  -> Ptr CUInt   -- exitcode
+  -> IO (Ptr CTesselation)
+
+cTesselationToTesselation :: [[Double]] -> CTesselation -> IO Tesselation
+cTesselationToTesselation vertices ctess = do
+  let ntiles    = fromIntegral $ __ntiles ctess
+      nsubtiles = fromIntegral $ __nsubtiles ctess
+      nsites    = length vertices
+  sites''    <- peekArray nsites (__sites ctess)
+  -- putStrLn "peek sites"
+  tiles''    <- peekArray ntiles (__tiles ctess)
+  -- putStrLn "peek tiles"
+  subtiles'' <- peekArray nsubtiles (__subtiles ctess)
+  -- putStrLn "peek ridges"
+  sites'     <- mapM (cSiteToSite vertices) sites''
+  let sites = fromAscList (map (fst3 &&& snd3) sites')
+      edgesIndices = concatMap thd3 sites'
+      edges = map (toPair &&& both (_point . ((!) sites))) edgesIndices
+  tiles'     <- mapM (cTileToTile vertices) tiles''
+  -- putStrLn "mapped cTileToTile"
+  subtiles'  <- mapM (cSubTiletoTileFacet vertices) subtiles''
+  -- putStrLn "mapped cSubTiletoTileFacet"
+  return Tesselation
+         { _sites      = sites
+         , _tiles      = fromAscList tiles'
+         , _tilefacets = fromAscList subtiles'
+         , _edges'     = H.fromList edges }
+  where
+    toPair (i,j) = Pair i j
diff --git a/src/Delaunay/Delaunay.hs b/src/Delaunay/Delaunay.hs
new file mode 100644
--- /dev/null
+++ b/src/Delaunay/Delaunay.hs
@@ -0,0 +1,94 @@
+module Delaunay.Delaunay
+  where
+import           Control.Monad         (unless, when)
+import           Data.IntMap.Strict    (IntMap)
+import qualified Data.IntMap.Strict    as IM
+import qualified Data.IntSet           as IS
+import           Data.List.Unique      (allUnique)
+import           Data.Maybe
+import           Delaunay.CDelaunay
+import           Delaunay.Types
+import           Foreign.C.Types
+import           Foreign.Marshal.Alloc (free, mallocBytes)
+import           Foreign.Marshal.Array (pokeArray)
+import           Foreign.Storable      (peek, sizeOf)
+import           Qhull.Types
+
+delaunay :: [[Double]]   -- sites (vertices)
+         -> Bool         -- add a point at infinity
+         -> Bool         -- include degenerate tiles
+         -> Maybe Double -- volume threshold
+         -> IO Tesselation
+delaunay sites atinfinity degenerate vthreshold = do
+  let n     = length sites
+      dim   = length (head sites)
+  when (dim < 2) $
+    error "dimension must be at least 2"
+  when (n <= dim+1) $
+    error "insufficient number of points"
+  unless (all (== dim) (map length (tail sites))) $
+    error "the points must have the same dimension"
+  unless (allUnique sites) $
+    error "some points are duplicated"
+  let vthreshold' = fromMaybe 0 vthreshold 
+  sitesPtr <- mallocBytes (n * dim * sizeOf (undefined :: CDouble))
+  pokeArray sitesPtr (concatMap (map realToFrac) sites)
+  exitcodePtr <- mallocBytes (sizeOf (undefined :: CUInt))
+  resultPtr <- c_tesselation sitesPtr
+               (fromIntegral dim) (fromIntegral n)
+               (fromIntegral $ fromEnum atinfinity)
+               (fromIntegral $ fromEnum degenerate)
+               (realToFrac vthreshold') exitcodePtr
+  exitcode <- peek exitcodePtr
+  free exitcodePtr
+  free sitesPtr
+  if exitcode /= 0
+    then do
+      free resultPtr
+      error $ "qhull returned an error (code " ++ show exitcode ++ ")"
+    else do
+      result <- peek resultPtr
+      out <- cTesselationToTesselation sites result
+      free resultPtr
+      return out
+
+-- | tile facets a vertex belongs to, vertex given by its index;
+-- the output is the empty map if the index is not valid
+vertexNeighborFacets :: Tesselation -> Index -> IntMap TileFacet
+vertexNeighborFacets tess i = IM.restrictKeys (_tilefacets tess) ids
+  where
+    ids = maybe IS.empty _neighfacetsIds (IM.lookup i (_sites tess))
+
+-- | whether a tile facet is sandwiched between two tiles
+sandwichedFacet :: TileFacet -> Bool
+sandwichedFacet tilefacet = IS.size (_facetOf tilefacet) == 2
+
+-- | the tiles a facet belongs to
+facetOf :: Tesselation -> TileFacet -> IntMap Tile
+facetOf tess tilefacet = IM.restrictKeys (_tiles tess) (_facetOf tilefacet)
+
+-- | the families of the tiles a facet belongs to
+facetFamilies :: Tesselation -> TileFacet -> IntMap Family
+facetFamilies tess tilefacet = IM.map _family (facetOf tess tilefacet)
+
+-- | the circumcenters of the tiles a facet belongs to
+facetCenters :: Tesselation -> TileFacet -> IntMap [Double]
+facetCenters tess tilefacet =
+  IM.map _center (facetOf tess tilefacet)
+
+funofFacetToFunofInt :: (Tesselation -> TileFacet -> IntMap a)
+                     -> (Tesselation -> Int -> IntMap a)
+funofFacetToFunofInt f tess i =
+  maybe IM.empty (f tess) (IM.lookup i (_tilefacets tess))
+
+-- | the tiles a facet belongs to, facet given by its id
+facetOf' :: Tesselation -> Int -> IntMap Tile
+facetOf' = funofFacetToFunofInt facetOf
+
+-- | the families of the tiles a facet belongs to, facet given by its id
+facetFamilies' :: Tesselation -> Int -> IntMap Family
+facetFamilies' = funofFacetToFunofInt facetFamilies
+
+-- | the circumcenters of the tiles a facet belongs to, facet given by its id
+facetCenters' :: Tesselation -> Int -> IntMap [Double]
+facetCenters' = funofFacetToFunofInt facetCenters
diff --git a/src/Delaunay/Examples.hs b/src/Delaunay/Examples.hs
new file mode 100644
--- /dev/null
+++ b/src/Delaunay/Examples.hs
@@ -0,0 +1,910 @@
+module Delaunay.Examples
+  (  module X
+   , duoCylinder)
+  where
+import ConvexHull.Examples as X
+
+duoCylinder :: [[Double]]
+duoCylinder =
+  [
+   [1, 0, 1, 0],
+   [1, 0, 0.978147600733806, 0.207911690817759],
+   [1, 0, 0.913545457642601, 0.4067366430758],
+   [1, 0, 0.809016994374947, 0.587785252292473],
+   [1, 0, 0.669130606358858, 0.743144825477394],
+   [1, 0, 0.5, 0.866025403784439],
+   [1, 0, 0.309016994374947, 0.951056516295154],
+   [1, 0, 0.104528463267653, 0.994521895368273],
+   [1, 0, -0.104528463267653, 0.994521895368273],
+   [1, 0, -0.309016994374947, 0.951056516295154],
+   [1, 0, -0.5, 0.866025403784439],
+   [1, 0, -0.669130606358858, 0.743144825477394],
+   [1, 0, -0.809016994374947, 0.587785252292473],
+   [1, 0, -0.913545457642601, 0.4067366430758],
+   [1, 0, -0.978147600733806, 0.207911690817759],
+   [1, 0, -1, 5.665498452323e-16],
+   [1, 0, -0.978147600733806, -0.207911690817759],
+   [1, 0, -0.913545457642601, -0.4067366430758],
+   [1, 0, -0.809016994374948, -0.587785252292473],
+   [1, 0, -0.669130606358858, -0.743144825477394],
+   [1, 0, -0.5, -0.866025403784438],
+   [1, 0, -0.309016994374948, -0.951056516295154],
+   [1, 0, -0.104528463267654, -0.994521895368273],
+   [1, 0, 0.104528463267653, -0.994521895368273],
+   [1, 0, 0.309016994374947, -0.951056516295154],
+   [1, 0, 0.5, -0.866025403784439],
+   [1, 0, 0.669130606358858, -0.743144825477394],
+   [1, 0, 0.809016994374947, -0.587785252292473],
+   [1, 0, 0.913545457642601, -0.4067366430758],
+   [1, 0, 0.978147600733806, -0.207911690817759],
+   [0.978147600733806, 0.207911690817759, 1, 0],
+   [0.978147600733806, 0.207911690817759, 0.978147600733806, 0.207911690817759],
+   [0.978147600733806, 0.207911690817759, 0.913545457642601, 0.4067366430758],
+   [0.978147600733806, 0.207911690817759, 0.809016994374947, 0.587785252292473],
+   [0.978147600733806, 0.207911690817759, 0.669130606358858, 0.743144825477394],
+   [0.978147600733806, 0.207911690817759, 0.5, 0.866025403784439],
+   [0.978147600733806, 0.207911690817759, 0.309016994374947, 0.951056516295154],
+   [0.978147600733806, 0.207911690817759, 0.104528463267653, 0.994521895368273],
+   [0.978147600733806, 0.207911690817759, -0.104528463267653, 0.994521895368273],
+   [0.978147600733806, 0.207911690817759, -0.309016994374947, 0.951056516295154],
+   [0.978147600733806, 0.207911690817759, -0.5, 0.866025403784439],
+   [0.978147600733806, 0.207911690817759, -0.669130606358858, 0.743144825477394],
+   [0.978147600733806, 0.207911690817759, -0.809016994374947, 0.587785252292473],
+   [0.978147600733806, 0.207911690817759, -0.913545457642601, 0.4067366430758],
+   [0.978147600733806, 0.207911690817759, -0.978147600733806, 0.207911690817759],
+   [0.978147600733806, 0.207911690817759, -1, 5.665498452323e-16],
+   [0.978147600733806, 0.207911690817759, -0.978147600733806, -0.207911690817759],
+   [0.978147600733806, 0.207911690817759, -0.913545457642601, -0.4067366430758],
+   [0.978147600733806, 0.207911690817759, -0.809016994374948, -0.587785252292473],
+   [0.978147600733806, 0.207911690817759, -0.669130606358858, -0.743144825477394],
+   [0.978147600733806, 0.207911690817759, -0.5, -0.866025403784438],
+   [0.978147600733806, 0.207911690817759, -0.309016994374948, -0.951056516295154],
+   [0.978147600733806, 0.207911690817759, -0.104528463267654, -0.994521895368273],
+   [0.978147600733806, 0.207911690817759, 0.104528463267653, -0.994521895368273],
+   [0.978147600733806, 0.207911690817759, 0.309016994374947, -0.951056516295154],
+   [0.978147600733806, 0.207911690817759, 0.5, -0.866025403784439],
+   [0.978147600733806, 0.207911690817759, 0.669130606358858, -0.743144825477394],
+   [0.978147600733806, 0.207911690817759, 0.809016994374947, -0.587785252292473],
+   [0.978147600733806, 0.207911690817759, 0.913545457642601, -0.4067366430758],
+   [0.978147600733806, 0.207911690817759, 0.978147600733806, -0.207911690817759],
+   [0.913545457642601, 0.4067366430758, 1, 0],
+   [0.913545457642601, 0.4067366430758, 0.978147600733806, 0.207911690817759],
+   [0.913545457642601, 0.4067366430758, 0.913545457642601, 0.4067366430758],
+   [0.913545457642601, 0.4067366430758, 0.809016994374947, 0.587785252292473],
+   [0.913545457642601, 0.4067366430758, 0.669130606358858, 0.743144825477394],
+   [0.913545457642601, 0.4067366430758, 0.5, 0.866025403784439],
+   [0.913545457642601, 0.4067366430758, 0.309016994374947, 0.951056516295154],
+   [0.913545457642601, 0.4067366430758, 0.104528463267653, 0.994521895368273],
+   [0.913545457642601, 0.4067366430758, -0.104528463267653, 0.994521895368273],
+   [0.913545457642601, 0.4067366430758, -0.309016994374947, 0.951056516295154],
+   [0.913545457642601, 0.4067366430758, -0.5, 0.866025403784439],
+   [0.913545457642601, 0.4067366430758, -0.669130606358858, 0.743144825477394],
+   [0.913545457642601, 0.4067366430758, -0.809016994374947, 0.587785252292473],
+   [0.913545457642601, 0.4067366430758, -0.913545457642601, 0.4067366430758],
+   [0.913545457642601, 0.4067366430758, -0.978147600733806, 0.207911690817759],
+   [0.913545457642601, 0.4067366430758, -1, 5.665498452323e-16],
+   [0.913545457642601, 0.4067366430758, -0.978147600733806, -0.207911690817759],
+   [0.913545457642601, 0.4067366430758, -0.913545457642601, -0.4067366430758],
+   [0.913545457642601, 0.4067366430758, -0.809016994374948, -0.587785252292473],
+   [0.913545457642601, 0.4067366430758, -0.669130606358858, -0.743144825477394],
+   [0.913545457642601, 0.4067366430758, -0.5, -0.866025403784438],
+   [0.913545457642601, 0.4067366430758, -0.309016994374948, -0.951056516295154],
+   [0.913545457642601, 0.4067366430758, -0.104528463267654, -0.994521895368273],
+   [0.913545457642601, 0.4067366430758, 0.104528463267653, -0.994521895368273],
+   [0.913545457642601, 0.4067366430758, 0.309016994374947, -0.951056516295154],
+   [0.913545457642601, 0.4067366430758, 0.5, -0.866025403784439],
+   [0.913545457642601, 0.4067366430758, 0.669130606358858, -0.743144825477394],
+   [0.913545457642601, 0.4067366430758, 0.809016994374947, -0.587785252292473],
+   [0.913545457642601, 0.4067366430758, 0.913545457642601, -0.4067366430758],
+   [0.913545457642601, 0.4067366430758, 0.978147600733806, -0.207911690817759],
+   [0.809016994374947, 0.587785252292473, 1, 0],
+   [0.809016994374947, 0.587785252292473, 0.978147600733806, 0.207911690817759],
+   [0.809016994374947, 0.587785252292473, 0.913545457642601, 0.4067366430758],
+   [0.809016994374947, 0.587785252292473, 0.809016994374947, 0.587785252292473],
+   [0.809016994374947, 0.587785252292473, 0.669130606358858, 0.743144825477394],
+   [0.809016994374947, 0.587785252292473, 0.5, 0.866025403784439],
+   [0.809016994374947, 0.587785252292473, 0.309016994374947, 0.951056516295154],
+   [0.809016994374947, 0.587785252292473, 0.104528463267653, 0.994521895368273],
+   [0.809016994374947, 0.587785252292473, -0.104528463267653, 0.994521895368273],
+   [0.809016994374947, 0.587785252292473, -0.309016994374947, 0.951056516295154],
+   [0.809016994374947, 0.587785252292473, -0.5, 0.866025403784439],
+   [0.809016994374947, 0.587785252292473, -0.669130606358858, 0.743144825477394],
+   [0.809016994374947, 0.587785252292473, -0.809016994374947, 0.587785252292473],
+   [0.809016994374947, 0.587785252292473, -0.913545457642601, 0.4067366430758],
+   [0.809016994374947, 0.587785252292473, -0.978147600733806, 0.207911690817759],
+   [0.809016994374947, 0.587785252292473, -1, 5.665498452323e-16],
+   [0.809016994374947, 0.587785252292473, -0.978147600733806, -0.207911690817759],
+   [0.809016994374947, 0.587785252292473, -0.913545457642601, -0.4067366430758],
+   [0.809016994374947, 0.587785252292473, -0.809016994374948, -0.587785252292473],
+   [0.809016994374947, 0.587785252292473, -0.669130606358858, -0.743144825477394],
+   [0.809016994374947, 0.587785252292473, -0.5, -0.866025403784438],
+   [0.809016994374947, 0.587785252292473, -0.309016994374948, -0.951056516295154],
+   [0.809016994374947, 0.587785252292473, -0.104528463267654, -0.994521895368273],
+   [0.809016994374947, 0.587785252292473, 0.104528463267653, -0.994521895368273],
+   [0.809016994374947, 0.587785252292473, 0.309016994374947, -0.951056516295154],
+   [0.809016994374947, 0.587785252292473, 0.5, -0.866025403784439],
+   [0.809016994374947, 0.587785252292473, 0.669130606358858, -0.743144825477394],
+   [0.809016994374947, 0.587785252292473, 0.809016994374947, -0.587785252292473],
+   [0.809016994374947, 0.587785252292473, 0.913545457642601, -0.4067366430758],
+   [0.809016994374947, 0.587785252292473, 0.978147600733806, -0.207911690817759],
+   [0.669130606358858, 0.743144825477394, 1, 0],
+   [0.669130606358858, 0.743144825477394, 0.978147600733806, 0.207911690817759],
+   [0.669130606358858, 0.743144825477394, 0.913545457642601, 0.4067366430758],
+   [0.669130606358858, 0.743144825477394, 0.809016994374947, 0.587785252292473],
+   [0.669130606358858, 0.743144825477394, 0.669130606358858, 0.743144825477394],
+   [0.669130606358858, 0.743144825477394, 0.5, 0.866025403784439],
+   [0.669130606358858, 0.743144825477394, 0.309016994374947, 0.951056516295154],
+   [0.669130606358858, 0.743144825477394, 0.104528463267653, 0.994521895368273],
+   [0.669130606358858, 0.743144825477394, -0.104528463267653, 0.994521895368273],
+   [0.669130606358858, 0.743144825477394, -0.309016994374947, 0.951056516295154],
+   [0.669130606358858, 0.743144825477394, -0.5, 0.866025403784439],
+   [0.669130606358858, 0.743144825477394, -0.669130606358858, 0.743144825477394],
+   [0.669130606358858, 0.743144825477394, -0.809016994374947, 0.587785252292473],
+   [0.669130606358858, 0.743144825477394, -0.913545457642601, 0.4067366430758],
+   [0.669130606358858, 0.743144825477394, -0.978147600733806, 0.207911690817759],
+   [0.669130606358858, 0.743144825477394, -1, 5.665498452323e-16],
+   [0.669130606358858, 0.743144825477394, -0.978147600733806, -0.207911690817759],
+   [0.669130606358858, 0.743144825477394, -0.913545457642601, -0.4067366430758],
+   [0.669130606358858, 0.743144825477394, -0.809016994374948, -0.587785252292473],
+   [0.669130606358858, 0.743144825477394, -0.669130606358858, -0.743144825477394],
+   [0.669130606358858, 0.743144825477394, -0.5, -0.866025403784438],
+   [0.669130606358858, 0.743144825477394, -0.309016994374948, -0.951056516295154],
+   [0.669130606358858, 0.743144825477394, -0.104528463267654, -0.994521895368273],
+   [0.669130606358858, 0.743144825477394, 0.104528463267653, -0.994521895368273],
+   [0.669130606358858, 0.743144825477394, 0.309016994374947, -0.951056516295154],
+   [0.669130606358858, 0.743144825477394, 0.5, -0.866025403784439],
+   [0.669130606358858, 0.743144825477394, 0.669130606358858, -0.743144825477394],
+   [0.669130606358858, 0.743144825477394, 0.809016994374947, -0.587785252292473],
+   [0.669130606358858, 0.743144825477394, 0.913545457642601, -0.4067366430758],
+   [0.669130606358858, 0.743144825477394, 0.978147600733806, -0.207911690817759],
+   [0.5, 0.866025403784439, 1, 0],
+   [0.5, 0.866025403784439, 0.978147600733806, 0.207911690817759],
+   [0.5, 0.866025403784439, 0.913545457642601, 0.4067366430758],
+   [0.5, 0.866025403784439, 0.809016994374947, 0.587785252292473],
+   [0.5, 0.866025403784439, 0.669130606358858, 0.743144825477394],
+   [0.5, 0.866025403784439, 0.5, 0.866025403784439],
+   [0.5, 0.866025403784439, 0.309016994374947, 0.951056516295154],
+   [0.5, 0.866025403784439, 0.104528463267653, 0.994521895368273],
+   [0.5, 0.866025403784439, -0.104528463267653, 0.994521895368273],
+   [0.5, 0.866025403784439, -0.309016994374947, 0.951056516295154],
+   [0.5, 0.866025403784439, -0.5, 0.866025403784439],
+   [0.5, 0.866025403784439, -0.669130606358858, 0.743144825477394],
+   [0.5, 0.866025403784439, -0.809016994374947, 0.587785252292473],
+   [0.5, 0.866025403784439, -0.913545457642601, 0.4067366430758],
+   [0.5, 0.866025403784439, -0.978147600733806, 0.207911690817759],
+   [0.5, 0.866025403784439, -1, 5.665498452323e-16],
+   [0.5, 0.866025403784439, -0.978147600733806, -0.207911690817759],
+   [0.5, 0.866025403784439, -0.913545457642601, -0.4067366430758],
+   [0.5, 0.866025403784439, -0.809016994374948, -0.587785252292473],
+   [0.5, 0.866025403784439, -0.669130606358858, -0.743144825477394],
+   [0.5, 0.866025403784439, -0.5, -0.866025403784438],
+   [0.5, 0.866025403784439, -0.309016994374948, -0.951056516295154],
+   [0.5, 0.866025403784439, -0.104528463267654, -0.994521895368273],
+   [0.5, 0.866025403784439, 0.104528463267653, -0.994521895368273],
+   [0.5, 0.866025403784439, 0.309016994374947, -0.951056516295154],
+   [0.5, 0.866025403784439, 0.5, -0.866025403784439],
+   [0.5, 0.866025403784439, 0.669130606358858, -0.743144825477394],
+   [0.5, 0.866025403784439, 0.809016994374947, -0.587785252292473],
+   [0.5, 0.866025403784439, 0.913545457642601, -0.4067366430758],
+   [0.5, 0.866025403784439, 0.978147600733806, -0.207911690817759],
+   [0.309016994374947, 0.951056516295154, 1, 0],
+   [0.309016994374947, 0.951056516295154, 0.978147600733806, 0.207911690817759],
+   [0.309016994374947, 0.951056516295154, 0.913545457642601, 0.4067366430758],
+   [0.309016994374947, 0.951056516295154, 0.809016994374947, 0.587785252292473],
+   [0.309016994374947, 0.951056516295154, 0.669130606358858, 0.743144825477394],
+   [0.309016994374947, 0.951056516295154, 0.5, 0.866025403784439],
+   [0.309016994374947, 0.951056516295154, 0.309016994374947, 0.951056516295154],
+   [0.309016994374947, 0.951056516295154, 0.104528463267653, 0.994521895368273],
+   [0.309016994374947, 0.951056516295154, -0.104528463267653, 0.994521895368273],
+   [0.309016994374947, 0.951056516295154, -0.309016994374947, 0.951056516295154],
+   [0.309016994374947, 0.951056516295154, -0.5, 0.866025403784439],
+   [0.309016994374947, 0.951056516295154, -0.669130606358858, 0.743144825477394],
+   [0.309016994374947, 0.951056516295154, -0.809016994374947, 0.587785252292473],
+   [0.309016994374947, 0.951056516295154, -0.913545457642601, 0.4067366430758],
+   [0.309016994374947, 0.951056516295154, -0.978147600733806, 0.207911690817759],
+   [0.309016994374947, 0.951056516295154, -1, 5.665498452323e-16],
+   [0.309016994374947, 0.951056516295154, -0.978147600733806, -0.207911690817759],
+   [0.309016994374947, 0.951056516295154, -0.913545457642601, -0.4067366430758],
+   [0.309016994374947, 0.951056516295154, -0.809016994374948, -0.587785252292473],
+   [0.309016994374947, 0.951056516295154, -0.669130606358858, -0.743144825477394],
+   [0.309016994374947, 0.951056516295154, -0.5, -0.866025403784438],
+   [0.309016994374947, 0.951056516295154, -0.309016994374948, -0.951056516295154],
+   [0.309016994374947, 0.951056516295154, -0.104528463267654, -0.994521895368273],
+   [0.309016994374947, 0.951056516295154, 0.104528463267653, -0.994521895368273],
+   [0.309016994374947, 0.951056516295154, 0.309016994374947, -0.951056516295154],
+   [0.309016994374947, 0.951056516295154, 0.5, -0.866025403784439],
+   [0.309016994374947, 0.951056516295154, 0.669130606358858, -0.743144825477394],
+   [0.309016994374947, 0.951056516295154, 0.809016994374947, -0.587785252292473],
+   [0.309016994374947, 0.951056516295154, 0.913545457642601, -0.4067366430758],
+   [0.309016994374947, 0.951056516295154, 0.978147600733806, -0.207911690817759],
+   [0.104528463267653, 0.994521895368273, 1, 0],
+   [0.104528463267653, 0.994521895368273, 0.978147600733806, 0.207911690817759],
+   [0.104528463267653, 0.994521895368273, 0.913545457642601, 0.4067366430758],
+   [0.104528463267653, 0.994521895368273, 0.809016994374947, 0.587785252292473],
+   [0.104528463267653, 0.994521895368273, 0.669130606358858, 0.743144825477394],
+   [0.104528463267653, 0.994521895368273, 0.5, 0.866025403784439],
+   [0.104528463267653, 0.994521895368273, 0.309016994374947, 0.951056516295154],
+   [0.104528463267653, 0.994521895368273, 0.104528463267653, 0.994521895368273],
+   [0.104528463267653, 0.994521895368273, -0.104528463267653, 0.994521895368273],
+   [0.104528463267653, 0.994521895368273, -0.309016994374947, 0.951056516295154],
+   [0.104528463267653, 0.994521895368273, -0.5, 0.866025403784439],
+   [0.104528463267653, 0.994521895368273, -0.669130606358858, 0.743144825477394],
+   [0.104528463267653, 0.994521895368273, -0.809016994374947, 0.587785252292473],
+   [0.104528463267653, 0.994521895368273, -0.913545457642601, 0.4067366430758],
+   [0.104528463267653, 0.994521895368273, -0.978147600733806, 0.207911690817759],
+   [0.104528463267653, 0.994521895368273, -1, 5.665498452323e-16],
+   [0.104528463267653, 0.994521895368273, -0.978147600733806, -0.207911690817759],
+   [0.104528463267653, 0.994521895368273, -0.913545457642601, -0.4067366430758],
+   [0.104528463267653, 0.994521895368273, -0.809016994374948, -0.587785252292473],
+   [0.104528463267653, 0.994521895368273, -0.669130606358858, -0.743144825477394],
+   [0.104528463267653, 0.994521895368273, -0.5, -0.866025403784438],
+   [0.104528463267653, 0.994521895368273, -0.309016994374948, -0.951056516295154],
+   [0.104528463267653, 0.994521895368273, -0.104528463267654, -0.994521895368273],
+   [0.104528463267653, 0.994521895368273, 0.104528463267653, -0.994521895368273],
+   [0.104528463267653, 0.994521895368273, 0.309016994374947, -0.951056516295154],
+   [0.104528463267653, 0.994521895368273, 0.5, -0.866025403784439],
+   [0.104528463267653, 0.994521895368273, 0.669130606358858, -0.743144825477394],
+   [0.104528463267653, 0.994521895368273, 0.809016994374947, -0.587785252292473],
+   [0.104528463267653, 0.994521895368273, 0.913545457642601, -0.4067366430758],
+   [0.104528463267653, 0.994521895368273, 0.978147600733806, -0.207911690817759],
+   [-0.104528463267653, 0.994521895368273, 1, 0],
+   [-0.104528463267653, 0.994521895368273, 0.978147600733806, 0.207911690817759],
+   [-0.104528463267653, 0.994521895368273, 0.913545457642601, 0.4067366430758],
+   [-0.104528463267653, 0.994521895368273, 0.809016994374947, 0.587785252292473],
+   [-0.104528463267653, 0.994521895368273, 0.669130606358858, 0.743144825477394],
+   [-0.104528463267653, 0.994521895368273, 0.5, 0.866025403784439],
+   [-0.104528463267653, 0.994521895368273, 0.309016994374947, 0.951056516295154],
+   [-0.104528463267653, 0.994521895368273, 0.104528463267653, 0.994521895368273],
+   [-0.104528463267653, 0.994521895368273, -0.104528463267653, 0.994521895368273],
+   [-0.104528463267653, 0.994521895368273, -0.309016994374947, 0.951056516295154],
+   [-0.104528463267653, 0.994521895368273, -0.5, 0.866025403784439],
+   [-0.104528463267653, 0.994521895368273, -0.669130606358858, 0.743144825477394],
+   [-0.104528463267653, 0.994521895368273, -0.809016994374947, 0.587785252292473],
+   [-0.104528463267653, 0.994521895368273, -0.913545457642601, 0.4067366430758],
+   [-0.104528463267653, 0.994521895368273, -0.978147600733806, 0.207911690817759],
+   [-0.104528463267653, 0.994521895368273, -1, 5.665498452323e-16],
+   [-0.104528463267653, 0.994521895368273, -0.978147600733806, -0.207911690817759],
+   [-0.104528463267653, 0.994521895368273, -0.913545457642601, -0.4067366430758],
+   [-0.104528463267653, 0.994521895368273, -0.809016994374948, -0.587785252292473],
+   [-0.104528463267653, 0.994521895368273, -0.669130606358858, -0.743144825477394],
+   [-0.104528463267653, 0.994521895368273, -0.5, -0.866025403784438],
+   [-0.104528463267653, 0.994521895368273, -0.309016994374948, -0.951056516295154],
+   [-0.104528463267653, 0.994521895368273, -0.104528463267654, -0.994521895368273],
+   [-0.104528463267653, 0.994521895368273, 0.104528463267653, -0.994521895368273],
+   [-0.104528463267653, 0.994521895368273, 0.309016994374947, -0.951056516295154],
+   [-0.104528463267653, 0.994521895368273, 0.5, -0.866025403784439],
+   [-0.104528463267653, 0.994521895368273, 0.669130606358858, -0.743144825477394],
+   [-0.104528463267653, 0.994521895368273, 0.809016994374947, -0.587785252292473],
+   [-0.104528463267653, 0.994521895368273, 0.913545457642601, -0.4067366430758],
+   [-0.104528463267653, 0.994521895368273, 0.978147600733806, -0.207911690817759],
+   [-0.309016994374947, 0.951056516295154, 1, 0],
+   [-0.309016994374947, 0.951056516295154, 0.978147600733806, 0.207911690817759],
+   [-0.309016994374947, 0.951056516295154, 0.913545457642601, 0.4067366430758],
+   [-0.309016994374947, 0.951056516295154, 0.809016994374947, 0.587785252292473],
+   [-0.309016994374947, 0.951056516295154, 0.669130606358858, 0.743144825477394],
+   [-0.309016994374947, 0.951056516295154, 0.5, 0.866025403784439],
+   [-0.309016994374947, 0.951056516295154, 0.309016994374947, 0.951056516295154],
+   [-0.309016994374947, 0.951056516295154, 0.104528463267653, 0.994521895368273],
+   [-0.309016994374947, 0.951056516295154, -0.104528463267653, 0.994521895368273],
+   [-0.309016994374947, 0.951056516295154, -0.309016994374947, 0.951056516295154],
+   [-0.309016994374947, 0.951056516295154, -0.5, 0.866025403784439],
+   [-0.309016994374947, 0.951056516295154, -0.669130606358858, 0.743144825477394],
+   [-0.309016994374947, 0.951056516295154, -0.809016994374947, 0.587785252292473],
+   [-0.309016994374947, 0.951056516295154, -0.913545457642601, 0.4067366430758],
+   [-0.309016994374947, 0.951056516295154, -0.978147600733806, 0.207911690817759],
+   [-0.309016994374947, 0.951056516295154, -1, 5.665498452323e-16],
+   [-0.309016994374947, 0.951056516295154, -0.978147600733806, -0.207911690817759],
+   [-0.309016994374947, 0.951056516295154, -0.913545457642601, -0.4067366430758],
+   [-0.309016994374947, 0.951056516295154, -0.809016994374948, -0.587785252292473],
+   [-0.309016994374947, 0.951056516295154, -0.669130606358858, -0.743144825477394],
+   [-0.309016994374947, 0.951056516295154, -0.5, -0.866025403784438],
+   [-0.309016994374947, 0.951056516295154, -0.309016994374948, -0.951056516295154],
+   [-0.309016994374947, 0.951056516295154, -0.104528463267654, -0.994521895368273],
+   [-0.309016994374947, 0.951056516295154, 0.104528463267653, -0.994521895368273],
+   [-0.309016994374947, 0.951056516295154, 0.309016994374947, -0.951056516295154],
+   [-0.309016994374947, 0.951056516295154, 0.5, -0.866025403784439],
+   [-0.309016994374947, 0.951056516295154, 0.669130606358858, -0.743144825477394],
+   [-0.309016994374947, 0.951056516295154, 0.809016994374947, -0.587785252292473],
+   [-0.309016994374947, 0.951056516295154, 0.913545457642601, -0.4067366430758],
+   [-0.309016994374947, 0.951056516295154, 0.978147600733806, -0.207911690817759],
+   [-0.5, 0.866025403784439, 1, 0],
+   [-0.5, 0.866025403784439, 0.978147600733806, 0.207911690817759],
+   [-0.5, 0.866025403784439, 0.913545457642601, 0.4067366430758],
+   [-0.5, 0.866025403784439, 0.809016994374947, 0.587785252292473],
+   [-0.5, 0.866025403784439, 0.669130606358858, 0.743144825477394],
+   [-0.5, 0.866025403784439, 0.5, 0.866025403784439],
+   [-0.5, 0.866025403784439, 0.309016994374947, 0.951056516295154],
+   [-0.5, 0.866025403784439, 0.104528463267653, 0.994521895368273],
+   [-0.5, 0.866025403784439, -0.104528463267653, 0.994521895368273],
+   [-0.5, 0.866025403784439, -0.309016994374947, 0.951056516295154],
+   [-0.5, 0.866025403784439, -0.5, 0.866025403784439],
+   [-0.5, 0.866025403784439, -0.669130606358858, 0.743144825477394],
+   [-0.5, 0.866025403784439, -0.809016994374947, 0.587785252292473],
+   [-0.5, 0.866025403784439, -0.913545457642601, 0.4067366430758],
+   [-0.5, 0.866025403784439, -0.978147600733806, 0.207911690817759],
+   [-0.5, 0.866025403784439, -1, 5.665498452323e-16],
+   [-0.5, 0.866025403784439, -0.978147600733806, -0.207911690817759],
+   [-0.5, 0.866025403784439, -0.913545457642601, -0.4067366430758],
+   [-0.5, 0.866025403784439, -0.809016994374948, -0.587785252292473],
+   [-0.5, 0.866025403784439, -0.669130606358858, -0.743144825477394],
+   [-0.5, 0.866025403784439, -0.5, -0.866025403784438],
+   [-0.5, 0.866025403784439, -0.309016994374948, -0.951056516295154],
+   [-0.5, 0.866025403784439, -0.104528463267654, -0.994521895368273],
+   [-0.5, 0.866025403784439, 0.104528463267653, -0.994521895368273],
+   [-0.5, 0.866025403784439, 0.309016994374947, -0.951056516295154],
+   [-0.5, 0.866025403784439, 0.5, -0.866025403784439],
+   [-0.5, 0.866025403784439, 0.669130606358858, -0.743144825477394],
+   [-0.5, 0.866025403784439, 0.809016994374947, -0.587785252292473],
+   [-0.5, 0.866025403784439, 0.913545457642601, -0.4067366430758],
+   [-0.5, 0.866025403784439, 0.978147600733806, -0.207911690817759],
+   [-0.669130606358858, 0.743144825477394, 1, 0],
+   [-0.669130606358858, 0.743144825477394, 0.978147600733806, 0.207911690817759],
+   [-0.669130606358858, 0.743144825477394, 0.913545457642601, 0.4067366430758],
+   [-0.669130606358858, 0.743144825477394, 0.809016994374947, 0.587785252292473],
+   [-0.669130606358858, 0.743144825477394, 0.669130606358858, 0.743144825477394],
+   [-0.669130606358858, 0.743144825477394, 0.5, 0.866025403784439],
+   [-0.669130606358858, 0.743144825477394, 0.309016994374947, 0.951056516295154],
+   [-0.669130606358858, 0.743144825477394, 0.104528463267653, 0.994521895368273],
+   [-0.669130606358858, 0.743144825477394, -0.104528463267653, 0.994521895368273],
+   [-0.669130606358858, 0.743144825477394, -0.309016994374947, 0.951056516295154],
+   [-0.669130606358858, 0.743144825477394, -0.5, 0.866025403784439],
+   [-0.669130606358858, 0.743144825477394, -0.669130606358858, 0.743144825477394],
+   [-0.669130606358858, 0.743144825477394, -0.809016994374947, 0.587785252292473],
+   [-0.669130606358858, 0.743144825477394, -0.913545457642601, 0.4067366430758],
+   [-0.669130606358858, 0.743144825477394, -0.978147600733806, 0.207911690817759],
+   [-0.669130606358858, 0.743144825477394, -1, 5.665498452323e-16],
+   [-0.669130606358858, 0.743144825477394, -0.978147600733806, -0.207911690817759],
+   [-0.669130606358858, 0.743144825477394, -0.913545457642601, -0.4067366430758],
+   [-0.669130606358858, 0.743144825477394, -0.809016994374948, -0.587785252292473],
+   [-0.669130606358858, 0.743144825477394, -0.669130606358858, -0.743144825477394],
+   [-0.669130606358858, 0.743144825477394, -0.5, -0.866025403784438],
+   [-0.669130606358858, 0.743144825477394, -0.309016994374948, -0.951056516295154],
+   [-0.669130606358858, 0.743144825477394, -0.104528463267654, -0.994521895368273],
+   [-0.669130606358858, 0.743144825477394, 0.104528463267653, -0.994521895368273],
+   [-0.669130606358858, 0.743144825477394, 0.309016994374947, -0.951056516295154],
+   [-0.669130606358858, 0.743144825477394, 0.5, -0.866025403784439],
+   [-0.669130606358858, 0.743144825477394, 0.669130606358858, -0.743144825477394],
+   [-0.669130606358858, 0.743144825477394, 0.809016994374947, -0.587785252292473],
+   [-0.669130606358858, 0.743144825477394, 0.913545457642601, -0.4067366430758],
+   [-0.669130606358858, 0.743144825477394, 0.978147600733806, -0.207911690817759],
+   [-0.809016994374947, 0.587785252292473, 1, 0],
+   [-0.809016994374947, 0.587785252292473, 0.978147600733806, 0.207911690817759],
+   [-0.809016994374947, 0.587785252292473, 0.913545457642601, 0.4067366430758],
+   [-0.809016994374947, 0.587785252292473, 0.809016994374947, 0.587785252292473],
+   [-0.809016994374947, 0.587785252292473, 0.669130606358858, 0.743144825477394],
+   [-0.809016994374947, 0.587785252292473, 0.5, 0.866025403784439],
+   [-0.809016994374947, 0.587785252292473, 0.309016994374947, 0.951056516295154],
+   [-0.809016994374947, 0.587785252292473, 0.104528463267653, 0.994521895368273],
+   [-0.809016994374947, 0.587785252292473, -0.104528463267653, 0.994521895368273],
+   [-0.809016994374947, 0.587785252292473, -0.309016994374947, 0.951056516295154],
+   [-0.809016994374947, 0.587785252292473, -0.5, 0.866025403784439],
+   [-0.809016994374947, 0.587785252292473, -0.669130606358858, 0.743144825477394],
+   [-0.809016994374947, 0.587785252292473, -0.809016994374947, 0.587785252292473],
+   [-0.809016994374947, 0.587785252292473, -0.913545457642601, 0.4067366430758],
+   [-0.809016994374947, 0.587785252292473, -0.978147600733806, 0.207911690817759],
+   [-0.809016994374947, 0.587785252292473, -1, 5.665498452323e-16],
+   [-0.809016994374947, 0.587785252292473, -0.978147600733806, -0.207911690817759],
+   [-0.809016994374947, 0.587785252292473, -0.913545457642601, -0.4067366430758],
+   [-0.809016994374947, 0.587785252292473, -0.809016994374948, -0.587785252292473],
+   [-0.809016994374947, 0.587785252292473, -0.669130606358858, -0.743144825477394],
+   [-0.809016994374947, 0.587785252292473, -0.5, -0.866025403784438],
+   [-0.809016994374947, 0.587785252292473, -0.309016994374948, -0.951056516295154],
+   [-0.809016994374947, 0.587785252292473, -0.104528463267654, -0.994521895368273],
+   [-0.809016994374947, 0.587785252292473, 0.104528463267653, -0.994521895368273],
+   [-0.809016994374947, 0.587785252292473, 0.309016994374947, -0.951056516295154],
+   [-0.809016994374947, 0.587785252292473, 0.5, -0.866025403784439],
+   [-0.809016994374947, 0.587785252292473, 0.669130606358858, -0.743144825477394],
+   [-0.809016994374947, 0.587785252292473, 0.809016994374947, -0.587785252292473],
+   [-0.809016994374947, 0.587785252292473, 0.913545457642601, -0.4067366430758],
+   [-0.809016994374947, 0.587785252292473, 0.978147600733806, -0.207911690817759],
+   [-0.913545457642601, 0.4067366430758, 1, 0],
+   [-0.913545457642601, 0.4067366430758, 0.978147600733806, 0.207911690817759],
+   [-0.913545457642601, 0.4067366430758, 0.913545457642601, 0.4067366430758],
+   [-0.913545457642601, 0.4067366430758, 0.809016994374947, 0.587785252292473],
+   [-0.913545457642601, 0.4067366430758, 0.669130606358858, 0.743144825477394],
+   [-0.913545457642601, 0.4067366430758, 0.5, 0.866025403784439],
+   [-0.913545457642601, 0.4067366430758, 0.309016994374947, 0.951056516295154],
+   [-0.913545457642601, 0.4067366430758, 0.104528463267653, 0.994521895368273],
+   [-0.913545457642601, 0.4067366430758, -0.104528463267653, 0.994521895368273],
+   [-0.913545457642601, 0.4067366430758, -0.309016994374947, 0.951056516295154],
+   [-0.913545457642601, 0.4067366430758, -0.5, 0.866025403784439],
+   [-0.913545457642601, 0.4067366430758, -0.669130606358858, 0.743144825477394],
+   [-0.913545457642601, 0.4067366430758, -0.809016994374947, 0.587785252292473],
+   [-0.913545457642601, 0.4067366430758, -0.913545457642601, 0.4067366430758],
+   [-0.913545457642601, 0.4067366430758, -0.978147600733806, 0.207911690817759],
+   [-0.913545457642601, 0.4067366430758, -1, 5.665498452323e-16],
+   [-0.913545457642601, 0.4067366430758, -0.978147600733806, -0.207911690817759],
+   [-0.913545457642601, 0.4067366430758, -0.913545457642601, -0.4067366430758],
+   [-0.913545457642601, 0.4067366430758, -0.809016994374948, -0.587785252292473],
+   [-0.913545457642601, 0.4067366430758, -0.669130606358858, -0.743144825477394],
+   [-0.913545457642601, 0.4067366430758, -0.5, -0.866025403784438],
+   [-0.913545457642601, 0.4067366430758, -0.309016994374948, -0.951056516295154],
+   [-0.913545457642601, 0.4067366430758, -0.104528463267654, -0.994521895368273],
+   [-0.913545457642601, 0.4067366430758, 0.104528463267653, -0.994521895368273],
+   [-0.913545457642601, 0.4067366430758, 0.309016994374947, -0.951056516295154],
+   [-0.913545457642601, 0.4067366430758, 0.5, -0.866025403784439],
+   [-0.913545457642601, 0.4067366430758, 0.669130606358858, -0.743144825477394],
+   [-0.913545457642601, 0.4067366430758, 0.809016994374947, -0.587785252292473],
+   [-0.913545457642601, 0.4067366430758, 0.913545457642601, -0.4067366430758],
+   [-0.913545457642601, 0.4067366430758, 0.978147600733806, -0.207911690817759],
+   [-0.978147600733806, 0.207911690817759, 1, 0],
+   [-0.978147600733806, 0.207911690817759, 0.978147600733806, 0.207911690817759],
+   [-0.978147600733806, 0.207911690817759, 0.913545457642601, 0.4067366430758],
+   [-0.978147600733806, 0.207911690817759, 0.809016994374947, 0.587785252292473],
+   [-0.978147600733806, 0.207911690817759, 0.669130606358858, 0.743144825477394],
+   [-0.978147600733806, 0.207911690817759, 0.5, 0.866025403784439],
+   [-0.978147600733806, 0.207911690817759, 0.309016994374947, 0.951056516295154],
+   [-0.978147600733806, 0.207911690817759, 0.104528463267653, 0.994521895368273],
+   [-0.978147600733806, 0.207911690817759, -0.104528463267653, 0.994521895368273],
+   [-0.978147600733806, 0.207911690817759, -0.309016994374947, 0.951056516295154],
+   [-0.978147600733806, 0.207911690817759, -0.5, 0.866025403784439],
+   [-0.978147600733806, 0.207911690817759, -0.669130606358858, 0.743144825477394],
+   [-0.978147600733806, 0.207911690817759, -0.809016994374947, 0.587785252292473],
+   [-0.978147600733806, 0.207911690817759, -0.913545457642601, 0.4067366430758],
+   [-0.978147600733806, 0.207911690817759, -0.978147600733806, 0.207911690817759],
+   [-0.978147600733806, 0.207911690817759, -1, 5.665498452323e-16],
+   [-0.978147600733806, 0.207911690817759, -0.978147600733806, -0.207911690817759],
+   [-0.978147600733806, 0.207911690817759, -0.913545457642601, -0.4067366430758],
+   [-0.978147600733806, 0.207911690817759, -0.809016994374948, -0.587785252292473],
+   [-0.978147600733806, 0.207911690817759, -0.669130606358858, -0.743144825477394],
+   [-0.978147600733806, 0.207911690817759, -0.5, -0.866025403784438],
+   [-0.978147600733806, 0.207911690817759, -0.309016994374948, -0.951056516295154],
+   [-0.978147600733806, 0.207911690817759, -0.104528463267654, -0.994521895368273],
+   [-0.978147600733806, 0.207911690817759, 0.104528463267653, -0.994521895368273],
+   [-0.978147600733806, 0.207911690817759, 0.309016994374947, -0.951056516295154],
+   [-0.978147600733806, 0.207911690817759, 0.5, -0.866025403784439],
+   [-0.978147600733806, 0.207911690817759, 0.669130606358858, -0.743144825477394],
+   [-0.978147600733806, 0.207911690817759, 0.809016994374947, -0.587785252292473],
+   [-0.978147600733806, 0.207911690817759, 0.913545457642601, -0.4067366430758],
+   [-0.978147600733806, 0.207911690817759, 0.978147600733806, -0.207911690817759],
+   [-1, 5.665498452323e-16, 1, 0],
+   [-1, 5.665498452323e-16, 0.978147600733806, 0.207911690817759],
+   [-1, 5.665498452323e-16, 0.913545457642601, 0.4067366430758],
+   [-1, 5.665498452323e-16, 0.809016994374947, 0.587785252292473],
+   [-1, 5.665498452323e-16, 0.669130606358858, 0.743144825477394],
+   [-1, 5.665498452323e-16, 0.5, 0.866025403784439],
+   [-1, 5.665498452323e-16, 0.309016994374947, 0.951056516295154],
+   [-1, 5.665498452323e-16, 0.104528463267653, 0.994521895368273],
+   [-1, 5.665498452323e-16, -0.104528463267653, 0.994521895368273],
+   [-1, 5.665498452323e-16, -0.309016994374947, 0.951056516295154],
+   [-1, 5.665498452323e-16, -0.5, 0.866025403784439],
+   [-1, 5.665498452323e-16, -0.669130606358858, 0.743144825477394],
+   [-1, 5.665498452323e-16, -0.809016994374947, 0.587785252292473],
+   [-1, 5.665498452323e-16, -0.913545457642601, 0.4067366430758],
+   [-1, 5.665498452323e-16, -0.978147600733806, 0.207911690817759],
+   [-1, 5.665498452323e-16, -1, 5.665498452323e-16],
+   [-1, 5.665498452323e-16, -0.978147600733806, -0.207911690817759],
+   [-1, 5.665498452323e-16, -0.913545457642601, -0.4067366430758],
+   [-1, 5.665498452323e-16, -0.809016994374948, -0.587785252292473],
+   [-1, 5.665498452323e-16, -0.669130606358858, -0.743144825477394],
+   [-1, 5.665498452323e-16, -0.5, -0.866025403784438],
+   [-1, 5.665498452323e-16, -0.309016994374948, -0.951056516295154],
+   [-1, 5.665498452323e-16, -0.104528463267654, -0.994521895368273],
+   [-1, 5.665498452323e-16, 0.104528463267653, -0.994521895368273],
+   [-1, 5.665498452323e-16, 0.309016994374947, -0.951056516295154],
+   [-1, 5.665498452323e-16, 0.5, -0.866025403784439],
+   [-1, 5.665498452323e-16, 0.669130606358858, -0.743144825477394],
+   [-1, 5.665498452323e-16, 0.809016994374947, -0.587785252292473],
+   [-1, 5.665498452323e-16, 0.913545457642601, -0.4067366430758],
+   [-1, 5.665498452323e-16, 0.978147600733806, -0.207911690817759],
+   [-0.978147600733806, -0.207911690817759, 1, 0],
+   [-0.978147600733806, -0.207911690817759, 0.978147600733806, 0.207911690817759],
+   [-0.978147600733806, -0.207911690817759, 0.913545457642601, 0.4067366430758],
+   [-0.978147600733806, -0.207911690817759, 0.809016994374947, 0.587785252292473],
+   [-0.978147600733806, -0.207911690817759, 0.669130606358858, 0.743144825477394],
+   [-0.978147600733806, -0.207911690817759, 0.5, 0.866025403784439],
+   [-0.978147600733806, -0.207911690817759, 0.309016994374947, 0.951056516295154],
+   [-0.978147600733806, -0.207911690817759, 0.104528463267653, 0.994521895368273],
+   [-0.978147600733806, -0.207911690817759, -0.104528463267653, 0.994521895368273],
+   [-0.978147600733806, -0.207911690817759, -0.309016994374947, 0.951056516295154],
+   [-0.978147600733806, -0.207911690817759, -0.5, 0.866025403784439],
+   [-0.978147600733806, -0.207911690817759, -0.669130606358858, 0.743144825477394],
+   [-0.978147600733806, -0.207911690817759, -0.809016994374947, 0.587785252292473],
+   [-0.978147600733806, -0.207911690817759, -0.913545457642601, 0.4067366430758],
+   [-0.978147600733806, -0.207911690817759, -0.978147600733806, 0.207911690817759],
+   [-0.978147600733806, -0.207911690817759, -1, 5.665498452323e-16],
+   [-0.978147600733806, -0.207911690817759, -0.978147600733806, -0.207911690817759],
+   [-0.978147600733806, -0.207911690817759, -0.913545457642601, -0.4067366430758],
+   [-0.978147600733806, -0.207911690817759, -0.809016994374948, -0.587785252292473],
+   [-0.978147600733806, -0.207911690817759, -0.669130606358858, -0.743144825477394],
+   [-0.978147600733806, -0.207911690817759, -0.5, -0.866025403784438],
+   [-0.978147600733806, -0.207911690817759, -0.309016994374948, -0.951056516295154],
+   [-0.978147600733806, -0.207911690817759, -0.104528463267654, -0.994521895368273],
+   [-0.978147600733806, -0.207911690817759, 0.104528463267653, -0.994521895368273],
+   [-0.978147600733806, -0.207911690817759, 0.309016994374947, -0.951056516295154],
+   [-0.978147600733806, -0.207911690817759, 0.5, -0.866025403784439],
+   [-0.978147600733806, -0.207911690817759, 0.669130606358858, -0.743144825477394],
+   [-0.978147600733806, -0.207911690817759, 0.809016994374947, -0.587785252292473],
+   [-0.978147600733806, -0.207911690817759, 0.913545457642601, -0.4067366430758],
+   [-0.978147600733806, -0.207911690817759, 0.978147600733806, -0.207911690817759],
+   [-0.913545457642601, -0.4067366430758, 1, 0],
+   [-0.913545457642601, -0.4067366430758, 0.978147600733806, 0.207911690817759],
+   [-0.913545457642601, -0.4067366430758, 0.913545457642601, 0.4067366430758],
+   [-0.913545457642601, -0.4067366430758, 0.809016994374947, 0.587785252292473],
+   [-0.913545457642601, -0.4067366430758, 0.669130606358858, 0.743144825477394],
+   [-0.913545457642601, -0.4067366430758, 0.5, 0.866025403784439],
+   [-0.913545457642601, -0.4067366430758, 0.309016994374947, 0.951056516295154],
+   [-0.913545457642601, -0.4067366430758, 0.104528463267653, 0.994521895368273],
+   [-0.913545457642601, -0.4067366430758, -0.104528463267653, 0.994521895368273],
+   [-0.913545457642601, -0.4067366430758, -0.309016994374947, 0.951056516295154],
+   [-0.913545457642601, -0.4067366430758, -0.5, 0.866025403784439],
+   [-0.913545457642601, -0.4067366430758, -0.669130606358858, 0.743144825477394],
+   [-0.913545457642601, -0.4067366430758, -0.809016994374947, 0.587785252292473],
+   [-0.913545457642601, -0.4067366430758, -0.913545457642601, 0.4067366430758],
+   [-0.913545457642601, -0.4067366430758, -0.978147600733806, 0.207911690817759],
+   [-0.913545457642601, -0.4067366430758, -1, 5.665498452323e-16],
+   [-0.913545457642601, -0.4067366430758, -0.978147600733806, -0.207911690817759],
+   [-0.913545457642601, -0.4067366430758, -0.913545457642601, -0.4067366430758],
+   [-0.913545457642601, -0.4067366430758, -0.809016994374948, -0.587785252292473],
+   [-0.913545457642601, -0.4067366430758, -0.669130606358858, -0.743144825477394],
+   [-0.913545457642601, -0.4067366430758, -0.5, -0.866025403784438],
+   [-0.913545457642601, -0.4067366430758, -0.309016994374948, -0.951056516295154],
+   [-0.913545457642601, -0.4067366430758, -0.104528463267654, -0.994521895368273],
+   [-0.913545457642601, -0.4067366430758, 0.104528463267653, -0.994521895368273],
+   [-0.913545457642601, -0.4067366430758, 0.309016994374947, -0.951056516295154],
+   [-0.913545457642601, -0.4067366430758, 0.5, -0.866025403784439],
+   [-0.913545457642601, -0.4067366430758, 0.669130606358858, -0.743144825477394],
+   [-0.913545457642601, -0.4067366430758, 0.809016994374947, -0.587785252292473],
+   [-0.913545457642601, -0.4067366430758, 0.913545457642601, -0.4067366430758],
+   [-0.913545457642601, -0.4067366430758, 0.978147600733806, -0.207911690817759],
+   [-0.809016994374948, -0.587785252292473, 1, 0],
+   [-0.809016994374948, -0.587785252292473, 0.978147600733806, 0.207911690817759],
+   [-0.809016994374948, -0.587785252292473, 0.913545457642601, 0.4067366430758],
+   [-0.809016994374948, -0.587785252292473, 0.809016994374947, 0.587785252292473],
+   [-0.809016994374948, -0.587785252292473, 0.669130606358858, 0.743144825477394],
+   [-0.809016994374948, -0.587785252292473, 0.5, 0.866025403784439],
+   [-0.809016994374948, -0.587785252292473, 0.309016994374947, 0.951056516295154],
+   [-0.809016994374948, -0.587785252292473, 0.104528463267653, 0.994521895368273],
+   [-0.809016994374948, -0.587785252292473, -0.104528463267653, 0.994521895368273],
+   [-0.809016994374948, -0.587785252292473, -0.309016994374947, 0.951056516295154],
+   [-0.809016994374948, -0.587785252292473, -0.5, 0.866025403784439],
+   [-0.809016994374948, -0.587785252292473, -0.669130606358858, 0.743144825477394],
+   [-0.809016994374948, -0.587785252292473, -0.809016994374947, 0.587785252292473],
+   [-0.809016994374948, -0.587785252292473, -0.913545457642601, 0.4067366430758],
+   [-0.809016994374948, -0.587785252292473, -0.978147600733806, 0.207911690817759],
+   [-0.809016994374948, -0.587785252292473, -1, 5.665498452323e-16],
+   [-0.809016994374948, -0.587785252292473, -0.978147600733806, -0.207911690817759],
+   [-0.809016994374948, -0.587785252292473, -0.913545457642601, -0.4067366430758],
+   [-0.809016994374948, -0.587785252292473, -0.809016994374948, -0.587785252292473],
+   [-0.809016994374948, -0.587785252292473, -0.669130606358858, -0.743144825477394],
+   [-0.809016994374948, -0.587785252292473, -0.5, -0.866025403784438],
+   [-0.809016994374948, -0.587785252292473, -0.309016994374948, -0.951056516295154],
+   [-0.809016994374948, -0.587785252292473, -0.104528463267654, -0.994521895368273],
+   [-0.809016994374948, -0.587785252292473, 0.104528463267653, -0.994521895368273],
+   [-0.809016994374948, -0.587785252292473, 0.309016994374947, -0.951056516295154],
+   [-0.809016994374948, -0.587785252292473, 0.5, -0.866025403784439],
+   [-0.809016994374948, -0.587785252292473, 0.669130606358858, -0.743144825477394],
+   [-0.809016994374948, -0.587785252292473, 0.809016994374947, -0.587785252292473],
+   [-0.809016994374948, -0.587785252292473, 0.913545457642601, -0.4067366430758],
+   [-0.809016994374948, -0.587785252292473, 0.978147600733806, -0.207911690817759],
+   [-0.669130606358858, -0.743144825477394, 1, 0],
+   [-0.669130606358858, -0.743144825477394, 0.978147600733806, 0.207911690817759],
+   [-0.669130606358858, -0.743144825477394, 0.913545457642601, 0.4067366430758],
+   [-0.669130606358858, -0.743144825477394, 0.809016994374947, 0.587785252292473],
+   [-0.669130606358858, -0.743144825477394, 0.669130606358858, 0.743144825477394],
+   [-0.669130606358858, -0.743144825477394, 0.5, 0.866025403784439],
+   [-0.669130606358858, -0.743144825477394, 0.309016994374947, 0.951056516295154],
+   [-0.669130606358858, -0.743144825477394, 0.104528463267653, 0.994521895368273],
+   [-0.669130606358858, -0.743144825477394, -0.104528463267653, 0.994521895368273],
+   [-0.669130606358858, -0.743144825477394, -0.309016994374947, 0.951056516295154],
+   [-0.669130606358858, -0.743144825477394, -0.5, 0.866025403784439],
+   [-0.669130606358858, -0.743144825477394, -0.669130606358858, 0.743144825477394],
+   [-0.669130606358858, -0.743144825477394, -0.809016994374947, 0.587785252292473],
+   [-0.669130606358858, -0.743144825477394, -0.913545457642601, 0.4067366430758],
+   [-0.669130606358858, -0.743144825477394, -0.978147600733806, 0.207911690817759],
+   [-0.669130606358858, -0.743144825477394, -1, 5.665498452323e-16],
+   [-0.669130606358858, -0.743144825477394, -0.978147600733806, -0.207911690817759],
+   [-0.669130606358858, -0.743144825477394, -0.913545457642601, -0.4067366430758],
+   [-0.669130606358858, -0.743144825477394, -0.809016994374948, -0.587785252292473],
+   [-0.669130606358858, -0.743144825477394, -0.669130606358858, -0.743144825477394],
+   [-0.669130606358858, -0.743144825477394, -0.5, -0.866025403784438],
+   [-0.669130606358858, -0.743144825477394, -0.309016994374948, -0.951056516295154],
+   [-0.669130606358858, -0.743144825477394, -0.104528463267654, -0.994521895368273],
+   [-0.669130606358858, -0.743144825477394, 0.104528463267653, -0.994521895368273],
+   [-0.669130606358858, -0.743144825477394, 0.309016994374947, -0.951056516295154],
+   [-0.669130606358858, -0.743144825477394, 0.5, -0.866025403784439],
+   [-0.669130606358858, -0.743144825477394, 0.669130606358858, -0.743144825477394],
+   [-0.669130606358858, -0.743144825477394, 0.809016994374947, -0.587785252292473],
+   [-0.669130606358858, -0.743144825477394, 0.913545457642601, -0.4067366430758],
+   [-0.669130606358858, -0.743144825477394, 0.978147600733806, -0.207911690817759],
+   [-0.5, -0.866025403784438, 1, 0],
+   [-0.5, -0.866025403784438, 0.978147600733806, 0.207911690817759],
+   [-0.5, -0.866025403784438, 0.913545457642601, 0.4067366430758],
+   [-0.5, -0.866025403784438, 0.809016994374947, 0.587785252292473],
+   [-0.5, -0.866025403784438, 0.669130606358858, 0.743144825477394],
+   [-0.5, -0.866025403784438, 0.5, 0.866025403784439],
+   [-0.5, -0.866025403784438, 0.309016994374947, 0.951056516295154],
+   [-0.5, -0.866025403784438, 0.104528463267653, 0.994521895368273],
+   [-0.5, -0.866025403784438, -0.104528463267653, 0.994521895368273],
+   [-0.5, -0.866025403784438, -0.309016994374947, 0.951056516295154],
+   [-0.5, -0.866025403784438, -0.5, 0.866025403784439],
+   [-0.5, -0.866025403784438, -0.669130606358858, 0.743144825477394],
+   [-0.5, -0.866025403784438, -0.809016994374947, 0.587785252292473],
+   [-0.5, -0.866025403784438, -0.913545457642601, 0.4067366430758],
+   [-0.5, -0.866025403784438, -0.978147600733806, 0.207911690817759],
+   [-0.5, -0.866025403784438, -1, 5.665498452323e-16],
+   [-0.5, -0.866025403784438, -0.978147600733806, -0.207911690817759],
+   [-0.5, -0.866025403784438, -0.913545457642601, -0.4067366430758],
+   [-0.5, -0.866025403784438, -0.809016994374948, -0.587785252292473],
+   [-0.5, -0.866025403784438, -0.669130606358858, -0.743144825477394],
+   [-0.5, -0.866025403784438, -0.5, -0.866025403784438],
+   [-0.5, -0.866025403784438, -0.309016994374948, -0.951056516295154],
+   [-0.5, -0.866025403784438, -0.104528463267654, -0.994521895368273],
+   [-0.5, -0.866025403784438, 0.104528463267653, -0.994521895368273],
+   [-0.5, -0.866025403784438, 0.309016994374947, -0.951056516295154],
+   [-0.5, -0.866025403784438, 0.5, -0.866025403784439],
+   [-0.5, -0.866025403784438, 0.669130606358858, -0.743144825477394],
+   [-0.5, -0.866025403784438, 0.809016994374947, -0.587785252292473],
+   [-0.5, -0.866025403784438, 0.913545457642601, -0.4067366430758],
+   [-0.5, -0.866025403784438, 0.978147600733806, -0.207911690817759],
+   [-0.309016994374948, -0.951056516295154, 1, 0],
+   [-0.309016994374948, -0.951056516295154, 0.978147600733806, 0.207911690817759],
+   [-0.309016994374948, -0.951056516295154, 0.913545457642601, 0.4067366430758],
+   [-0.309016994374948, -0.951056516295154, 0.809016994374947, 0.587785252292473],
+   [-0.309016994374948, -0.951056516295154, 0.669130606358858, 0.743144825477394],
+   [-0.309016994374948, -0.951056516295154, 0.5, 0.866025403784439],
+   [-0.309016994374948, -0.951056516295154, 0.309016994374947, 0.951056516295154],
+   [-0.309016994374948, -0.951056516295154, 0.104528463267653, 0.994521895368273],
+   [-0.309016994374948, -0.951056516295154, -0.104528463267653, 0.994521895368273],
+   [-0.309016994374948, -0.951056516295154, -0.309016994374947, 0.951056516295154],
+   [-0.309016994374948, -0.951056516295154, -0.5, 0.866025403784439],
+   [-0.309016994374948, -0.951056516295154, -0.669130606358858, 0.743144825477394],
+   [-0.309016994374948, -0.951056516295154, -0.809016994374947, 0.587785252292473],
+   [-0.309016994374948, -0.951056516295154, -0.913545457642601, 0.4067366430758],
+   [-0.309016994374948, -0.951056516295154, -0.978147600733806, 0.207911690817759],
+   [-0.309016994374948, -0.951056516295154, -1, 5.665498452323e-16],
+   [-0.309016994374948, -0.951056516295154, -0.978147600733806, -0.207911690817759],
+   [-0.309016994374948, -0.951056516295154, -0.913545457642601, -0.4067366430758],
+   [-0.309016994374948, -0.951056516295154, -0.809016994374948, -0.587785252292473],
+   [-0.309016994374948, -0.951056516295154, -0.669130606358858, -0.743144825477394],
+   [-0.309016994374948, -0.951056516295154, -0.5, -0.866025403784438],
+   [-0.309016994374948, -0.951056516295154, -0.309016994374948, -0.951056516295154],
+   [-0.309016994374948, -0.951056516295154, -0.104528463267654, -0.994521895368273],
+   [-0.309016994374948, -0.951056516295154, 0.104528463267653, -0.994521895368273],
+   [-0.309016994374948, -0.951056516295154, 0.309016994374947, -0.951056516295154],
+   [-0.309016994374948, -0.951056516295154, 0.5, -0.866025403784439],
+   [-0.309016994374948, -0.951056516295154, 0.669130606358858, -0.743144825477394],
+   [-0.309016994374948, -0.951056516295154, 0.809016994374947, -0.587785252292473],
+   [-0.309016994374948, -0.951056516295154, 0.913545457642601, -0.4067366430758],
+   [-0.309016994374948, -0.951056516295154, 0.978147600733806, -0.207911690817759],
+   [-0.104528463267654, -0.994521895368273, 1, 0],
+   [-0.104528463267654, -0.994521895368273, 0.978147600733806, 0.207911690817759],
+   [-0.104528463267654, -0.994521895368273, 0.913545457642601, 0.4067366430758],
+   [-0.104528463267654, -0.994521895368273, 0.809016994374947, 0.587785252292473],
+   [-0.104528463267654, -0.994521895368273, 0.669130606358858, 0.743144825477394],
+   [-0.104528463267654, -0.994521895368273, 0.5, 0.866025403784439],
+   [-0.104528463267654, -0.994521895368273, 0.309016994374947, 0.951056516295154],
+   [-0.104528463267654, -0.994521895368273, 0.104528463267653, 0.994521895368273],
+   [-0.104528463267654, -0.994521895368273, -0.104528463267653, 0.994521895368273],
+   [-0.104528463267654, -0.994521895368273, -0.309016994374947, 0.951056516295154],
+   [-0.104528463267654, -0.994521895368273, -0.5, 0.866025403784439],
+   [-0.104528463267654, -0.994521895368273, -0.669130606358858, 0.743144825477394],
+   [-0.104528463267654, -0.994521895368273, -0.809016994374947, 0.587785252292473],
+   [-0.104528463267654, -0.994521895368273, -0.913545457642601, 0.4067366430758],
+   [-0.104528463267654, -0.994521895368273, -0.978147600733806, 0.207911690817759],
+   [-0.104528463267654, -0.994521895368273, -1, 5.665498452323e-16],
+   [-0.104528463267654, -0.994521895368273, -0.978147600733806, -0.207911690817759],
+   [-0.104528463267654, -0.994521895368273, -0.913545457642601, -0.4067366430758],
+   [-0.104528463267654, -0.994521895368273, -0.809016994374948, -0.587785252292473],
+   [-0.104528463267654, -0.994521895368273, -0.669130606358858, -0.743144825477394],
+   [-0.104528463267654, -0.994521895368273, -0.5, -0.866025403784438],
+   [-0.104528463267654, -0.994521895368273, -0.309016994374948, -0.951056516295154],
+   [-0.104528463267654, -0.994521895368273, -0.104528463267654, -0.994521895368273],
+   [-0.104528463267654, -0.994521895368273, 0.104528463267653, -0.994521895368273],
+   [-0.104528463267654, -0.994521895368273, 0.309016994374947, -0.951056516295154],
+   [-0.104528463267654, -0.994521895368273, 0.5, -0.866025403784439],
+   [-0.104528463267654, -0.994521895368273, 0.669130606358858, -0.743144825477394],
+   [-0.104528463267654, -0.994521895368273, 0.809016994374947, -0.587785252292473],
+   [-0.104528463267654, -0.994521895368273, 0.913545457642601, -0.4067366430758],
+   [-0.104528463267654, -0.994521895368273, 0.978147600733806, -0.207911690817759],
+   [0.104528463267653, -0.994521895368273, 1, 0],
+   [0.104528463267653, -0.994521895368273, 0.978147600733806, 0.207911690817759],
+   [0.104528463267653, -0.994521895368273, 0.913545457642601, 0.4067366430758],
+   [0.104528463267653, -0.994521895368273, 0.809016994374947, 0.587785252292473],
+   [0.104528463267653, -0.994521895368273, 0.669130606358858, 0.743144825477394],
+   [0.104528463267653, -0.994521895368273, 0.5, 0.866025403784439],
+   [0.104528463267653, -0.994521895368273, 0.309016994374947, 0.951056516295154],
+   [0.104528463267653, -0.994521895368273, 0.104528463267653, 0.994521895368273],
+   [0.104528463267653, -0.994521895368273, -0.104528463267653, 0.994521895368273],
+   [0.104528463267653, -0.994521895368273, -0.309016994374947, 0.951056516295154],
+   [0.104528463267653, -0.994521895368273, -0.5, 0.866025403784439],
+   [0.104528463267653, -0.994521895368273, -0.669130606358858, 0.743144825477394],
+   [0.104528463267653, -0.994521895368273, -0.809016994374947, 0.587785252292473],
+   [0.104528463267653, -0.994521895368273, -0.913545457642601, 0.4067366430758],
+   [0.104528463267653, -0.994521895368273, -0.978147600733806, 0.207911690817759],
+   [0.104528463267653, -0.994521895368273, -1, 5.665498452323e-16],
+   [0.104528463267653, -0.994521895368273, -0.978147600733806, -0.207911690817759],
+   [0.104528463267653, -0.994521895368273, -0.913545457642601, -0.4067366430758],
+   [0.104528463267653, -0.994521895368273, -0.809016994374948, -0.587785252292473],
+   [0.104528463267653, -0.994521895368273, -0.669130606358858, -0.743144825477394],
+   [0.104528463267653, -0.994521895368273, -0.5, -0.866025403784438],
+   [0.104528463267653, -0.994521895368273, -0.309016994374948, -0.951056516295154],
+   [0.104528463267653, -0.994521895368273, -0.104528463267654, -0.994521895368273],
+   [0.104528463267653, -0.994521895368273, 0.104528463267653, -0.994521895368273],
+   [0.104528463267653, -0.994521895368273, 0.309016994374947, -0.951056516295154],
+   [0.104528463267653, -0.994521895368273, 0.5, -0.866025403784439],
+   [0.104528463267653, -0.994521895368273, 0.669130606358858, -0.743144825477394],
+   [0.104528463267653, -0.994521895368273, 0.809016994374947, -0.587785252292473],
+   [0.104528463267653, -0.994521895368273, 0.913545457642601, -0.4067366430758],
+   [0.104528463267653, -0.994521895368273, 0.978147600733806, -0.207911690817759],
+   [0.309016994374947, -0.951056516295154, 1, 0],
+   [0.309016994374947, -0.951056516295154, 0.978147600733806, 0.207911690817759],
+   [0.309016994374947, -0.951056516295154, 0.913545457642601, 0.4067366430758],
+   [0.309016994374947, -0.951056516295154, 0.809016994374947, 0.587785252292473],
+   [0.309016994374947, -0.951056516295154, 0.669130606358858, 0.743144825477394],
+   [0.309016994374947, -0.951056516295154, 0.5, 0.866025403784439],
+   [0.309016994374947, -0.951056516295154, 0.309016994374947, 0.951056516295154],
+   [0.309016994374947, -0.951056516295154, 0.104528463267653, 0.994521895368273],
+   [0.309016994374947, -0.951056516295154, -0.104528463267653, 0.994521895368273],
+   [0.309016994374947, -0.951056516295154, -0.309016994374947, 0.951056516295154],
+   [0.309016994374947, -0.951056516295154, -0.5, 0.866025403784439],
+   [0.309016994374947, -0.951056516295154, -0.669130606358858, 0.743144825477394],
+   [0.309016994374947, -0.951056516295154, -0.809016994374947, 0.587785252292473],
+   [0.309016994374947, -0.951056516295154, -0.913545457642601, 0.4067366430758],
+   [0.309016994374947, -0.951056516295154, -0.978147600733806, 0.207911690817759],
+   [0.309016994374947, -0.951056516295154, -1, 5.665498452323e-16],
+   [0.309016994374947, -0.951056516295154, -0.978147600733806, -0.207911690817759],
+   [0.309016994374947, -0.951056516295154, -0.913545457642601, -0.4067366430758],
+   [0.309016994374947, -0.951056516295154, -0.809016994374948, -0.587785252292473],
+   [0.309016994374947, -0.951056516295154, -0.669130606358858, -0.743144825477394],
+   [0.309016994374947, -0.951056516295154, -0.5, -0.866025403784438],
+   [0.309016994374947, -0.951056516295154, -0.309016994374948, -0.951056516295154],
+   [0.309016994374947, -0.951056516295154, -0.104528463267654, -0.994521895368273],
+   [0.309016994374947, -0.951056516295154, 0.104528463267653, -0.994521895368273],
+   [0.309016994374947, -0.951056516295154, 0.309016994374947, -0.951056516295154],
+   [0.309016994374947, -0.951056516295154, 0.5, -0.866025403784439],
+   [0.309016994374947, -0.951056516295154, 0.669130606358858, -0.743144825477394],
+   [0.309016994374947, -0.951056516295154, 0.809016994374947, -0.587785252292473],
+   [0.309016994374947, -0.951056516295154, 0.913545457642601, -0.4067366430758],
+   [0.309016994374947, -0.951056516295154, 0.978147600733806, -0.207911690817759],
+   [0.5, -0.866025403784439, 1, 0],
+   [0.5, -0.866025403784439, 0.978147600733806, 0.207911690817759],
+   [0.5, -0.866025403784439, 0.913545457642601, 0.4067366430758],
+   [0.5, -0.866025403784439, 0.809016994374947, 0.587785252292473],
+   [0.5, -0.866025403784439, 0.669130606358858, 0.743144825477394],
+   [0.5, -0.866025403784439, 0.5, 0.866025403784439],
+   [0.5, -0.866025403784439, 0.309016994374947, 0.951056516295154],
+   [0.5, -0.866025403784439, 0.104528463267653, 0.994521895368273],
+   [0.5, -0.866025403784439, -0.104528463267653, 0.994521895368273],
+   [0.5, -0.866025403784439, -0.309016994374947, 0.951056516295154],
+   [0.5, -0.866025403784439, -0.5, 0.866025403784439],
+   [0.5, -0.866025403784439, -0.669130606358858, 0.743144825477394],
+   [0.5, -0.866025403784439, -0.809016994374947, 0.587785252292473],
+   [0.5, -0.866025403784439, -0.913545457642601, 0.4067366430758],
+   [0.5, -0.866025403784439, -0.978147600733806, 0.207911690817759],
+   [0.5, -0.866025403784439, -1, 5.665498452323e-16],
+   [0.5, -0.866025403784439, -0.978147600733806, -0.207911690817759],
+   [0.5, -0.866025403784439, -0.913545457642601, -0.4067366430758],
+   [0.5, -0.866025403784439, -0.809016994374948, -0.587785252292473],
+   [0.5, -0.866025403784439, -0.669130606358858, -0.743144825477394],
+   [0.5, -0.866025403784439, -0.5, -0.866025403784438],
+   [0.5, -0.866025403784439, -0.309016994374948, -0.951056516295154],
+   [0.5, -0.866025403784439, -0.104528463267654, -0.994521895368273],
+   [0.5, -0.866025403784439, 0.104528463267653, -0.994521895368273],
+   [0.5, -0.866025403784439, 0.309016994374947, -0.951056516295154],
+   [0.5, -0.866025403784439, 0.5, -0.866025403784439],
+   [0.5, -0.866025403784439, 0.669130606358858, -0.743144825477394],
+   [0.5, -0.866025403784439, 0.809016994374947, -0.587785252292473],
+   [0.5, -0.866025403784439, 0.913545457642601, -0.4067366430758],
+   [0.5, -0.866025403784439, 0.978147600733806, -0.207911690817759],
+   [0.669130606358858, -0.743144825477394, 1, 0],
+   [0.669130606358858, -0.743144825477394, 0.978147600733806, 0.207911690817759],
+   [0.669130606358858, -0.743144825477394, 0.913545457642601, 0.4067366430758],
+   [0.669130606358858, -0.743144825477394, 0.809016994374947, 0.587785252292473],
+   [0.669130606358858, -0.743144825477394, 0.669130606358858, 0.743144825477394],
+   [0.669130606358858, -0.743144825477394, 0.5, 0.866025403784439],
+   [0.669130606358858, -0.743144825477394, 0.309016994374947, 0.951056516295154],
+   [0.669130606358858, -0.743144825477394, 0.104528463267653, 0.994521895368273],
+   [0.669130606358858, -0.743144825477394, -0.104528463267653, 0.994521895368273],
+   [0.669130606358858, -0.743144825477394, -0.309016994374947, 0.951056516295154],
+   [0.669130606358858, -0.743144825477394, -0.5, 0.866025403784439],
+   [0.669130606358858, -0.743144825477394, -0.669130606358858, 0.743144825477394],
+   [0.669130606358858, -0.743144825477394, -0.809016994374947, 0.587785252292473],
+   [0.669130606358858, -0.743144825477394, -0.913545457642601, 0.4067366430758],
+   [0.669130606358858, -0.743144825477394, -0.978147600733806, 0.207911690817759],
+   [0.669130606358858, -0.743144825477394, -1, 5.665498452323e-16],
+   [0.669130606358858, -0.743144825477394, -0.978147600733806, -0.207911690817759],
+   [0.669130606358858, -0.743144825477394, -0.913545457642601, -0.4067366430758],
+   [0.669130606358858, -0.743144825477394, -0.809016994374948, -0.587785252292473],
+   [0.669130606358858, -0.743144825477394, -0.669130606358858, -0.743144825477394],
+   [0.669130606358858, -0.743144825477394, -0.5, -0.866025403784438],
+   [0.669130606358858, -0.743144825477394, -0.309016994374948, -0.951056516295154],
+   [0.669130606358858, -0.743144825477394, -0.104528463267654, -0.994521895368273],
+   [0.669130606358858, -0.743144825477394, 0.104528463267653, -0.994521895368273],
+   [0.669130606358858, -0.743144825477394, 0.309016994374947, -0.951056516295154],
+   [0.669130606358858, -0.743144825477394, 0.5, -0.866025403784439],
+   [0.669130606358858, -0.743144825477394, 0.669130606358858, -0.743144825477394],
+   [0.669130606358858, -0.743144825477394, 0.809016994374947, -0.587785252292473],
+   [0.669130606358858, -0.743144825477394, 0.913545457642601, -0.4067366430758],
+   [0.669130606358858, -0.743144825477394, 0.978147600733806, -0.207911690817759],
+   [0.809016994374947, -0.587785252292473, 1, 0],
+   [0.809016994374947, -0.587785252292473, 0.978147600733806, 0.207911690817759],
+   [0.809016994374947, -0.587785252292473, 0.913545457642601, 0.4067366430758],
+   [0.809016994374947, -0.587785252292473, 0.809016994374947, 0.587785252292473],
+   [0.809016994374947, -0.587785252292473, 0.669130606358858, 0.743144825477394],
+   [0.809016994374947, -0.587785252292473, 0.5, 0.866025403784439],
+   [0.809016994374947, -0.587785252292473, 0.309016994374947, 0.951056516295154],
+   [0.809016994374947, -0.587785252292473, 0.104528463267653, 0.994521895368273],
+   [0.809016994374947, -0.587785252292473, -0.104528463267653, 0.994521895368273],
+   [0.809016994374947, -0.587785252292473, -0.309016994374947, 0.951056516295154],
+   [0.809016994374947, -0.587785252292473, -0.5, 0.866025403784439],
+   [0.809016994374947, -0.587785252292473, -0.669130606358858, 0.743144825477394],
+   [0.809016994374947, -0.587785252292473, -0.809016994374947, 0.587785252292473],
+   [0.809016994374947, -0.587785252292473, -0.913545457642601, 0.4067366430758],
+   [0.809016994374947, -0.587785252292473, -0.978147600733806, 0.207911690817759],
+   [0.809016994374947, -0.587785252292473, -1, 5.665498452323e-16],
+   [0.809016994374947, -0.587785252292473, -0.978147600733806, -0.207911690817759],
+   [0.809016994374947, -0.587785252292473, -0.913545457642601, -0.4067366430758],
+   [0.809016994374947, -0.587785252292473, -0.809016994374948, -0.587785252292473],
+   [0.809016994374947, -0.587785252292473, -0.669130606358858, -0.743144825477394],
+   [0.809016994374947, -0.587785252292473, -0.5, -0.866025403784438],
+   [0.809016994374947, -0.587785252292473, -0.309016994374948, -0.951056516295154],
+   [0.809016994374947, -0.587785252292473, -0.104528463267654, -0.994521895368273],
+   [0.809016994374947, -0.587785252292473, 0.104528463267653, -0.994521895368273],
+   [0.809016994374947, -0.587785252292473, 0.309016994374947, -0.951056516295154],
+   [0.809016994374947, -0.587785252292473, 0.5, -0.866025403784439],
+   [0.809016994374947, -0.587785252292473, 0.669130606358858, -0.743144825477394],
+   [0.809016994374947, -0.587785252292473, 0.809016994374947, -0.587785252292473],
+   [0.809016994374947, -0.587785252292473, 0.913545457642601, -0.4067366430758],
+   [0.809016994374947, -0.587785252292473, 0.978147600733806, -0.207911690817759],
+   [0.913545457642601, -0.4067366430758, 1, 0],
+   [0.913545457642601, -0.4067366430758, 0.978147600733806, 0.207911690817759],
+   [0.913545457642601, -0.4067366430758, 0.913545457642601, 0.4067366430758],
+   [0.913545457642601, -0.4067366430758, 0.809016994374947, 0.587785252292473],
+   [0.913545457642601, -0.4067366430758, 0.669130606358858, 0.743144825477394],
+   [0.913545457642601, -0.4067366430758, 0.5, 0.866025403784439],
+   [0.913545457642601, -0.4067366430758, 0.309016994374947, 0.951056516295154],
+   [0.913545457642601, -0.4067366430758, 0.104528463267653, 0.994521895368273],
+   [0.913545457642601, -0.4067366430758, -0.104528463267653, 0.994521895368273],
+   [0.913545457642601, -0.4067366430758, -0.309016994374947, 0.951056516295154],
+   [0.913545457642601, -0.4067366430758, -0.5, 0.866025403784439],
+   [0.913545457642601, -0.4067366430758, -0.669130606358858, 0.743144825477394],
+   [0.913545457642601, -0.4067366430758, -0.809016994374947, 0.587785252292473],
+   [0.913545457642601, -0.4067366430758, -0.913545457642601, 0.4067366430758],
+   [0.913545457642601, -0.4067366430758, -0.978147600733806, 0.207911690817759],
+   [0.913545457642601, -0.4067366430758, -1, 5.665498452323e-16],
+   [0.913545457642601, -0.4067366430758, -0.978147600733806, -0.207911690817759],
+   [0.913545457642601, -0.4067366430758, -0.913545457642601, -0.4067366430758],
+   [0.913545457642601, -0.4067366430758, -0.809016994374948, -0.587785252292473],
+   [0.913545457642601, -0.4067366430758, -0.669130606358858, -0.743144825477394],
+   [0.913545457642601, -0.4067366430758, -0.5, -0.866025403784438],
+   [0.913545457642601, -0.4067366430758, -0.309016994374948, -0.951056516295154],
+   [0.913545457642601, -0.4067366430758, -0.104528463267654, -0.994521895368273],
+   [0.913545457642601, -0.4067366430758, 0.104528463267653, -0.994521895368273],
+   [0.913545457642601, -0.4067366430758, 0.309016994374947, -0.951056516295154],
+   [0.913545457642601, -0.4067366430758, 0.5, -0.866025403784439],
+   [0.913545457642601, -0.4067366430758, 0.669130606358858, -0.743144825477394],
+   [0.913545457642601, -0.4067366430758, 0.809016994374947, -0.587785252292473],
+   [0.913545457642601, -0.4067366430758, 0.913545457642601, -0.4067366430758],
+   [0.913545457642601, -0.4067366430758, 0.978147600733806, -0.207911690817759],
+   [0.978147600733806, -0.207911690817759, 1, 0],
+   [0.978147600733806, -0.207911690817759, 0.978147600733806, 0.207911690817759],
+   [0.978147600733806, -0.207911690817759, 0.913545457642601, 0.4067366430758],
+   [0.978147600733806, -0.207911690817759, 0.809016994374947, 0.587785252292473],
+   [0.978147600733806, -0.207911690817759, 0.669130606358858, 0.743144825477394],
+   [0.978147600733806, -0.207911690817759, 0.5, 0.866025403784439],
+   [0.978147600733806, -0.207911690817759, 0.309016994374947, 0.951056516295154],
+   [0.978147600733806, -0.207911690817759, 0.104528463267653, 0.994521895368273],
+   [0.978147600733806, -0.207911690817759, -0.104528463267653, 0.994521895368273],
+   [0.978147600733806, -0.207911690817759, -0.309016994374947, 0.951056516295154],
+   [0.978147600733806, -0.207911690817759, -0.5, 0.866025403784439],
+   [0.978147600733806, -0.207911690817759, -0.669130606358858, 0.743144825477394],
+   [0.978147600733806, -0.207911690817759, -0.809016994374947, 0.587785252292473],
+   [0.978147600733806, -0.207911690817759, -0.913545457642601, 0.4067366430758],
+   [0.978147600733806, -0.207911690817759, -0.978147600733806, 0.207911690817759],
+   [0.978147600733806, -0.207911690817759, -1, 5.665498452323e-16],
+   [0.978147600733806, -0.207911690817759, -0.978147600733806, -0.207911690817759],
+   [0.978147600733806, -0.207911690817759, -0.913545457642601, -0.4067366430758],
+   [0.978147600733806, -0.207911690817759, -0.809016994374948, -0.587785252292473],
+   [0.978147600733806, -0.207911690817759, -0.669130606358858, -0.743144825477394],
+   [0.978147600733806, -0.207911690817759, -0.5, -0.866025403784438],
+   [0.978147600733806, -0.207911690817759, -0.309016994374948, -0.951056516295154],
+   [0.978147600733806, -0.207911690817759, -0.104528463267654, -0.994521895368273],
+   [0.978147600733806, -0.207911690817759, 0.104528463267653, -0.994521895368273],
+   [0.978147600733806, -0.207911690817759, 0.309016994374947, -0.951056516295154],
+   [0.978147600733806, -0.207911690817759, 0.5, -0.866025403784439],
+   [0.978147600733806, -0.207911690817759, 0.669130606358858, -0.743144825477394],
+   [0.978147600733806, -0.207911690817759, 0.809016994374947, -0.587785252292473],
+   [0.978147600733806, -0.207911690817759, 0.913545457642601, -0.4067366430758],
+   [0.978147600733806, -0.207911690817759, 0.978147600733806, -0.207911690817759]
+  ]
diff --git a/src/Delaunay/R.hs b/src/Delaunay/R.hs
new file mode 100644
--- /dev/null
+++ b/src/Delaunay/R.hs
@@ -0,0 +1,98 @@
+module Delaunay.R
+  where
+-- import qualified Data.HashMap.Strict.InsOrd as H
+import qualified Data.IntMap.Strict         as IM
+import qualified Data.IntSet                as IS
+import           Data.List
+import           Data.List.Index            (iconcatMap)
+-- import           Data.Maybe
+import           Delaunay
+import           Text.Printf
+
+-- | R code to plot a 2D Delaunay tesselation
+delaunay2ForR :: Tesselation -> Bool -> String
+delaunay2ForR tess colors =
+  let tiles = IM.elems (_tiles tess) in
+  "plot(0, 0, type=\"n\", xlim=c(0,5), ylim=c(0,5)) # please set the limits\n" ++
+  (if colors
+    then printf "colors <- heat.colors(%d, alpha=0.5)\n" (length tiles + 1)
+    else "\n") ++
+  concatMap triangle (zip [1 .. length tiles] tiles)
+  where
+    triangle :: (Int, Tile) -> String
+    triangle (i, tile) =
+      let pts = map (\p -> [p!!0,p!!1,p!!2]) (verticesCoordinates tile)
+      in
+      printf "polygon(c(%f,%f,%f), c(%f,%f,%f), border=\"black\", "
+             (pts!!0!!0) (pts!!1!!0) (pts!!2!!0)
+             (pts!!0!!1) (pts!!1!!1) (pts!!2!!1) ++
+      -- "polygon(c(" ++ show (pts!!0!!0) ++ ", " ++ show (pts!!1!!0) ++
+      --                 ", " ++ show (pts!!2!!0) ++ "), "
+      --              ++ "c(" ++ show (pts!!0!!1) ++ ", " ++ show (pts!!1!!1) ++
+      --                 ", " ++ show (pts!!2!!1) ++
+      --              "), border=\"black\", " ++
+      (if colors
+        then printf "col=colors[%d])\n" i
+        else "col=\"lightblue\")\n")
+
+-- | R code to plot a 3D Delaunay tesselation
+delaunay3rgl :: Tesselation -> Bool -> Bool -> Bool -> Bool -> Maybe Double -> String
+delaunay3rgl tess interior exterior segments colors alpha =
+  let allridges = IM.elems (_tilefacets tess) in
+  let ridges | exterior && interior = allridges
+             | interior = filter sandwichedFacet allridges
+             | exterior = filter (not . sandwichedFacet) allridges
+  in
+  "library(rgl)\n" ++
+  (if colors
+    then printf "colors <- topo.colors(%d, alpha=0.5)\n" (length ridges + 1)
+    else "\n") ++
+  concatMap rglRidge ridges ++
+  (if segments
+    then if exterior && interior
+      then concatMap rglSegment (edgesCoordinates tess)
+      else concatMap rglSegment (tilefacetsEdges ridges)
+    else "")
+  where
+    rglRidge :: TileFacet -> String
+    rglRidge ridge =
+      let i = 1 + head (IS.elems $ _facetOf ridge) in
+      printf "\ntriangles3d(rbind(c%s,c%s,c%s"
+             (show (pts!!0)) (show (pts!!1)) (show (pts!!2)) ++
+      (if colors
+        then
+          printf "), color=colors[%d]" i
+        else
+          "), color=\"blue\"") ++
+      maybe ")\n" (printf "alpha=%f)\n") alpha
+      where
+        pts = map (\p -> (p!!0,p!!1,p!!2)) (verticesCoordinates ridge)
+    rglSegment :: ([Double], [Double]) -> String
+    rglSegment (p1, p2) =
+      printf "segments3d(rbind(c%s,c%s), color=\"black\")\n"
+             (show p1') (show p2')
+      where
+        p1' = (p1!!0, p1!!1, p1!!2)
+        p2' = (p2!!0, p2!!1, p2!!2)
+    tilefacetsEdges :: [TileFacet] -> [([Double], [Double])]
+    tilefacetsEdges tilefacets = foldl' union [] (map tilefacetEdges tilefacets)
+      where
+        tilefacetEdges :: TileFacet -> [([Double], [Double])]
+        tilefacetEdges tilefacet = [(v!!0,v!!1),(v!!1,v!!2),(v!!2,v!!0)]
+          where
+            v = verticesCoordinates tilefacet
+
+delaunaySpheres :: Tesselation -> String
+delaunaySpheres tess =
+  let tiles = IM.elems (_tiles tess) in
+  "library(rgl)\n" ++
+  printf "colors <- rainbow(%d)\n" (length tiles) ++
+  iconcatMap rglSphere tiles
+  where
+    rglSphere :: Int -> Tile -> String
+    rglSphere i tile =
+      printf "spheres3d(%f, %f, %f, radius=%f, color=colors[%d], alpha=0.75)\n"
+             (c!!0) (c!!1) (c!!2) r (i+1)
+      where
+        c = _center tile
+        r = _circumradius (_simplex tile)
diff --git a/src/Delaunay/Types.hs b/src/Delaunay/Types.hs
new file mode 100644
--- /dev/null
+++ b/src/Delaunay/Types.hs
@@ -0,0 +1,85 @@
+module Delaunay.Types
+  where
+import           Data.IntMap.Strict (IntMap)
+import qualified Data.IntMap.Strict as IM
+import           Data.IntSet        (IntSet)
+import           Qhull.Types
+
+data Site = Site {
+    _point          :: [Double]
+  , _neighsitesIds  :: IndexSet
+  , _neighfacetsIds :: IntSet
+  , _neightilesIds  :: IntSet
+} deriving Show
+
+data Simplex = Simplex {
+    _vertices'    :: IndexMap [Double]
+  , _circumcenter :: [Double]
+  , _circumradius :: Double
+  , _volume'      :: Double
+} deriving Show
+
+instance HasCenter Simplex where
+  _center = _circumcenter
+
+instance HasVertices Simplex where
+  _vertices = _vertices'
+
+instance HasVolume Simplex where
+  _volume = _volume'
+
+data TileFacet = TileFacet {
+    _subsimplex :: Simplex
+  , _facetOf    :: IntSet
+  , _normal'    :: [Double]
+  , _offset'    :: Double
+} deriving Show
+
+instance HasNormal TileFacet where
+  _normal = _normal'
+  _offset = _offset'
+
+instance HasVertices TileFacet where
+  _vertices = _vertices' . _subsimplex
+
+instance HasVolume TileFacet where
+  _volume = _volume' . _subsimplex
+
+instance HasCenter TileFacet where
+  _center = _circumcenter . _subsimplex
+
+data Tile = Tile {
+    _simplex      :: Simplex
+  , _neighborsIds :: IntSet
+  , _facetsIds    :: IntSet
+  , _family'      :: Family
+  , _toporiented  :: Bool
+} deriving Show
+
+instance HasFamily Tile where
+  _family = _family'
+
+instance HasVertices Tile where
+  _vertices = _vertices' . _simplex
+
+instance HasVolume Tile where
+  _volume = _volume' . _simplex
+
+instance HasCenter Tile where
+  _center = _circumcenter . _simplex
+
+data Tesselation = Tesselation {
+    _sites      :: IndexMap Site
+  , _tiles      :: IntMap Tile
+  , _tilefacets :: IntMap TileFacet
+  , _edges'     :: EdgeMap
+} deriving Show
+
+instance HasEdges Tesselation where
+  _edges = _edges'
+
+instance HasVertices Tesselation where
+  _vertices tess = IM.map _point (_sites tess)
+
+instance HasVolume Tesselation where
+  _volume tess = sum (IM.elems $ IM.map (_volume' . _simplex) (_tiles tess))
diff --git a/src/HalfSpaces.hs b/src/HalfSpaces.hs
new file mode 100644
--- /dev/null
+++ b/src/HalfSpaces.hs
@@ -0,0 +1,7 @@
+module HalfSpaces
+  (module X)
+  where
+import           HalfSpaces.Constraint        as X
+import           HalfSpaces.Examples          as X
+import           HalfSpaces.HalfSpaces        as X
+import           HalfSpaces.LinearCombination as X 
diff --git a/src/HalfSpaces/CHalfSpaces.hs b/src/HalfSpaces/CHalfSpaces.hs
new file mode 100644
--- /dev/null
+++ b/src/HalfSpaces/CHalfSpaces.hs
@@ -0,0 +1,15 @@
+{-# LANGUAGE ForeignFunctionInterface #-}
+module HalfSpaces.CHalfSpaces
+  where
+import           Foreign
+import           Foreign.C.Types
+
+foreign import ccall unsafe "intersections" c_intersections
+  :: Ptr CDouble  -- halfspaces
+  -> Ptr CDouble  -- interior point
+  -> CUInt        -- dim
+  -> CUInt        -- n halfspaces
+  -> Ptr CUInt    -- n intersections
+  -> Ptr CUInt    -- exitcode
+  -> CUInt        -- 0/1 print to stdout
+  -> IO (Ptr (Ptr CDouble))
diff --git a/src/HalfSpaces/Constraint.hs b/src/HalfSpaces/Constraint.hs
new file mode 100644
--- /dev/null
+++ b/src/HalfSpaces/Constraint.hs
@@ -0,0 +1,28 @@
+module HalfSpaces.Constraint
+  where
+-- import           Data.Ratio                   (Rational)#
+import           HalfSpaces.LinearCombination (LinearCombination, constant)
+
+data Sense = Gt | Lt
+  deriving Eq
+
+instance Show Sense where
+  show Gt = ">="
+  show Lt = "<="
+
+data Constraint = Constraint LinearCombination Sense LinearCombination
+  deriving (Eq, Show)
+
+(.>=.) :: LinearCombination -> LinearCombination -> Constraint
+(.>=.) lhs rhs = Constraint lhs Gt rhs
+
+(.<=.) :: LinearCombination -> LinearCombination -> Constraint
+(.<=.) lhs rhs = Constraint lhs Lt rhs
+
+(.>=) :: LinearCombination -> Rational -> Constraint
+(.>=) lhs x = (.>=.) lhs (constant x)
+
+(.<=) :: LinearCombination -> Rational -> Constraint
+(.<=) lhs x = (.<=.) lhs (constant x)
+
+infix 4 .<=., .>=.
diff --git a/src/HalfSpaces/Examples.hs b/src/HalfSpaces/Examples.hs
new file mode 100644
--- /dev/null
+++ b/src/HalfSpaces/Examples.hs
@@ -0,0 +1,53 @@
+module HalfSpaces.Examples
+  where
+import           Data.Ratio                   ((%))
+import           Data.VectorSpace
+import           HalfSpaces.Constraint        (Constraint (..), (.<=), (.<=.),
+                                               (.>=), (.>=.))
+import           HalfSpaces.LinearCombination (constant, cst, linearCombination,
+                                               newVar)
+
+testSmall :: [Constraint]
+testSmall = [ x .<= 1, x .>= 0, y .<= 1]
+  where
+    x = newVar 1
+    y = newVar 2
+
+rggConstraints :: [Constraint]
+rggConstraints =
+  [ x .>= (-5)
+  , x .<=  4
+  , y .>= (-5)
+  , y .<=. cst 3 ^-^ x
+  , z .>= (-10)
+  , z .<=. cst 6 ^-^ x ^-^ y ]
+  where
+    x = newVar 1
+    y = newVar 2
+    z = newVar 3
+
+region3D :: [Constraint]
+region3D =
+  [ x .>=  0 -- shortcut for x .>=. cst 0
+  , x .<=  3
+  , y .>=  0
+  , y .<=. cst 2 ^-^ (2%3)*^x
+  , z .>=  0
+  , z .<=. cst 6 ^-^ 2*^x ^-^ 3*^y ]
+  where
+    x = newVar 1
+    y = newVar 2
+    z = newVar 3
+
+cubeConstraints :: [Constraint]
+cubeConstraints =
+  [ x .<= 1
+  , x .>= (-1)
+  , y .<= 1
+  , y .>= (-1)
+  , z .<= 1
+  , z .>= (-1) ]
+  where
+    x = newVar 1
+    y = newVar 2
+    z = newVar 3
diff --git a/src/HalfSpaces/HalfSpaces.hs b/src/HalfSpaces/HalfSpaces.hs
new file mode 100644
--- /dev/null
+++ b/src/HalfSpaces/HalfSpaces.hs
@@ -0,0 +1,64 @@
+module HalfSpaces.HalfSpaces
+  where
+import           Control.Monad          (unless, when, (<$!>), (=<<))
+import           Foreign.C.Types
+import           Foreign.Marshal.Alloc  (free, mallocBytes)
+import           Foreign.Marshal.Array  (peekArray, pokeArray)
+import           Foreign.Storable       (peek, sizeOf)
+import           HalfSpaces.CHalfSpaces
+import           HalfSpaces.Constraint  (Constraint)
+import           HalfSpaces.Internal    (normalizeConstraints)
+import           HalfSpaces.ToySolver
+
+hsintersections' :: [[Double]]     -- halfspaces
+                 -> [Double]       -- interior point
+                 -> Bool           -- print to stdout
+                 -> IO [[Double]]
+hsintersections' halfspaces ipoint stdout = do
+  let n     = length halfspaces
+      dim   = length ipoint
+  unless (all (== dim+1) (map length halfspaces)) $
+    error "the points must have the same dimension"
+  when (dim < 2) $
+    error "dimension must be at least 2"
+  when (n <= dim) $
+    error "insufficient number of halfspaces"
+  hsPtr <- mallocBytes (n * (dim+1) * sizeOf (undefined :: CDouble))
+  pokeArray hsPtr (concatMap (map realToFrac) halfspaces)
+  ipointPtr <- mallocBytes (dim * sizeOf (undefined :: CDouble))
+  pokeArray ipointPtr (map realToFrac ipoint)
+  exitcodePtr <- mallocBytes (sizeOf (undefined :: CUInt))
+  nintersectionsPtr <- mallocBytes (sizeOf (undefined :: CUInt))
+  resultPtr <- c_intersections hsPtr ipointPtr
+               (fromIntegral dim) (fromIntegral n)
+               nintersectionsPtr exitcodePtr (fromIntegral $ fromEnum stdout)
+  exitcode <- peek exitcodePtr
+  free exitcodePtr
+  free hsPtr
+  if exitcode /= 0
+    then do
+      free resultPtr
+      free nintersectionsPtr
+      error $ "qhull returned an error (code " ++ show exitcode ++ ")"
+    else do
+      nintersections <- (<$!>) fromIntegral (peek nintersectionsPtr)
+      result <- (<$!>) (map (map realToFrac))
+                       ((=<<) (mapM (peekArray dim))
+                             (peekArray nintersections resultPtr))
+      free resultPtr
+      free nintersectionsPtr
+      return result
+
+hsintersections :: [Constraint] -> Bool -> IO [[Double]]
+hsintersections constraints stdout = do
+  let halfspacesMatrix = normalizeConstraints constraints
+  ipoint <- (<$!>) (map realToFrac) (interiorPoint constraints)
+  hsintersections' halfspacesMatrix ipoint stdout
+
+cubeConstraints' :: [[Double]]
+cubeConstraints' = [[ 1, 0, 0,-1]
+                   ,[-1, 0, 0,-1]
+                   ,[ 0, 1, 0,-1]
+                   ,[ 0,-1, 0,-1]
+                   ,[ 0, 0, 1,-1]
+                   ,[ 0, 0,-1,-1]]
diff --git a/src/HalfSpaces/Internal.hs b/src/HalfSpaces/Internal.hs
new file mode 100644
--- /dev/null
+++ b/src/HalfSpaces/Internal.hs
@@ -0,0 +1,46 @@
+module HalfSpaces.Internal
+  (normalizeConstraints, varsOfConstraint)
+  where
+import           Data.IntMap.Strict           (IntMap, mergeWithKey)
+import qualified Data.IntMap.Strict           as IM
+import           Data.List                    (nub, union)
+import           Data.Ratio
+import           HalfSpaces.Constraint        (Constraint (..), Sense (..))
+import           HalfSpaces.LinearCombination (LinearCombination (..), VarIndex)
+
+normalizeLinearCombination :: [VarIndex] -> LinearCombination -> IntMap Rational
+normalizeLinearCombination vars (LinearCombination lc) =
+  IM.union lc (IM.fromList [(i,0) | i <- vars `union` [0]])
+
+varsOfLinearCombo :: LinearCombination -> [VarIndex]
+varsOfLinearCombo (LinearCombination imap) = IM.keys imap
+
+varsOfConstraint :: Constraint -> [VarIndex]
+varsOfConstraint (Constraint lhs _ rhs) =
+  varsOfLinearCombo lhs `union` varsOfLinearCombo rhs
+
+normalizeConstraint :: [VarIndex] -> Constraint -> [Double]
+normalizeConstraint vars (Constraint lhs sense rhs) =
+  if sense == Lt
+    then xs ++ [x]
+    else map negate xs ++ [-x]
+  where
+    lhs' = normalizeLinearCombination vars lhs
+    rhs' = normalizeLinearCombination vars rhs
+    coefs = IM.elems $ mergeWithKey (\_ a b -> Just (a-b)) id id lhs' rhs'
+    denominators = map denominator coefs
+    ppcm = foldr lcm 1 denominators % 1
+    x:xs = map (realToFrac . numerator . (*ppcm)) coefs
+  -- let (x:xs) = map realToFrac $
+  --              IM.elems $ mergeWithKey (\_ a b -> Just (a-b)) id id lhs' rhs'
+  -- in
+  -- if sense == Lt
+  --   then xs ++ [x]
+  --   else map negate xs ++ [-x]
+  -- where lhs' = normalizeLinearCombination vars lhs
+  --       rhs' = normalizeLinearCombination vars rhs
+
+normalizeConstraints :: [Constraint] -> [[Double]] -- for qhalf
+normalizeConstraints constraints = map (normalizeConstraint vars) constraints
+  where
+    vars = nub $ concatMap varsOfConstraint constraints
diff --git a/src/HalfSpaces/LinearCombination.hs b/src/HalfSpaces/LinearCombination.hs
new file mode 100644
--- /dev/null
+++ b/src/HalfSpaces/LinearCombination.hs
@@ -0,0 +1,63 @@
+{-# LANGUAGE TypeFamilies #-}
+module HalfSpaces.LinearCombination
+  where
+import           Data.AdditiveGroup
+import           Data.IntMap.Strict (IntMap, mergeWithKey)
+import qualified Data.IntMap.Strict as IM
+import           Data.List
+import           Data.Ratio
+import           Data.Tuple         (swap)
+import           Data.VectorSpace
+
+newtype LinearCombination = LinearCombination (IntMap Rational)
+  deriving Eq
+
+instance Show LinearCombination where
+  show (LinearCombination x) =
+    intercalate " + " $
+      map (\(i,r) -> if i==0
+                      then showRational r
+                      else if r == 1
+                            then "x" ++ show i
+                            else showRational r ++ "*x" ++ show i)
+          (IM.toAscList x)
+    where
+      showRational :: Rational -> String
+      showRational r = if q==1 then show p else show p ++ "/" ++ show q
+                       where
+                        p = numerator r
+                        q = denominator r
+
+instance AdditiveGroup LinearCombination where
+  zeroV = LinearCombination (IM.singleton 0 0)
+  (^+^) (LinearCombination imap1) (LinearCombination imap2) =
+    LinearCombination
+    (mergeWithKey (\_ x y -> Just (x+y)) id id imap1 imap2)
+  negateV (LinearCombination imap) = LinearCombination (IM.map negate imap)
+
+instance VectorSpace LinearCombination where
+  type Scalar LinearCombination = Rational
+  (*^) lambda (LinearCombination imap) =
+    LinearCombination (IM.map (*lambda) imap)
+
+type Var = LinearCombination
+type VarIndex = Int
+
+-- | new variable
+newVar :: VarIndex -> Var
+newVar i = if i >= 0
+            then LinearCombination (IM.singleton i 1)
+            else error "negative index"
+
+-- | linear combination from list of terms
+linearCombination :: [(Rational,Var)] -> LinearCombination
+linearCombination terms = linearCombo (map swap terms)
+--  LinearCombination (IM.fromListWith (+) (map swap terms))
+
+-- | constant linear combination
+constant :: Rational -> LinearCombination
+constant x = LinearCombination (IM.singleton 0 x)
+
+-- | alias for `constant`
+cst :: Rational -> LinearCombination
+cst = constant
diff --git a/src/HalfSpaces/ToySolver.hs b/src/HalfSpaces/ToySolver.hs
new file mode 100644
--- /dev/null
+++ b/src/HalfSpaces/ToySolver.hs
@@ -0,0 +1,44 @@
+module HalfSpaces.ToySolver
+  (interiorPoint)
+  where
+import           Control.Monad                (replicateM_)
+import           Data.Default.Class
+import           Data.IntMap.Strict           (IntMap, mapKeys, mergeWithKey)
+import qualified Data.IntMap.Strict           as IM
+import           Data.List                    (nub)
+-- import           Data.Ratio                   (Rational)
+import           Data.VectorSpace
+import           HalfSpaces.Constraint        (Constraint (..), Sense (..))
+import           HalfSpaces.Internal          (varsOfConstraint)
+import           HalfSpaces.LinearCombination (LinearCombination (..))
+import           ToySolver.Arith.Simplex      hiding (Lt)
+import qualified ToySolver.Data.LA            as LA
+
+constraintToCoeffMap :: Int -> Constraint -> IntMap Rational
+constraintToCoeffMap
+  newvar (Constraint (LinearCombination lhs) sense (LinearCombination rhs)) =
+  let terms = mapKeys (subtract 1)
+              (mergeWithKey (\_ x y -> Just (x-y)) id (IM.map negate) lhs rhs)
+  in
+  if sense == Lt
+    then IM.union terms (IM.singleton newvar 1)
+    else IM.union (IM.map negate terms) (IM.singleton newvar 1)
+
+constraintToAtom :: Int -> Constraint -> Atom Rational
+constraintToAtom newvar constraint =
+  let coeffmap = constraintToCoeffMap newvar constraint
+  in
+  LA.fromCoeffMap coeffmap .<=. LA.constant 0
+
+interiorPoint :: [Constraint] -> IO [Rational]
+interiorPoint constraints = do
+  let vars = nub (concatMap varsOfConstraint constraints)
+      dim = length (filter (/= 0) vars)
+      atoms = map (constraintToAtom dim) constraints
+  solver <- newSolver
+  replicateM_ (dim+1) (newVar solver)
+  mapM_ (assertAtom solver) atoms
+  setObj solver (negateV $ LA.var dim)
+  o <- optimize solver def
+  print o
+  mapM (getValue solver) [0 .. dim-1]
diff --git a/src/Qhull/Shared.hs b/src/Qhull/Shared.hs
new file mode 100644
--- /dev/null
+++ b/src/Qhull/Shared.hs
@@ -0,0 +1,54 @@
+module Qhull.Shared
+  where
+import qualified Data.HashMap.Strict.InsOrd as H
+import qualified Data.IntMap.Strict         as IM
+import           Data.Maybe
+import           Qhull.Types
+
+-- | whether two families are the same
+sameFamily :: Family -> Family -> Bool
+sameFamily (Family i) (Family j) = i == j
+sameFamily _ _ = False
+
+-- | vertices ids
+verticesIds :: HasVertices a => a -> [Index]
+verticesIds = IM.keys . _vertices
+
+-- | vertices coordinates
+verticesCoordinates :: HasVertices a => a -> [[Double]]
+verticesCoordinates = IM.elems . _vertices
+
+-- | number of vertices
+nVertices :: HasVertices a => a -> Int
+nVertices = IM.size . _vertices
+
+-- | edges ids
+edgesIds :: HasEdges a => a -> [IndexPair]
+edgesIds = H.keys . _edges
+
+-- | edges ids as pairs of integers
+edgesIds' :: HasEdges a => a -> [(Index,Index)]
+edgesIds' x = map fromPair (edgesIds x)
+  where
+    fromPair (Pair i j) = (i,j)
+
+-- | edges coordinates
+edgesCoordinates :: HasEdges a => a -> [([Double],[Double])]
+edgesCoordinates = H.elems . _edges
+
+-- | number of edges
+nEdges :: HasEdges a => a -> Int
+nEdges = H.size . _edges
+
+-- | whether a pair of vertices indices form an edge;
+-- the order of the indices has no importance
+isEdge :: HasEdges a => a -> (Index, Index) -> Bool
+isEdge x (i,j) = Pair i j `H.member` _edges x
+
+-- | edge as pair of points; the order of the vertices has no importance
+toPoints :: HasEdges a => a -> (Index, Index) -> Maybe ([Double], [Double])
+toPoints x (i,j) = H.lookup (Pair i j) (_edges x)
+
+-- | edge as pair of points, without checking the edge exists
+toPoints' :: HasEdges a => a -> (Index, Index) -> ([Double], [Double])
+toPoints' x (i,j) = fromJust $ toPoints x (i,j)
diff --git a/src/Qhull/Types.hs b/src/Qhull/Types.hs
new file mode 100644
--- /dev/null
+++ b/src/Qhull/Types.hs
@@ -0,0 +1,42 @@
+module Qhull.Types
+  where
+import           Data.Hashable
+import           Data.HashMap.Strict.InsOrd (InsOrdHashMap)
+import           Data.IntMap.Strict         (IntMap)
+import           Data.IntSet                (IntSet)
+
+type Index = Int
+type IndexMap = IntMap
+type IndexSet = IntSet
+
+data IndexPair = Pair Index Index
+  deriving (Show, Read)
+instance Eq IndexPair where
+    Pair i j == Pair i' j' = (i == i' && j == j') || (i == j' && j == i')
+
+instance Hashable IndexPair where
+  hashWithSalt _ (Pair i j) = (i+j)*(i+j+1) + 2 * min i j
+
+type EdgeMap = InsOrdHashMap IndexPair ([Double],[Double])
+
+data Family = Family Int | None
+     deriving (Show, Read, Eq)
+
+class HasFamily m where
+  _family :: m -> Family
+
+class HasNormal m where
+  _normal :: m -> [Double]
+  _offset :: m -> Double
+
+class HasVertices m where
+  _vertices :: m -> IndexMap [Double]
+
+class HasEdges m where
+  _edges :: m -> EdgeMap
+
+class HasVolume m where
+  _volume :: m -> Double
+
+class HasCenter m where
+  _center :: m -> [Double]
diff --git a/src/Voronoi/R.hs b/src/Voronoi/R.hs
new file mode 100644
--- /dev/null
+++ b/src/Voronoi/R.hs
@@ -0,0 +1,98 @@
+module Voronoi.R
+  where
+import           ConvexHull         (convexHull, ConvexHull (..))
+import qualified Data.IntMap.Strict as IM
+import           Data.List          (intercalate, transpose)
+import           Data.List.Index    (imap, iconcatMap)
+import           Data.Maybe
+import           Delaunay.R
+import           Delaunay.Types     (Tesselation)
+import           Qhull.Shared
+import           Voronoi2D
+import           Voronoi3D
+
+voronoi2ForR :: Voronoi2 -> Maybe Tesselation -> String
+voronoi2ForR v d =
+  (if isJust d then dcode else "")
+  ++ "colors <- rainbow(" ++ show (length boundedCells +1) ++ ")\n"
+  ++ unlines (imap boundedCellForR boundedCells)
+  ++ unlines (map cellForR v)
+  where
+    dcode = delaunay2ForR (fromJust d) True
+    cellForR :: ([Double], Cell2) -> String
+    cellForR (site, edges) =
+      point ++ "\n" ++ unlines (map f edges)
+      where
+        point =
+          "points(" ++ show (site!!0) ++ ", " ++ show (site!!1) ++
+                    ", pch=19, col=\"blue\")"
+        f :: Edge2 -> String
+        f edge = case edge of
+          Edge2 ((x0,y0),(x1,y1)) ->
+            "segments(" ++ intercalate "," (map show [x0,y0,x1,y1]) ++
+                        ", col=\"black\", lty=1, lwd=1)"
+          IEdge2 ((x0,y0),(x1,y1)) ->
+            "segments(" ++ intercalate "," (map show [x0,y0,x0+x1,y0+y1]) ++
+                        ", col=\"red\", lty=1, lwd=1)"
+          TIEdge2 ((x0,y0),(x1,y1)) ->
+            "segments(" ++ intercalate "," (map show [x0,y0,x1,y1]) ++
+                        ", col=\"red\", lty=1, lwd=1)"
+    boundedCells = map snd (restrictVoronoi2 v)
+    boundedCellForR :: Int -> Cell2 -> String
+    boundedCellForR i cell =
+      "polygon(c(" ++ intercalate "," (map show x) ++ "), c(" ++
+      intercalate "," (map show y) ++ "), col=colors[" ++ show (i+1) ++ "])\n"
+      where
+        [x, y] = transpose (cell2Vertices' cell)
+
+
+voronoi3ForRgl :: Voronoi3 -> Maybe Int -> Maybe Tesselation -> String
+voronoi3ForRgl v n d =
+  let v' = if isJust n then roundVoronoi3 (fromJust n) v else v in
+  let code = unlines $ map cellForRgl v' in
+  "library(rgl)\n" ++
+  if isJust d
+    then code ++ "\n" ++ "# Delaunay:\n" ++
+         delaunay3rgl (fromJust d) True True True True (Just 0.9)
+    else code
+  where
+    cellForRgl :: ([Double], Cell3) -> String
+    cellForRgl (site, cell) = plotpoint ++ unlines (map f cell)
+      where
+        plotpoint = "spheres3d(" ++ intercalate "," (map show site) ++ ", radius=0.1, color=\"red\")\n"
+        f :: Edge3 -> String
+        f edge = case edge of
+          Edge3 (x,y) ->
+            "segments3d(rbind(c" ++ show x ++ ", \n\tc" ++ show y ++ "))"
+          TIEdge3 (x,y) ->
+            "segments3d(rbind(c" ++ show x ++ ", \n\tc" ++ show y ++ "), col=c(\"red\",\"red\"))"
+          IEdge3 (x,y) ->
+            "segments3d(rbind(c" ++ show x ++ ", \n\tc" ++ show (sumTriplet x y) ++ "), col=c(\"red\",\"red\"))"
+        sumTriplet (a,b,c) (a',b',c') = (a+a',b+b',c+c')
+
+-- | plot with facets
+voronoi3ForRgl' :: Voronoi3 -> Maybe Int -> Maybe Tesselation -> IO String
+voronoi3ForRgl' v n d = do -- faudrait un argument approx
+  let code1 = voronoi3ForRgl v n d
+      v' = restrictVoronoi3' v
+      v'' = if isJust n then roundVoronoi3 (fromJust n) v' else v'
+      boundedCells = map (cell3Vertices . snd) (restrictVoronoi3' v'')
+--      boundedCells' = map (nub . map (map (approx 13))) boundedCells
+  hulls <- mapM (\cell -> convexHull cell True False Nothing) boundedCells
+  let triangles = map (map verticesCoordinates . IM.elems . _hfacets) hulls
+      code_colors = "colors <- rainbow(" ++ show (length triangles +1) ++ ")\n"
+      code2 = iconcatMap (\i x -> concatMap (rglTriangle i) x ++ "\n") triangles
+  return $ code_colors ++ code1 ++ code2
+  where
+    -- approx :: RealFrac a => Int -> a -> a
+    -- approx n x = fromInteger (round $ x * (10^n)) / (10.0^^n)
+    asTriplet p = (p!!0, p!!1, p!!2)
+    rglTriangle :: Int -> [[Double]] -> String
+    rglTriangle i threepoints =
+      "triangles3d(rbind(c" ++ show p1 ++ ", \n\tc" ++ show p2 ++
+      ", \n\tc" ++ show p3 ++ "), color=colors[" ++ show (i+1) ++ "]" ++
+      ", alpha=0.75)\n"
+      where
+        p1 = asTriplet $ threepoints!!0
+        p2 = asTriplet $ threepoints!!1
+        p3 = asTriplet $ threepoints!!2
diff --git a/src/Voronoi/Shared.hs b/src/Voronoi/Shared.hs
new file mode 100644
--- /dev/null
+++ b/src/Voronoi/Shared.hs
@@ -0,0 +1,8 @@
+module Voronoi.Shared
+  where
+
+filterVoronoi :: ([a] -> Bool) -> [([Double], [a])] -> [([Double], [a])]
+filterVoronoi cellTester = filter (\(_, cell) -> cellTester cell)
+
+removeDegenerateCells :: [([Double], [a])] -> [([Double], [a])]
+removeDegenerateCells = filterVoronoi (not . null)
diff --git a/src/Voronoi/Voronoi.hs b/src/Voronoi/Voronoi.hs
new file mode 100644
--- /dev/null
+++ b/src/Voronoi/Voronoi.hs
@@ -0,0 +1,63 @@
+module Voronoi.Voronoi
+  where
+import qualified Data.IntMap.Strict as IM
+import qualified Data.IntSet        as IS
+import           Data.Maybe
+import           Delaunay.Delaunay (vertexNeighborFacets)
+import           Delaunay.Types
+import           Qhull.Shared
+import           Qhull.Types
+
+type Point = [Double]
+type Vector = [Double]
+data Edge = Edge (Point, Point) | IEdge (Point, Vector)
+     deriving Show
+type Cell = [Edge]
+
+-- factor2 :: (Double,Double,Double,Double) -> (Double,Double) -> (Double,Double) -> Double
+-- factor2 box@(xmin, xmax, ymin, ymax) p@(p1,p2) (v1,v2)
+--   | v1==0 = if v2>0 then (ymax-p2)/v2 else (ymin-p2)/v2
+--   | v2==0 = if v1>0 then (xmax-p1)/v1 else (xmin-p1)/v1
+--   | otherwise = min (factor2 box p (v1,0)) (factor2 box p (0,v2))
+--   --  | v1>0 && v2>0 = min ((r-p1)/v1) ((t-p2)/v2)
+--   --  | v1>0 && v2<0 = min ((r-p1)/v1) ((b-p2)/v2)
+--   --  | v1<0 && v2>0 = min ((l-p1)/v1) ((t-p2)/v2)
+--   --  | v1<0 && v2<0 = min ((l-p1)/v1) ((b-p2)/v2)
+
+-- approx :: RealFrac a => Int -> a -> a
+-- approx n x = fromInteger (round $ x * (10^n)) / (10.0^^n)
+
+edgesFromTileFacet :: Tesselation -> TileFacet -> Maybe Edge
+edgesFromTileFacet tess tilefacet
+  | length tileindices == 1 = Just $ IEdge (c1, _normal tilefacet)
+  | sameFamily (_family tile1) (_family tile2) || c1 == c2 = Nothing
+  | otherwise = Just $ Edge (c1, c2)
+  where
+    tileindices = (IS.toList . _facetOf) tilefacet
+    tiles = _tiles tess
+    tile1 = tiles IM.! head tileindices
+    tile2 = tiles IM.! last tileindices
+    c1 = _center tile1
+    c2 = _center tile2
+
+voronoiCell :: ([TileFacet] -> [TileFacet]) -> (Edge -> a) -> Tesselation
+            -> Index -> [a]
+voronoiCell facetsQuotienter edgeTransformer tess i =
+  let tilefacets = facetsQuotienter $ IM.elems (vertexNeighborFacets tess i) in
+  map (edgeTransformer . fromJust) $
+      filter isJust $ map (edgesFromTileFacet tess) tilefacets
+
+voronoi :: (Tesselation -> Index -> a) -> Tesselation -> [([Double], a)]
+voronoi cellGetter tess =
+  let sites = IM.elems $ _vertices tess in
+    zip sites (map (cellGetter tess) [0 .. length sites -1])
+
+voronoi' :: Tesselation -> [([Double], Cell)]
+voronoi' = voronoi (voronoiCell id id)
+
+-- | whether a Voronoi cell is bounded
+boundedCell :: Cell -> Bool
+boundedCell = all isFiniteEdge
+  where
+    isFiniteEdge (Edge _) = True
+    isFiniteEdge _        = False
diff --git a/src/Voronoi2D.hs b/src/Voronoi2D.hs
new file mode 100644
--- /dev/null
+++ b/src/Voronoi2D.hs
@@ -0,0 +1,122 @@
+module Voronoi2D
+  (Edge2(..)
+ , Cell2
+ , Voronoi2
+ , prettyShowVoronoi2
+ , voronoiCell2
+ , voronoi2
+ , clipVoronoi2
+ , boundedCell2
+ , restrictVoronoi2
+ , cell2Vertices
+ , cell2Vertices'
+ , module Voronoi.Shared)
+  where
+import           Control.Arrow    (second)
+import           Data.Graph       (flattenSCCs, stronglyConnComp)
+import           Data.List
+import           Data.List.Index  (imap)
+import           Data.Tuple.Extra (both)
+import           Delaunay.Types
+import           Qhull.Types
+import           Text.Show.Pretty (ppShow)
+import           Voronoi.Shared
+import           Voronoi.Voronoi
+
+type Point2 = (Double, Double)
+type Vector2 = (Double, Double)
+data Edge2 = Edge2 (Point2, Point2) | IEdge2 (Point2, Vector2)
+             | TIEdge2 (Point2, Point2)
+              deriving (Show, Eq)
+type Cell2 = [Edge2]
+type Voronoi2 = [([Double], Cell2)]
+type Box2 = ((Double, Double), (Double, Double))
+
+-- | pretty print a Voronoi 2D diagram
+prettyShowVoronoi2 :: Voronoi2 -> Maybe Int -> IO ()
+prettyShowVoronoi2 v m = do
+  let string = intercalate "\n---\n" (map (prettyShowCell2 m) v)
+  putStrLn string
+  where
+    approx :: RealFrac a => Int -> a -> a
+    approx n x = fromInteger (round $ x * (10^n)) / (10.0^^n)
+    roundPairPoint2 :: (Point2, Point2) -> Int -> (Point2, Point2)
+    roundPairPoint2 ((x1,x2), (y1,y2)) n =
+      (asPair $ map (approx n) [x1,x2], asPair $ map (approx n) [y1,y2])
+    prettyShowEdge2 :: Maybe Int -> Edge2 -> String
+    prettyShowEdge2 n edge = case edge of
+      Edge2 x   -> " Edge " ++ string x
+      IEdge2 x  -> " IEdge " ++ string x
+      TIEdge2 x -> " TIEdge " ++ string x
+      where
+        string x = ppShow $ maybe x (roundPairPoint2 x) n
+    prettyShowEdges2 :: Maybe Int -> [Edge2] -> String
+    prettyShowEdges2 n edges = intercalate "\n" (map (prettyShowEdge2 n) edges)
+    prettyShowCell2 :: Maybe Int -> ([Double], Cell2) -> String
+    prettyShowCell2 n (site, edges) =
+      "Site " ++ ppShow site ++ " :\n" ++ prettyShowEdges2 n edges
+
+asPair :: [Double] -> (Double, Double)
+asPair [a,b] = (a,b)
+asPair _     = (undefined, undefined)
+
+edgeToEdge2 :: Edge -> Edge2
+edgeToEdge2 (Edge (x, y))  = Edge2 (both asPair (x, y))
+edgeToEdge2 (IEdge (x, v)) = IEdge2 (both asPair (x, v))
+
+-- | Voronoi cell of a vertex given by its index
+voronoiCell2 :: Tesselation -> Index -> Cell2
+voronoiCell2 = voronoiCell id edgeToEdge2
+
+-- | 2D Voronoi Diagram
+voronoi2 :: Tesselation -> Voronoi2
+voronoi2 = voronoi voronoiCell2
+
+-- | whether a 2D Voronoi cell is bounded
+boundedCell2 :: Cell2 -> Bool
+boundedCell2 = all isFiniteEdge
+  where
+    isFiniteEdge (Edge2 _) = True
+    isFiniteEdge _         = False
+
+-- | restrict a 2D Voronoi diagram to its bounded cells
+restrictVoronoi2 :: Voronoi2 -> Voronoi2
+restrictVoronoi2 = filterVoronoi boundedCell2
+
+-- | vertices of a bounded 2D cell
+cell2Vertices :: Cell2 -> [[Double]]
+cell2Vertices cell = nub $ concatMap extractVertices cell
+  where
+    extractVertices :: Edge2 -> [[Double]]
+    extractVertices (Edge2 ((x1,x2),(y1,y2))) = [[x1,x2],[y1,y2]]
+    extractVertices _                         = []
+
+-- | ordered vertices of a bounded 2D cell
+cell2Vertices' :: Cell2 -> [[Double]]
+cell2Vertices' cell = flattenSCCs (stronglyConnComp x)
+  where
+    vs = cell2Vertices cell
+    x = imap (\i v -> (v, i, findIndices (connectedVertices v) vs)) vs
+    connectedVertices :: [Double] -> [Double] -> Bool
+    connectedVertices [x1,x2] [y1,y2] =
+      (Edge2 ((x1,x2),(y1,y2)) `elem` cell) ||
+      (Edge2 ((y1,y2),(x1,x2)) `elem` cell)
+    connectedVertices _ _ = False
+
+truncEdge2 :: Box2 -> Edge2 -> Edge2
+truncEdge2 ((xmin, xmax), (ymin, ymax)) edge =
+  if isIEdge edge
+    then TIEdge2 (p, (p1 + factor v1 v2 * v1, p2 + factor v1 v2 * v2))
+    else edge
+  where
+    isIEdge (IEdge2 _) = True
+    isIEdge _          = False
+    IEdge2 (p@(p1,p2), (v1,v2)) = edge
+    factor w1 w2 | w1==0 = if w2>0 then (ymax-p2)/w2 else (ymin-p2)/w2
+                 | w2==0 = if w1>0 then (xmax-p1)/w1 else (xmin-p1)/w1
+                 | otherwise = min (factor w1 0) (factor 0 w2)
+    -- factor = factor2 box p v
+
+-- | clip a 2D Voronoi diagram in a bounding box
+clipVoronoi2 :: Box2 -> Voronoi2 -> Voronoi2
+clipVoronoi2 box = map (second (map (truncEdge2 box)))
diff --git a/src/Voronoi3D.hs b/src/Voronoi3D.hs
new file mode 100644
--- /dev/null
+++ b/src/Voronoi3D.hs
@@ -0,0 +1,196 @@
+module Voronoi3D
+  (Edge3(..)
+ , Cell3
+ , Voronoi3
+ , prettyShowVoronoi3
+ , clipVoronoi3
+ , voronoiCell3
+ , voronoi3
+ , cell3Vertices
+ , voronoi3vertices
+ , boundedCell3
+ , restrictVoronoi3
+ , restrictVoronoi3'
+ , restrictVoronoi3box
+ , restrictVoronoi3box'
+ , roundVoronoi3
+ , summaryVoronoi3
+ , module Voronoi.Shared)
+  where
+import           Control.Arrow    (second)
+import           Control.Monad    (liftM2)
+import qualified Data.IntSet      as IS
+import           Data.List
+import           Data.List.Unique (count_)
+import           Data.Tuple.Extra (both)
+import           Delaunay.Types
+import           Qhull.Types
+import           Text.Show.Pretty (ppShow)
+import           Voronoi.Shared
+import           Voronoi.Voronoi
+
+type Point3 = (Double, Double, Double)
+type Vector3 = (Double, Double, Double)
+
+data Edge3 = Edge3 (Point3, Point3) | IEdge3 (Point3, Vector3)
+             | TIEdge3 (Point3, Point3)
+              deriving (Show)
+instance Eq Edge3 where
+  Edge3 (x,y) == Edge3 (x',y') = (x == x' && y == y') || (x == y') && (y == x')
+  IEdge3 (x,v) == IEdge3 (x',v') = x == x' && v == v'
+  TIEdge3 (x,y) == TIEdge3 (x',y') = x == x' && y == y'
+  _ == _ = False
+
+type Cell3 = [Edge3]
+type Voronoi3 = [([Double], Cell3)]
+type Box3 = ((Double, Double), (Double, Double), (Double, Double))
+
+-- | summary of a 3D Voronoi diagram
+summaryVoronoi3 :: Voronoi3 -> IO ()
+summaryVoronoi3 v = do
+  let ntotal = show $ length v
+  let boundedDiagram = restrictVoronoi3 v
+  let nbounded = show $ length boundedDiagram
+  let ndegenerate = show $ length $ filterVoronoi null boundedDiagram
+  let lengths = map (\(_,cell) -> length cell) boundedDiagram
+  putStrLn $ "Voronoi diagram with " ++ ntotal ++ " cells, including " ++
+             nbounded ++ " bounded and " ++ ndegenerate ++ " degenerate.\n" ++
+             "Number of edges for bounded cells: " ++ show (count_ lengths)
+
+-- | pretty print a 3D Voronoi diagram
+prettyShowVoronoi3 :: Voronoi3 -> Maybe Int -> IO ()
+prettyShowVoronoi3 v m = do
+  let string = intercalate "\n---\n" (map (prettyShowCell3 m) v)
+  putStrLn $ string ++ "\n------\n"
+  where
+    approx :: RealFrac a => Int -> a -> a
+    approx n x = fromInteger (round $ x * (10^n)) / (10.0^^n)
+    roundPairPoint3 :: (Point3, Point3) -> Int -> (Point3, Point3)
+    roundPairPoint3 ((x1,x2,x3), (y1,y2,y3)) n =
+      (asTriplet $ map (approx n) [x1,x2,x3],
+       asTriplet $ map (approx n) [y1,y2,y3])
+    prettyShowEdge3 :: Maybe Int -> Edge3 -> String
+    prettyShowEdge3 n edge = case edge of
+      Edge3 x   -> " Edge " ++ string x
+      IEdge3 x  -> " IEdge " ++ string x
+      TIEdge3 x -> " TIEdge " ++ string x
+      where
+        string x = ppShow $ maybe x (roundPairPoint3 x) n
+    prettyShowEdges3 :: Maybe Int -> [Edge3] -> String
+    prettyShowEdges3 n edges = intercalate "\n" (map (prettyShowEdge3 n) edges)
+    prettyShowCell3 :: Maybe Int -> ([Double], Cell3) -> String
+    prettyShowCell3 n (site, edges) =
+      "Site " ++ ppShow site ++ " :\n" ++ prettyShowEdges3 n edges
+
+
+asTriplet :: [a] -> (a, a, a)
+asTriplet [x,y,z] = (x,y,z)
+asTriplet _       = (undefined, undefined, undefined)
+
+edgeToEdge3 :: Edge -> Edge3
+edgeToEdge3 (Edge (x, y))  = Edge3 (both asTriplet (x, y))
+edgeToEdge3 (IEdge (x, v)) = IEdge3 (both asTriplet (x, v))
+
+equalFacets :: TileFacet -> TileFacet -> Bool
+equalFacets tfacet1 tfacet2 =
+  IS.size f1 == 1 && IS.size f2 == 1 &&
+  _center tfacet1 == _center tfacet2 &&
+  _normal tfacet1 == _normal tfacet2
+  where
+    f1 = _facetOf tfacet1
+    f2 = _facetOf tfacet2
+
+-- | Voronoi cell of a vertex given by its index
+voronoiCell3 :: Tesselation -> Index -> Cell3
+voronoiCell3 = voronoiCell (nubBy equalFacets) edgeToEdge3
+
+-- | 3D Voronoi diagram
+voronoi3 :: Tesselation -> Voronoi3
+voronoi3 = voronoi voronoiCell3
+
+-- |
+roundVoronoi3 :: Int -> Voronoi3 -> Voronoi3
+roundVoronoi3 n = map (second roundCell3)
+  where
+    roundCell3 :: Cell3 -> Cell3
+    roundCell3 cell = nub $ map roundEdge3 cell
+    roundEdge3 :: Edge3 -> Edge3
+    roundEdge3 (Edge3 (x,y)) = Edge3 (approx n x, approx n y)
+    roundEdge3 (IEdge3 (x,v)) = IEdge3 (approx n x, approx n v)
+    roundEdge3 (TIEdge3 (x,y)) = TIEdge3 (approx n x, y)
+    approx :: Int -> (Double, Double, Double) -> (Double, Double, Double)
+    approx m (a,b,c) =
+      asTriplet $ map (\x -> fromInteger (round $ x*(10^m)) / (10.0^^m)) [a,b,c]
+
+
+-- | whether a 3D Voronoi cell is bounded
+boundedCell3 :: Cell3 -> Bool
+boundedCell3 = all isFiniteEdge
+  where
+    isFiniteEdge (Edge3 _) = True
+    isFiniteEdge _         = False
+
+-- | whether a 3D Voronoi cell is inside a given box
+cell3inBox :: Box3 -> Cell3 -> Bool
+cell3inBox ((xmin,xmax), (ymin, ymax), (zmin,zmax)) cell =
+  boundedCell3 cell && all edgeInBox cell
+  where
+    tripletInBox (x,y,z) =
+      x > xmin && y > ymin && z > zmin && x < xmax && y < ymax && z < zmax
+    edgeInBox (Edge3 (p1,p2)) = tripletInBox p1 && tripletInBox p2
+    edgeInBox _               = False
+
+-- | restrict a 3D Voronoi diagram to its bounded cells
+restrictVoronoi3 :: Voronoi3 -> Voronoi3
+restrictVoronoi3 = filterVoronoi boundedCell3
+
+-- | restrict a 3D Voronoi diagram to its nondegenerate bounded cells
+restrictVoronoi3' :: Voronoi3 -> Voronoi3
+restrictVoronoi3' = filterVoronoi (liftM2 (&&) boundedCell3 (not . null))
+--                    (\cell -> boundedCell3 cell && not (null cell))
+
+-- | restrict a 3D Voronoi diagram to the cells contained in a box
+restrictVoronoi3box :: Box3 -> Voronoi3 -> Voronoi3
+restrictVoronoi3box box = filterVoronoi (cell3inBox box)
+
+-- | restrict a 3D Voronoi diagram to the nondegenerate cells contained in a box
+restrictVoronoi3box' :: Box3 -> Voronoi3 -> Voronoi3
+restrictVoronoi3box' box =
+  filterVoronoi (not . null) . restrictVoronoi3box box
+
+-- | vertices of a bounded 3D cell
+cell3Vertices :: Cell3 -> [[Double]]
+cell3Vertices cell = nub $ concatMap extractVertices cell
+  where
+    extractVertices :: Edge3 -> [[Double]]
+    extractVertices (Edge3 ((x1,y1,z1),(x2,y2,z2))) = [[x1,y1,z1],[x2,y2,z2]]
+    extractVertices _                               = []
+
+-- | vertices of a 3D Voronoi diagram
+voronoi3vertices :: Voronoi3 -> [[Double]]
+voronoi3vertices = concatMap (\(_,cell) -> cell3Vertices cell)
+
+truncEdge3 :: Box3 -> Edge3 -> Edge3
+truncEdge3 ((xmin, xmax), (ymin, ymax), (zmin, zmax)) edge =
+  if isIEdge edge
+    then TIEdge3 ((p1,p2,p3), (p1 + factor v1 v2 v3 * v1,
+                  p2 + factor v1 v2 v3 * v2, p3 + factor v1 v2 v3 * v3))
+    else edge
+  where
+    isIEdge (IEdge3 _) = True
+    isIEdge _          = False
+    IEdge3 ((p1,p2,p3), (v1,v2,v3)) = edge
+    factor u1 u2 u3 | u1==0 && u2==0 = (if u3>0 then zmax-p3 else zmin-p3)/u3
+                    | u1==0 && u3==0 = (if u2>0 then ymax-p2 else ymin-p2)/u2
+                    | u2==0 && u3==0 = (if u1>0 then xmax-p1 else xmin-p1)/u1
+                    | otherwise = min (min (factor u1 0 0) (factor 0 u2 0))
+                                      (factor 0 0 u3)
+    -- factor u1 u2 u3 | u3==0 = factor2 (xmin,xmax,ymin,ymax) (p1,p2) (u1,u2)
+    --                 | u2==0 = factor2 (zmin,zmax,xmin,xmax) (p3,p1) (u3,u1)
+    --                 | u1==0 = factor2 (ymin,ymax,zmin,zmax) (p2,p3) (u2,u3)
+    --                 | otherwise = min (min (factor u1 u2 0) (factor 0 u2 u3))
+    --                                   (factor u1 0 u3)
+
+-- | clip 3D Voronoi diagram in a bounding box
+clipVoronoi3 :: Box3 -> Voronoi3 -> Voronoi3
+clipVoronoi3 box = map (second (map (truncEdge3 box)))
