packages feed

qhull (empty) → 0.1.0.1

raw patch · 61 files changed

+35045/−0 lines, 61 filesdep +Uniquedep +basedep +combinatsetup-changed

Dependencies added: Unique, base, combinat, containers, data-default-class, extra, hashable, ilist, insert-ordered-containers, optparse-applicative, pretty-show, qhull, random, regex-base, regex-compat, regex-posix, split, toysolver, vector-algorithms, vector-space

Files

+ C/convexhull.c view
@@ -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;+  }++}
+ C/delaunay.c view
@@ -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);+}
+ C/geom2_r.c view
@@ -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 */+
+ C/geom_r.c view
@@ -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 */+++
+ C/global_r.c view
@@ -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 */+
+ C/halfspaces.c view
@@ -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;+  }++}
+ C/io_r.c view
@@ -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 */+
+ C/libqhull_r.c view
@@ -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 */++
+ C/mem_r.c view
@@ -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 */
+ C/merge_r.c view
@@ -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 */+
+ C/poly2_r.c view
@@ -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 */
+ C/poly_r.c view
@@ -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 */+++
+ C/qset_r.c view
@@ -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 */++
+ C/random_r.c view
@@ -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 */
+ C/stat_r.c view
@@ -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+
+ C/user_r.c view
@@ -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 */++
+ C/usermem_r.c view
@@ -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 */++
+ C/userprintf_r.c view
@@ -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 */+
+ C/utils.c view
@@ -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;+}
+ GPL_3 view
@@ -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>.
+ README.md view
@@ -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)
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ qhull.cabal view
@@ -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
+ src-exe/Adjacency/AdjacencyMatrix.hs view
@@ -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" )
+ src-exe/ConvexHull/Main.hs view
@@ -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
+ src-exe/Delaunay/Main.hs view
@@ -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)
+ src-exe/HalfSpaces/Main.hs view
@@ -0,0 +1,9 @@+module Main+  where+import           HalfSpaces+import           Text.Show.Pretty++main :: IO ()+main = do+  is <- hsintersections region3D False+  pPrint is
+ src-exe/Voronoi/Main.hs view
@@ -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)
+ src/ConvexHull.hs view
@@ -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
+ src/ConvexHull/BiTruncatedTesseract.hs view
@@ -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) ]+
+ src/ConvexHull/CConvexHull.hs view
@@ -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+                    }
+ src/ConvexHull/CantiTrunc600Cell/Data.hs view
@@ -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
+ src/ConvexHull/ConvexHull.hs view
@@ -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
+ src/ConvexHull/Examples.hs view
@@ -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 ] ]
+ src/ConvexHull/OmniTruncated120Cell.hs view
@@ -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++++
+ src/ConvexHull/R.hs view
@@ -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
+ src/ConvexHull/SnubDodecahedron/SnubDodecahedron.hs view
@@ -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+
+ src/ConvexHull/Truncated120Cell3.hs view
@@ -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
+ src/ConvexHull/Types.hs view
@@ -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)
+ src/Delaunay.hs view
@@ -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
+ src/Delaunay/Adjacency.hs view
@@ -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
+ src/Delaunay/CDelaunay.hs view
@@ -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
+ src/Delaunay/Delaunay.hs view
@@ -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
+ src/Delaunay/Examples.hs view
@@ -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]+  ]
+ src/Delaunay/R.hs view
@@ -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)
+ src/Delaunay/Types.hs view
@@ -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))
+ src/HalfSpaces.hs view
@@ -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 
+ src/HalfSpaces/CHalfSpaces.hs view
@@ -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))
+ src/HalfSpaces/Constraint.hs view
@@ -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 .<=., .>=.
+ src/HalfSpaces/Examples.hs view
@@ -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
+ src/HalfSpaces/HalfSpaces.hs view
@@ -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]]
+ src/HalfSpaces/Internal.hs view
@@ -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
+ src/HalfSpaces/LinearCombination.hs view
@@ -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
+ src/HalfSpaces/ToySolver.hs view
@@ -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]
+ src/Qhull/Shared.hs view
@@ -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)
+ src/Qhull/Types.hs view
@@ -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]
+ src/Voronoi/R.hs view
@@ -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
+ src/Voronoi/Shared.hs view
@@ -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)
+ src/Voronoi/Voronoi.hs view
@@ -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
+ src/Voronoi2D.hs view
@@ -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)))
+ src/Voronoi3D.hs view
@@ -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)))