From 705ccbe331cd61e305a27bc4c461f0e0aafcb6c5 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Tue, 7 Aug 2018 11:15:47 +0200 Subject: [PATCH 01/21] Added qhull library to xs/src and cmake --- xs/CMakeLists.txt | 6 +- xs/src/qhull/Announce.txt | 47 + xs/src/qhull/CMakeLists.txt | 128 + xs/src/qhull/COPYING.txt | 38 + xs/src/qhull/README.txt | 623 +++ xs/src/qhull/REGISTER.txt | 32 + xs/src/qhull/html/index.htm | 935 ++++ .../html/normal_voronoi_knauss_oesterle.jpg | Bin 0 -> 23924 bytes xs/src/qhull/html/qconvex.htm | 630 +++ xs/src/qhull/html/qdelau_f.htm | 416 ++ xs/src/qhull/html/qdelaun.htm | 628 +++ xs/src/qhull/html/qh--4d.gif | Bin 0 -> 4372 bytes xs/src/qhull/html/qh--cone.gif | Bin 0 -> 2946 bytes xs/src/qhull/html/qh--dt.gif | Bin 0 -> 3772 bytes xs/src/qhull/html/qh--geom.gif | Bin 0 -> 318 bytes xs/src/qhull/html/qh--half.gif | Bin 0 -> 2537 bytes xs/src/qhull/html/qh--rand.gif | Bin 0 -> 3875 bytes xs/src/qhull/html/qh-code.htm | 1062 +++++ xs/src/qhull/html/qh-eg.htm | 693 +++ xs/src/qhull/html/qh-faq.htm | 1547 +++++++ xs/src/qhull/html/qh-get.htm | 106 + xs/src/qhull/html/qh-impre.htm | 826 ++++ xs/src/qhull/html/qh-optc.htm | 292 ++ xs/src/qhull/html/qh-optf.htm | 736 +++ xs/src/qhull/html/qh-optg.htm | 274 ++ xs/src/qhull/html/qh-opto.htm | 353 ++ xs/src/qhull/html/qh-optp.htm | 253 + xs/src/qhull/html/qh-optq.htm | 731 +++ xs/src/qhull/html/qh-optt.htm | 278 ++ xs/src/qhull/html/qh-quick.htm | 495 ++ xs/src/qhull/html/qhalf.htm | 626 +++ xs/src/qhull/html/qhull-cpp.xml | 214 + xs/src/qhull/html/qhull.htm | 473 ++ xs/src/qhull/html/qhull.man | 1008 ++++ xs/src/qhull/html/qhull.txt | 1263 +++++ xs/src/qhull/html/qvoron_f.htm | 396 ++ xs/src/qhull/html/qvoronoi.htm | 667 +++ xs/src/qhull/html/rbox.htm | 277 ++ xs/src/qhull/html/rbox.man | 176 + xs/src/qhull/html/rbox.txt | 195 + xs/src/qhull/index.htm | 284 ++ xs/src/qhull/origCMakeLists.txt | 426 ++ xs/src/qhull/src/Changes.txt | 2129 +++++++++ xs/src/qhull/src/libqhull/DEPRECATED.txt | 29 + xs/src/qhull/src/libqhull/Makefile | 240 + xs/src/qhull/src/libqhull/Mborland | 206 + xs/src/qhull/src/libqhull/geom.c | 1234 +++++ xs/src/qhull/src/libqhull/geom.h | 176 + xs/src/qhull/src/libqhull/geom2.c | 2094 +++++++++ xs/src/qhull/src/libqhull/global.c | 2217 +++++++++ xs/src/qhull/src/libqhull/index.htm | 264 ++ xs/src/qhull/src/libqhull/io.c | 4062 +++++++++++++++++ xs/src/qhull/src/libqhull/io.h | 159 + xs/src/qhull/src/libqhull/libqhull.c | 1403 ++++++ xs/src/qhull/src/libqhull/libqhull.h | 1140 +++++ xs/src/qhull/src/libqhull/libqhull.pro | 67 + xs/src/qhull/src/libqhull/mem.c | 576 +++ xs/src/qhull/src/libqhull/mem.h | 222 + xs/src/qhull/src/libqhull/merge.c | 3628 +++++++++++++++ xs/src/qhull/src/libqhull/merge.h | 178 + xs/src/qhull/src/libqhull/poly.c | 1205 +++++ xs/src/qhull/src/libqhull/poly.h | 296 ++ xs/src/qhull/src/libqhull/poly2.c | 3222 +++++++++++++ xs/src/qhull/src/libqhull/qh-geom.htm | 295 ++ xs/src/qhull/src/libqhull/qh-globa.htm | 165 + xs/src/qhull/src/libqhull/qh-io.htm | 305 ++ xs/src/qhull/src/libqhull/qh-mem.htm | 145 + xs/src/qhull/src/libqhull/qh-merge.htm | 366 ++ xs/src/qhull/src/libqhull/qh-poly.htm | 485 ++ xs/src/qhull/src/libqhull/qh-qhull.htm | 279 ++ xs/src/qhull/src/libqhull/qh-set.htm | 308 ++ xs/src/qhull/src/libqhull/qh-stat.htm | 163 + xs/src/qhull/src/libqhull/qh-user.htm | 271 ++ xs/src/qhull/src/libqhull/qhull-exports.def | 417 ++ xs/src/qhull/src/libqhull/qhull_a.h | 150 + xs/src/qhull/src/libqhull/qhull_p-exports.def | 418 ++ xs/src/qhull/src/libqhull/qset.c | 1340 ++++++ xs/src/qhull/src/libqhull/qset.h | 490 ++ xs/src/qhull/src/libqhull/random.c | 245 + xs/src/qhull/src/libqhull/random.h | 34 + xs/src/qhull/src/libqhull/rboxlib.c | 870 ++++ xs/src/qhull/src/libqhull/stat.c | 717 +++ xs/src/qhull/src/libqhull/stat.h | 543 +++ xs/src/qhull/src/libqhull/user.c | 538 +++ xs/src/qhull/src/libqhull/user.h | 909 ++++ xs/src/qhull/src/libqhull/usermem.c | 94 + xs/src/qhull/src/libqhull/userprintf.c | 66 + xs/src/qhull/src/libqhull/userprintf_rbox.c | 53 + xs/src/qhull/src/libqhull_r/Makefile | 240 + xs/src/qhull/src/libqhull_r/geom2_r.c | 2096 +++++++++ xs/src/qhull/src/libqhull_r/geom_r.c | 1234 +++++ xs/src/qhull/src/libqhull_r/geom_r.h | 184 + xs/src/qhull/src/libqhull_r/global_r.c | 2100 +++++++++ xs/src/qhull/src/libqhull_r/index.htm | 266 ++ xs/src/qhull/src/libqhull_r/io_r.c | 4062 +++++++++++++++++ xs/src/qhull/src/libqhull_r/io_r.h | 167 + xs/src/qhull/src/libqhull_r/libqhull_r.c | 1403 ++++++ xs/src/qhull/src/libqhull_r/libqhull_r.h | 1134 +++++ xs/src/qhull/src/libqhull_r/libqhull_r.pro | 67 + xs/src/qhull/src/libqhull_r/mem_r.c | 562 +++ xs/src/qhull/src/libqhull_r/mem_r.h | 234 + xs/src/qhull/src/libqhull_r/merge_r.c | 3627 +++++++++++++++ xs/src/qhull/src/libqhull_r/merge_r.h | 186 + xs/src/qhull/src/libqhull_r/poly2_r.c | 3222 +++++++++++++ xs/src/qhull/src/libqhull_r/poly_r.c | 1205 +++++ xs/src/qhull/src/libqhull_r/poly_r.h | 303 ++ xs/src/qhull/src/libqhull_r/qh-geom_r.htm | 295 ++ xs/src/qhull/src/libqhull_r/qh-globa_r.htm | 163 + xs/src/qhull/src/libqhull_r/qh-io_r.htm | 305 ++ xs/src/qhull/src/libqhull_r/qh-mem_r.htm | 145 + xs/src/qhull/src/libqhull_r/qh-merge_r.htm | 366 ++ xs/src/qhull/src/libqhull_r/qh-poly_r.htm | 485 ++ xs/src/qhull/src/libqhull_r/qh-qhull_r.htm | 279 ++ xs/src/qhull/src/libqhull_r/qh-set_r.htm | 308 ++ xs/src/qhull/src/libqhull_r/qh-stat_r.htm | 161 + xs/src/qhull/src/libqhull_r/qh-user_r.htm | 271 ++ .../qhull/src/libqhull_r/qhull_r-exports.def | 404 ++ xs/src/qhull/src/libqhull_r/qhull_ra.h | 158 + xs/src/qhull/src/libqhull_r/qset_r.c | 1340 ++++++ xs/src/qhull/src/libqhull_r/qset_r.h | 502 ++ xs/src/qhull/src/libqhull_r/random_r.c | 247 + xs/src/qhull/src/libqhull_r/random_r.h | 41 + xs/src/qhull/src/libqhull_r/rboxlib_r.c | 842 ++++ xs/src/qhull/src/libqhull_r/stat_r.c | 682 +++ xs/src/qhull/src/libqhull_r/stat_r.h | 533 +++ xs/src/qhull/src/libqhull_r/user_r.c | 527 +++ xs/src/qhull/src/libqhull_r/user_r.h | 882 ++++ xs/src/qhull/src/libqhull_r/usermem_r.c | 94 + xs/src/qhull/src/libqhull_r/userprintf_r.c | 65 + .../qhull/src/libqhull_r/userprintf_rbox_r.c | 53 + xs/src/qhull/src/libqhullcpp/Coordinates.cpp | 198 + xs/src/qhull/src/libqhullcpp/Coordinates.h | 303 ++ .../src/libqhullcpp/PointCoordinates.cpp | 348 ++ .../qhull/src/libqhullcpp/PointCoordinates.h | 161 + xs/src/qhull/src/libqhullcpp/Qhull.cpp | 352 ++ xs/src/qhull/src/libqhullcpp/Qhull.h | 132 + xs/src/qhull/src/libqhullcpp/QhullError.h | 62 + xs/src/qhull/src/libqhullcpp/QhullFacet.cpp | 519 +++ xs/src/qhull/src/libqhullcpp/QhullFacet.h | 151 + .../qhull/src/libqhullcpp/QhullFacetList.cpp | 174 + xs/src/qhull/src/libqhullcpp/QhullFacetList.h | 106 + .../qhull/src/libqhullcpp/QhullFacetSet.cpp | 147 + xs/src/qhull/src/libqhullcpp/QhullFacetSet.h | 97 + .../qhull/src/libqhullcpp/QhullHyperplane.cpp | 187 + .../qhull/src/libqhullcpp/QhullHyperplane.h | 123 + xs/src/qhull/src/libqhullcpp/QhullIterator.h | 173 + .../qhull/src/libqhullcpp/QhullLinkedList.h | 388 ++ xs/src/qhull/src/libqhullcpp/QhullPoint.cpp | 203 + xs/src/qhull/src/libqhullcpp/QhullPoint.h | 136 + .../qhull/src/libqhullcpp/QhullPointSet.cpp | 62 + xs/src/qhull/src/libqhullcpp/QhullPointSet.h | 77 + xs/src/qhull/src/libqhullcpp/QhullPoints.cpp | 320 ++ xs/src/qhull/src/libqhullcpp/QhullPoints.h | 266 ++ xs/src/qhull/src/libqhullcpp/QhullQh.cpp | 237 + xs/src/qhull/src/libqhullcpp/QhullQh.h | 110 + xs/src/qhull/src/libqhullcpp/QhullRidge.cpp | 124 + xs/src/qhull/src/libqhullcpp/QhullRidge.h | 112 + xs/src/qhull/src/libqhullcpp/QhullSet.cpp | 62 + xs/src/qhull/src/libqhullcpp/QhullSet.h | 462 ++ xs/src/qhull/src/libqhullcpp/QhullSets.h | 27 + xs/src/qhull/src/libqhullcpp/QhullStat.cpp | 42 + xs/src/qhull/src/libqhullcpp/QhullStat.h | 49 + xs/src/qhull/src/libqhullcpp/QhullVertex.cpp | 112 + xs/src/qhull/src/libqhullcpp/QhullVertex.h | 104 + .../qhull/src/libqhullcpp/QhullVertexSet.cpp | 161 + xs/src/qhull/src/libqhullcpp/QhullVertexSet.h | 86 + xs/src/qhull/src/libqhullcpp/RboxPoints.cpp | 224 + xs/src/qhull/src/libqhullcpp/RboxPoints.h | 69 + xs/src/qhull/src/libqhullcpp/RoadError.cpp | 158 + xs/src/qhull/src/libqhullcpp/RoadError.h | 88 + xs/src/qhull/src/libqhullcpp/RoadLogEvent.cpp | 122 + xs/src/qhull/src/libqhullcpp/RoadLogEvent.h | 77 + .../qhull/src/libqhullcpp/functionObjects.h | 67 + xs/src/qhull/src/libqhullcpp/libqhullcpp.pro | 71 + xs/src/qhull/src/libqhullcpp/qt-qhull.cpp | 130 + .../qhull/src/libqhullcpp/usermem_r-cpp.cpp | 93 + .../src/libqhullstatic/libqhullstatic.pro | 19 + .../src/libqhullstatic_r/libqhullstatic_r.pro | 21 + xs/src/qhull/src/qconvex/qconvex.c | 326 ++ xs/src/qhull/src/qconvex/qconvex.pro | 9 + xs/src/qhull/src/qconvex/qconvex_r.c | 328 ++ xs/src/qhull/src/qdelaunay/qdelaun.c | 315 ++ xs/src/qhull/src/qdelaunay/qdelaun_r.c | 317 ++ xs/src/qhull/src/qdelaunay/qdelaunay.pro | 9 + xs/src/qhull/src/qhalf/qhalf.c | 316 ++ xs/src/qhull/src/qhalf/qhalf.pro | 9 + xs/src/qhull/src/qhalf/qhalf_r.c | 318 ++ xs/src/qhull/src/qhull-all.pro | 94 + xs/src/qhull/src/qhull-app-c.pri | 24 + xs/src/qhull/src/qhull-app-c_r.pri | 26 + xs/src/qhull/src/qhull-app-cpp.pri | 23 + xs/src/qhull/src/qhull-app-shared.pri | 27 + xs/src/qhull/src/qhull-app-shared_r.pri | 29 + xs/src/qhull/src/qhull-libqhull-src.pri | 39 + xs/src/qhull/src/qhull-libqhull-src_r.pri | 39 + xs/src/qhull/src/qhull-warn.pri | 57 + xs/src/qhull/src/qhull/qhull.pro | 9 + xs/src/qhull/src/qhull/unix.c | 372 ++ xs/src/qhull/src/qhull/unix_r.c | 374 ++ .../qhull/src/qhulltest/Coordinates_test.cpp | 539 +++ .../src/qhulltest/PointCoordinates_test.cpp | 478 ++ .../src/qhulltest/QhullFacetList_test.cpp | 196 + .../src/qhulltest/QhullFacetSet_test.cpp | 153 + .../qhull/src/qhulltest/QhullFacet_test.cpp | 283 ++ .../src/qhulltest/QhullHyperplane_test.cpp | 429 ++ .../src/qhulltest/QhullLinkedList_test.cpp | 330 ++ .../src/qhulltest/QhullPointSet_test.cpp | 378 ++ .../qhull/src/qhulltest/QhullPoint_test.cpp | 437 ++ .../qhull/src/qhulltest/QhullPoints_test.cpp | 561 +++ .../qhull/src/qhulltest/QhullRidge_test.cpp | 159 + xs/src/qhull/src/qhulltest/QhullSet_test.cpp | 434 ++ .../src/qhulltest/QhullVertexSet_test.cpp | 152 + .../qhull/src/qhulltest/QhullVertex_test.cpp | 184 + xs/src/qhull/src/qhulltest/Qhull_test.cpp | 360 ++ .../qhull/src/qhulltest/RboxPoints_test.cpp | 215 + xs/src/qhull/src/qhulltest/RoadTest.cpp | 94 + xs/src/qhull/src/qhulltest/RoadTest.h | 102 + xs/src/qhull/src/qhulltest/qhulltest.cpp | 94 + xs/src/qhull/src/qhulltest/qhulltest.pro | 36 + xs/src/qhull/src/qvoronoi/qvoronoi.c | 303 ++ xs/src/qhull/src/qvoronoi/qvoronoi.pro | 9 + xs/src/qhull/src/qvoronoi/qvoronoi_r.c | 305 ++ xs/src/qhull/src/rbox/rbox.c | 88 + xs/src/qhull/src/rbox/rbox.pro | 9 + xs/src/qhull/src/rbox/rbox_r.c | 78 + xs/src/qhull/src/testqset/testqset.c | 891 ++++ xs/src/qhull/src/testqset/testqset.pro | 30 + xs/src/qhull/src/testqset_r/testqset_r.c | 890 ++++ xs/src/qhull/src/testqset_r/testqset_r.pro | 30 + xs/src/qhull/src/user_eg/user_eg.c | 330 ++ xs/src/qhull/src/user_eg/user_eg.pro | 11 + xs/src/qhull/src/user_eg/user_eg_r.c | 326 ++ xs/src/qhull/src/user_eg2/user_eg2.c | 746 +++ xs/src/qhull/src/user_eg2/user_eg2.pro | 11 + xs/src/qhull/src/user_eg2/user_eg2_r.c | 742 +++ xs/src/qhull/src/user_eg3/user_eg3.pro | 12 + xs/src/qhull/src/user_eg3/user_eg3_r.cpp | 162 + 237 files changed, 104145 insertions(+), 1 deletion(-) create mode 100644 xs/src/qhull/Announce.txt create mode 100644 xs/src/qhull/CMakeLists.txt create mode 100644 xs/src/qhull/COPYING.txt create mode 100644 xs/src/qhull/README.txt create mode 100644 xs/src/qhull/REGISTER.txt create mode 100644 xs/src/qhull/html/index.htm create mode 100644 xs/src/qhull/html/normal_voronoi_knauss_oesterle.jpg create mode 100644 xs/src/qhull/html/qconvex.htm create mode 100644 xs/src/qhull/html/qdelau_f.htm create mode 100644 xs/src/qhull/html/qdelaun.htm create mode 100644 xs/src/qhull/html/qh--4d.gif create mode 100644 xs/src/qhull/html/qh--cone.gif create mode 100644 xs/src/qhull/html/qh--dt.gif create mode 100644 xs/src/qhull/html/qh--geom.gif create mode 100644 xs/src/qhull/html/qh--half.gif create mode 100644 xs/src/qhull/html/qh--rand.gif create mode 100644 xs/src/qhull/html/qh-code.htm create mode 100644 xs/src/qhull/html/qh-eg.htm create mode 100644 xs/src/qhull/html/qh-faq.htm create mode 100644 xs/src/qhull/html/qh-get.htm create mode 100644 xs/src/qhull/html/qh-impre.htm create mode 100644 xs/src/qhull/html/qh-optc.htm create mode 100644 xs/src/qhull/html/qh-optf.htm create mode 100644 xs/src/qhull/html/qh-optg.htm create mode 100644 xs/src/qhull/html/qh-opto.htm create mode 100644 xs/src/qhull/html/qh-optp.htm create mode 100644 xs/src/qhull/html/qh-optq.htm create mode 100644 xs/src/qhull/html/qh-optt.htm create mode 100644 xs/src/qhull/html/qh-quick.htm create mode 100644 xs/src/qhull/html/qhalf.htm create mode 100644 xs/src/qhull/html/qhull-cpp.xml create mode 100644 xs/src/qhull/html/qhull.htm create mode 100644 xs/src/qhull/html/qhull.man create mode 100644 xs/src/qhull/html/qhull.txt create mode 100644 xs/src/qhull/html/qvoron_f.htm create mode 100644 xs/src/qhull/html/qvoronoi.htm create mode 100644 xs/src/qhull/html/rbox.htm create mode 100644 xs/src/qhull/html/rbox.man create mode 100644 xs/src/qhull/html/rbox.txt create mode 100644 xs/src/qhull/index.htm create mode 100644 xs/src/qhull/origCMakeLists.txt create mode 100644 xs/src/qhull/src/Changes.txt create mode 100644 xs/src/qhull/src/libqhull/DEPRECATED.txt create mode 100644 xs/src/qhull/src/libqhull/Makefile create mode 100644 xs/src/qhull/src/libqhull/Mborland create mode 100644 xs/src/qhull/src/libqhull/geom.c create mode 100644 xs/src/qhull/src/libqhull/geom.h create mode 100644 xs/src/qhull/src/libqhull/geom2.c create mode 100644 xs/src/qhull/src/libqhull/global.c create mode 100644 xs/src/qhull/src/libqhull/index.htm create mode 100644 xs/src/qhull/src/libqhull/io.c create mode 100644 xs/src/qhull/src/libqhull/io.h create mode 100644 xs/src/qhull/src/libqhull/libqhull.c create mode 100644 xs/src/qhull/src/libqhull/libqhull.h create mode 100644 xs/src/qhull/src/libqhull/libqhull.pro create mode 100644 xs/src/qhull/src/libqhull/mem.c create mode 100644 xs/src/qhull/src/libqhull/mem.h create mode 100644 xs/src/qhull/src/libqhull/merge.c create mode 100644 xs/src/qhull/src/libqhull/merge.h create mode 100644 xs/src/qhull/src/libqhull/poly.c create mode 100644 xs/src/qhull/src/libqhull/poly.h create mode 100644 xs/src/qhull/src/libqhull/poly2.c create mode 100644 xs/src/qhull/src/libqhull/qh-geom.htm create mode 100644 xs/src/qhull/src/libqhull/qh-globa.htm create mode 100644 xs/src/qhull/src/libqhull/qh-io.htm create mode 100644 xs/src/qhull/src/libqhull/qh-mem.htm create mode 100644 xs/src/qhull/src/libqhull/qh-merge.htm create mode 100644 xs/src/qhull/src/libqhull/qh-poly.htm create mode 100644 xs/src/qhull/src/libqhull/qh-qhull.htm create mode 100644 xs/src/qhull/src/libqhull/qh-set.htm create mode 100644 xs/src/qhull/src/libqhull/qh-stat.htm create mode 100644 xs/src/qhull/src/libqhull/qh-user.htm create mode 100644 xs/src/qhull/src/libqhull/qhull-exports.def create mode 100644 xs/src/qhull/src/libqhull/qhull_a.h create mode 100644 xs/src/qhull/src/libqhull/qhull_p-exports.def create mode 100644 xs/src/qhull/src/libqhull/qset.c create mode 100644 xs/src/qhull/src/libqhull/qset.h create mode 100644 xs/src/qhull/src/libqhull/random.c create mode 100644 xs/src/qhull/src/libqhull/random.h create mode 100644 xs/src/qhull/src/libqhull/rboxlib.c create mode 100644 xs/src/qhull/src/libqhull/stat.c create mode 100644 xs/src/qhull/src/libqhull/stat.h create mode 100644 xs/src/qhull/src/libqhull/user.c create mode 100644 xs/src/qhull/src/libqhull/user.h create mode 100644 xs/src/qhull/src/libqhull/usermem.c create mode 100644 xs/src/qhull/src/libqhull/userprintf.c create mode 100644 xs/src/qhull/src/libqhull/userprintf_rbox.c create mode 100644 xs/src/qhull/src/libqhull_r/Makefile create mode 100644 xs/src/qhull/src/libqhull_r/geom2_r.c create mode 100644 xs/src/qhull/src/libqhull_r/geom_r.c create mode 100644 xs/src/qhull/src/libqhull_r/geom_r.h create mode 100644 xs/src/qhull/src/libqhull_r/global_r.c create mode 100644 xs/src/qhull/src/libqhull_r/index.htm create mode 100644 xs/src/qhull/src/libqhull_r/io_r.c create mode 100644 xs/src/qhull/src/libqhull_r/io_r.h create mode 100644 xs/src/qhull/src/libqhull_r/libqhull_r.c create mode 100644 xs/src/qhull/src/libqhull_r/libqhull_r.h create mode 100644 xs/src/qhull/src/libqhull_r/libqhull_r.pro create mode 100644 xs/src/qhull/src/libqhull_r/mem_r.c create mode 100644 xs/src/qhull/src/libqhull_r/mem_r.h create mode 100644 xs/src/qhull/src/libqhull_r/merge_r.c create mode 100644 xs/src/qhull/src/libqhull_r/merge_r.h create mode 100644 xs/src/qhull/src/libqhull_r/poly2_r.c create mode 100644 xs/src/qhull/src/libqhull_r/poly_r.c create mode 100644 xs/src/qhull/src/libqhull_r/poly_r.h create mode 100644 xs/src/qhull/src/libqhull_r/qh-geom_r.htm create mode 100644 xs/src/qhull/src/libqhull_r/qh-globa_r.htm create mode 100644 xs/src/qhull/src/libqhull_r/qh-io_r.htm create mode 100644 xs/src/qhull/src/libqhull_r/qh-mem_r.htm create mode 100644 xs/src/qhull/src/libqhull_r/qh-merge_r.htm create mode 100644 xs/src/qhull/src/libqhull_r/qh-poly_r.htm create mode 100644 xs/src/qhull/src/libqhull_r/qh-qhull_r.htm create mode 100644 xs/src/qhull/src/libqhull_r/qh-set_r.htm create mode 100644 xs/src/qhull/src/libqhull_r/qh-stat_r.htm create mode 100644 xs/src/qhull/src/libqhull_r/qh-user_r.htm create mode 100644 xs/src/qhull/src/libqhull_r/qhull_r-exports.def create mode 100644 xs/src/qhull/src/libqhull_r/qhull_ra.h create mode 100644 xs/src/qhull/src/libqhull_r/qset_r.c create mode 100644 xs/src/qhull/src/libqhull_r/qset_r.h create mode 100644 xs/src/qhull/src/libqhull_r/random_r.c create mode 100644 xs/src/qhull/src/libqhull_r/random_r.h create mode 100644 xs/src/qhull/src/libqhull_r/rboxlib_r.c create mode 100644 xs/src/qhull/src/libqhull_r/stat_r.c create mode 100644 xs/src/qhull/src/libqhull_r/stat_r.h create mode 100644 xs/src/qhull/src/libqhull_r/user_r.c create mode 100644 xs/src/qhull/src/libqhull_r/user_r.h create mode 100644 xs/src/qhull/src/libqhull_r/usermem_r.c create mode 100644 xs/src/qhull/src/libqhull_r/userprintf_r.c create mode 100644 xs/src/qhull/src/libqhull_r/userprintf_rbox_r.c create mode 100644 xs/src/qhull/src/libqhullcpp/Coordinates.cpp create mode 100644 xs/src/qhull/src/libqhullcpp/Coordinates.h create mode 100644 xs/src/qhull/src/libqhullcpp/PointCoordinates.cpp create mode 100644 xs/src/qhull/src/libqhullcpp/PointCoordinates.h create mode 100644 xs/src/qhull/src/libqhullcpp/Qhull.cpp create mode 100644 xs/src/qhull/src/libqhullcpp/Qhull.h create mode 100644 xs/src/qhull/src/libqhullcpp/QhullError.h create mode 100644 xs/src/qhull/src/libqhullcpp/QhullFacet.cpp create mode 100644 xs/src/qhull/src/libqhullcpp/QhullFacet.h create mode 100644 xs/src/qhull/src/libqhullcpp/QhullFacetList.cpp create mode 100644 xs/src/qhull/src/libqhullcpp/QhullFacetList.h create mode 100644 xs/src/qhull/src/libqhullcpp/QhullFacetSet.cpp create mode 100644 xs/src/qhull/src/libqhullcpp/QhullFacetSet.h create mode 100644 xs/src/qhull/src/libqhullcpp/QhullHyperplane.cpp create mode 100644 xs/src/qhull/src/libqhullcpp/QhullHyperplane.h create mode 100644 xs/src/qhull/src/libqhullcpp/QhullIterator.h create mode 100644 xs/src/qhull/src/libqhullcpp/QhullLinkedList.h create mode 100644 xs/src/qhull/src/libqhullcpp/QhullPoint.cpp create mode 100644 xs/src/qhull/src/libqhullcpp/QhullPoint.h create mode 100644 xs/src/qhull/src/libqhullcpp/QhullPointSet.cpp create mode 100644 xs/src/qhull/src/libqhullcpp/QhullPointSet.h create mode 100644 xs/src/qhull/src/libqhullcpp/QhullPoints.cpp create mode 100644 xs/src/qhull/src/libqhullcpp/QhullPoints.h create mode 100644 xs/src/qhull/src/libqhullcpp/QhullQh.cpp create mode 100644 xs/src/qhull/src/libqhullcpp/QhullQh.h create mode 100644 xs/src/qhull/src/libqhullcpp/QhullRidge.cpp create mode 100644 xs/src/qhull/src/libqhullcpp/QhullRidge.h create mode 100644 xs/src/qhull/src/libqhullcpp/QhullSet.cpp create mode 100644 xs/src/qhull/src/libqhullcpp/QhullSet.h create mode 100644 xs/src/qhull/src/libqhullcpp/QhullSets.h create mode 100644 xs/src/qhull/src/libqhullcpp/QhullStat.cpp create mode 100644 xs/src/qhull/src/libqhullcpp/QhullStat.h create mode 100644 xs/src/qhull/src/libqhullcpp/QhullVertex.cpp create mode 100644 xs/src/qhull/src/libqhullcpp/QhullVertex.h create mode 100644 xs/src/qhull/src/libqhullcpp/QhullVertexSet.cpp create mode 100644 xs/src/qhull/src/libqhullcpp/QhullVertexSet.h create mode 100644 xs/src/qhull/src/libqhullcpp/RboxPoints.cpp create mode 100644 xs/src/qhull/src/libqhullcpp/RboxPoints.h create mode 100644 xs/src/qhull/src/libqhullcpp/RoadError.cpp create mode 100644 xs/src/qhull/src/libqhullcpp/RoadError.h create mode 100644 xs/src/qhull/src/libqhullcpp/RoadLogEvent.cpp create mode 100644 xs/src/qhull/src/libqhullcpp/RoadLogEvent.h create mode 100644 xs/src/qhull/src/libqhullcpp/functionObjects.h create mode 100644 xs/src/qhull/src/libqhullcpp/libqhullcpp.pro create mode 100644 xs/src/qhull/src/libqhullcpp/qt-qhull.cpp create mode 100644 xs/src/qhull/src/libqhullcpp/usermem_r-cpp.cpp create mode 100644 xs/src/qhull/src/libqhullstatic/libqhullstatic.pro create mode 100644 xs/src/qhull/src/libqhullstatic_r/libqhullstatic_r.pro create mode 100644 xs/src/qhull/src/qconvex/qconvex.c create mode 100644 xs/src/qhull/src/qconvex/qconvex.pro create mode 100644 xs/src/qhull/src/qconvex/qconvex_r.c create mode 100644 xs/src/qhull/src/qdelaunay/qdelaun.c create mode 100644 xs/src/qhull/src/qdelaunay/qdelaun_r.c create mode 100644 xs/src/qhull/src/qdelaunay/qdelaunay.pro create mode 100644 xs/src/qhull/src/qhalf/qhalf.c create mode 100644 xs/src/qhull/src/qhalf/qhalf.pro create mode 100644 xs/src/qhull/src/qhalf/qhalf_r.c create mode 100644 xs/src/qhull/src/qhull-all.pro create mode 100644 xs/src/qhull/src/qhull-app-c.pri create mode 100644 xs/src/qhull/src/qhull-app-c_r.pri create mode 100644 xs/src/qhull/src/qhull-app-cpp.pri create mode 100644 xs/src/qhull/src/qhull-app-shared.pri create mode 100644 xs/src/qhull/src/qhull-app-shared_r.pri create mode 100644 xs/src/qhull/src/qhull-libqhull-src.pri create mode 100644 xs/src/qhull/src/qhull-libqhull-src_r.pri create mode 100644 xs/src/qhull/src/qhull-warn.pri create mode 100644 xs/src/qhull/src/qhull/qhull.pro create mode 100644 xs/src/qhull/src/qhull/unix.c create mode 100644 xs/src/qhull/src/qhull/unix_r.c create mode 100644 xs/src/qhull/src/qhulltest/Coordinates_test.cpp create mode 100644 xs/src/qhull/src/qhulltest/PointCoordinates_test.cpp create mode 100644 xs/src/qhull/src/qhulltest/QhullFacetList_test.cpp create mode 100644 xs/src/qhull/src/qhulltest/QhullFacetSet_test.cpp create mode 100644 xs/src/qhull/src/qhulltest/QhullFacet_test.cpp create mode 100644 xs/src/qhull/src/qhulltest/QhullHyperplane_test.cpp create mode 100644 xs/src/qhull/src/qhulltest/QhullLinkedList_test.cpp create mode 100644 xs/src/qhull/src/qhulltest/QhullPointSet_test.cpp create mode 100644 xs/src/qhull/src/qhulltest/QhullPoint_test.cpp create mode 100644 xs/src/qhull/src/qhulltest/QhullPoints_test.cpp create mode 100644 xs/src/qhull/src/qhulltest/QhullRidge_test.cpp create mode 100644 xs/src/qhull/src/qhulltest/QhullSet_test.cpp create mode 100644 xs/src/qhull/src/qhulltest/QhullVertexSet_test.cpp create mode 100644 xs/src/qhull/src/qhulltest/QhullVertex_test.cpp create mode 100644 xs/src/qhull/src/qhulltest/Qhull_test.cpp create mode 100644 xs/src/qhull/src/qhulltest/RboxPoints_test.cpp create mode 100644 xs/src/qhull/src/qhulltest/RoadTest.cpp create mode 100644 xs/src/qhull/src/qhulltest/RoadTest.h create mode 100644 xs/src/qhull/src/qhulltest/qhulltest.cpp create mode 100644 xs/src/qhull/src/qhulltest/qhulltest.pro create mode 100644 xs/src/qhull/src/qvoronoi/qvoronoi.c create mode 100644 xs/src/qhull/src/qvoronoi/qvoronoi.pro create mode 100644 xs/src/qhull/src/qvoronoi/qvoronoi_r.c create mode 100644 xs/src/qhull/src/rbox/rbox.c create mode 100644 xs/src/qhull/src/rbox/rbox.pro create mode 100644 xs/src/qhull/src/rbox/rbox_r.c create mode 100644 xs/src/qhull/src/testqset/testqset.c create mode 100644 xs/src/qhull/src/testqset/testqset.pro create mode 100644 xs/src/qhull/src/testqset_r/testqset_r.c create mode 100644 xs/src/qhull/src/testqset_r/testqset_r.pro create mode 100644 xs/src/qhull/src/user_eg/user_eg.c create mode 100644 xs/src/qhull/src/user_eg/user_eg.pro create mode 100644 xs/src/qhull/src/user_eg/user_eg_r.c create mode 100644 xs/src/qhull/src/user_eg2/user_eg2.c create mode 100644 xs/src/qhull/src/user_eg2/user_eg2.pro create mode 100644 xs/src/qhull/src/user_eg2/user_eg2_r.c create mode 100644 xs/src/qhull/src/user_eg3/user_eg3.pro create mode 100644 xs/src/qhull/src/user_eg3/user_eg3_r.cpp diff --git a/xs/CMakeLists.txt b/xs/CMakeLists.txt index d41b4c13a..f1609bc14 100644 --- a/xs/CMakeLists.txt +++ b/xs/CMakeLists.txt @@ -459,7 +459,7 @@ if(APPLE) # Ignore undefined symbols of the perl interpreter, they will be found in the caller image. target_link_libraries(XS "-undefined dynamic_lookup") endif() -target_link_libraries(XS libslic3r libslic3r_gui admesh miniz clipper nowide polypartition poly2tri semver avrdude) +target_link_libraries(XS libslic3r libslic3r_gui admesh miniz clipper nowide polypartition poly2tri semver avrdude qhull) if(SLIC3R_PROFILE) target_link_libraries(XS Shiny) endif() @@ -540,6 +540,10 @@ endif() add_subdirectory(src/avrdude) +add_subdirectory(src/qhull) +include_directories(${LIBDIR}/qhull/src) +message(STATUS ${LIBDIR}/qhull/src) + ## REQUIRED packages # Find and configure boost diff --git a/xs/src/qhull/Announce.txt b/xs/src/qhull/Announce.txt new file mode 100644 index 000000000..635cff1af --- /dev/null +++ b/xs/src/qhull/Announce.txt @@ -0,0 +1,47 @@ + + Qhull 2015.2 2016/01/18 + + http://www.qhull.org + git@github.com:qhull/qhull.git + http://www.geomview.org + +Qhull computes convex hulls, Delaunay triangulations, Voronoi diagrams, +furthest-site Voronoi diagrams, and halfspace intersections about a point. +It runs in 2-d, 3-d, 4-d, or higher. It implements the Quickhull algorithm +for computing convex hulls. Qhull handles round-off errors from floating +point arithmetic. It can approximate a convex hull. + +The program includes options for hull volume, facet area, partial hulls, +input transformations, randomization, tracing, multiple output formats, and +execution statistics. The program can be called from within your application. +You can view the results in 2-d, 3-d and 4-d with Geomview. + +To download Qhull: + http://www.qhull.org/download + git@github.com:qhull/qhull.git + +Download qhull-96.ps for: + + Barber, C. B., D.P. Dobkin, and H.T. Huhdanpaa, "The + Quickhull Algorithm for Convex Hulls," ACM Trans. on + Mathematical Software, 22(4):469-483, Dec. 1996. + http://www.acm.org/pubs/citations/journals/toms/1996-22-4/p469-barber/ + http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.117.405 + +Abstract: + +The convex hull of a set of points is the smallest convex set that contains +the points. This article presents a practical convex hull algorithm that +combines the two-dimensional Quickhull Algorithm with the general dimension +Beneath-Beyond Algorithm. It is similar to the randomized, incremental +algorithms for convex hull and Delaunay triangulation. We provide empirical +evidence that the algorithm runs faster when the input contains non-extreme +points, and that it uses less memory. + +Computational geometry algorithms have traditionally assumed that input sets +are well behaved. When an algorithm is implemented with floating point +arithmetic, this assumption can lead to serious errors. We briefly describe +a solution to this problem when computing the convex hull in two, three, or +four dimensions. The output is a set of "thick" facets that contain all +possible exact convex hulls of the input. A variation is effective in five +or more dimensions. diff --git a/xs/src/qhull/CMakeLists.txt b/xs/src/qhull/CMakeLists.txt new file mode 100644 index 000000000..d798b018f --- /dev/null +++ b/xs/src/qhull/CMakeLists.txt @@ -0,0 +1,128 @@ + +# This CMake file is written specifically to integrate qhull library with Slic3rPE +# (see https://github.com/prusa3d/Slic3r for more information about the project) +# +# Only original libraries qhullstatic_r and qhullcpp are included. +# They are built as a single statically linked library. +# +# Created by modification of the original qhull CMakeLists. +# Lukas Matena (25.7.2018), lukasmatena@seznam.cz + + +project(qhull) +cmake_minimum_required(VERSION 2.6) + +# Define qhull_VERSION in CMakeLists.txt, Makefile, qhull-exports.def, qhull_p-exports.def, qhull_r-exports.def, qhull-warn.pri +set(qhull_VERSION2 "2015.2 2016/01/18") # not used, See global.c, global_r.c, rbox.c, rbox_r.c +set(qhull_VERSION "7.2.0") # Advance every release + +#include(CMakeModules/CheckLFS.cmake) +#option(WITH_LFS "Enable Large File Support" ON) +#check_lfs(WITH_LFS) + + +message(STATUS "qhull Version: ${qhull_VERSION} (static linking)") + + +set(libqhull_HEADERS + # reentrant qhull HEADERS: + src/libqhull_r/libqhull_r.h + src/libqhull_r/geom_r.h + src/libqhull_r/io_r.h + src/libqhull_r/mem_r.h + src/libqhull_r/merge_r.h + src/libqhull_r/poly_r.h + src/libqhull_r/qhull_ra.h + src/libqhull_r/qset_r.h + src/libqhull_r/random_r.h + src/libqhull_r/stat_r.h + src/libqhull_r/user_r.h + + # C++ interface to reentrant Qhull HEADERS: + src/libqhullcpp/Coordinates.h + src/libqhullcpp/functionObjects.h + src/libqhullcpp/PointCoordinates.h + src/libqhullcpp/Qhull.h + src/libqhullcpp/QhullError.h + src/libqhullcpp/QhullFacet.h + src/libqhullcpp/QhullFacetList.h + src/libqhullcpp/QhullFacetSet.h + src/libqhullcpp/QhullHyperplane.h + src/libqhullcpp/QhullIterator.h + src/libqhullcpp/QhullLinkedList.h + src/libqhullcpp/QhullPoint.h + src/libqhullcpp/QhullPoints.h + src/libqhullcpp/QhullPointSet.h + src/libqhullcpp/QhullQh.h + src/libqhullcpp/QhullRidge.h + src/libqhullcpp/QhullSet.h + src/libqhullcpp/QhullSets.h + src/libqhullcpp/QhullStat.h + src/libqhullcpp/QhullVertex.h + src/libqhullcpp/QhullVertexSet.h + src/libqhullcpp/RboxPoints.h + src/libqhullcpp/RoadError.h + src/libqhullcpp/RoadLogEvent.h + src/qhulltest/RoadTest.h +) + +set(libqhull_SOURCES + # reentrant qhull SOURCES: + src/libqhull_r/global_r.c + src/libqhull_r/stat_r.c + src/libqhull_r/geom2_r.c + src/libqhull_r/poly2_r.c + src/libqhull_r/merge_r.c + src/libqhull_r/libqhull_r.c + src/libqhull_r/geom_r.c + src/libqhull_r/poly_r.c + src/libqhull_r/qset_r.c + src/libqhull_r/mem_r.c + src/libqhull_r/random_r.c + src/libqhull_r/usermem_r.c + src/libqhull_r/userprintf_r.c + src/libqhull_r/io_r.c + src/libqhull_r/user_r.c + src/libqhull_r/rboxlib_r.c + src/libqhull_r/userprintf_rbox_r.c + + # C++ interface to reentrant Qhull SOURCES: + src/libqhullcpp/Coordinates.cpp + src/libqhullcpp/PointCoordinates.cpp + src/libqhullcpp/Qhull.cpp + src/libqhullcpp/QhullFacet.cpp + src/libqhullcpp/QhullFacetList.cpp + src/libqhullcpp/QhullFacetSet.cpp + src/libqhullcpp/QhullHyperplane.cpp + src/libqhullcpp/QhullPoint.cpp + src/libqhullcpp/QhullPointSet.cpp + src/libqhullcpp/QhullPoints.cpp + src/libqhullcpp/QhullQh.cpp + src/libqhullcpp/QhullRidge.cpp + src/libqhullcpp/QhullSet.cpp + src/libqhullcpp/QhullStat.cpp + src/libqhullcpp/QhullVertex.cpp + src/libqhullcpp/QhullVertexSet.cpp + src/libqhullcpp/RboxPoints.cpp + src/libqhullcpp/RoadError.cpp + src/libqhullcpp/RoadLogEvent.cpp + + # headers for both (libqhullr and libqhullcpp: + ${libqhull_HEADERS} +) + + +################################################## +# combined library (reentrant qhull and qhullcpp) for Slic3r: +set(qhull_STATIC qhull) +add_library(${qhull_STATIC} STATIC ${libqhull_SOURCES}) +set_target_properties(${qhull_STATIC} PROPERTIES + VERSION ${qhull_VERSION}) + +if(UNIX) + target_link_libraries(${qhull_STATIC} m) +endif(UNIX) +################################################## + +# LIBDIR is defined in the main xs CMake file: +target_include_directories(${qhull_STATIC} PRIVATE ${LIBDIR}/qhull/src) diff --git a/xs/src/qhull/COPYING.txt b/xs/src/qhull/COPYING.txt new file mode 100644 index 000000000..2895ec6a3 --- /dev/null +++ b/xs/src/qhull/COPYING.txt @@ -0,0 +1,38 @@ + Qhull, Copyright (c) 1993-2015 + + C.B. Barber + Arlington, MA + + and + + The National Science and Technology Research Center for + Computation and Visualization of Geometric Structures + (The Geometry Center) + University of Minnesota + + email: qhull@qhull.org + +This software includes Qhull from C.B. Barber and The Geometry Center. +Qhull is copyrighted as noted above. Qhull is free software and may +be obtained via http from www.qhull.org. It may be freely copied, modified, +and redistributed under the following conditions: + +1. All copyright notices must remain intact in all files. + +2. A copy of this text file must be distributed along with any copies + of Qhull that you redistribute; this includes copies that you have + modified, or copies of programs or other software products that + include Qhull. + +3. If you modify Qhull, you must include a notice giving the + name of the person performing the modification, the date of + modification, and the reason for such modification. + +4. When distributing modified versions of Qhull, or other software + products that include Qhull, you must provide notice that the original + source code may be obtained as noted above. + +5. There is no warranty or other guarantee of fitness for Qhull, it is + provided solely "as is". Bug reports or fixes may be sent to + qhull_bug@qhull.org; the authors may or may not act on them as + they desire. diff --git a/xs/src/qhull/README.txt b/xs/src/qhull/README.txt new file mode 100644 index 000000000..f4c7a3b22 --- /dev/null +++ b/xs/src/qhull/README.txt @@ -0,0 +1,623 @@ +This distribution of qhull library is only meant for interfacing qhull with Slic3rPE +(https://github.com/prusa3d/Slic3r). + +The qhull source file was acquired from https://github.com/qhull/qhull at revision +f0bd8ceeb84b554d7cdde9bbfae7d3351270478c. + +No changes to the qhull library were made, except for +- setting REALfloat=1 in user_r.h to enforce calculations in floats +- modifying CMakeLists.txt (the original was renamed to origCMakeLists.txt) + +Many thanks to C. Bradford Barber and all contributors. + +Lukas Matena (lukasmatena@seznam.cz) +25.7.2018 + + +See original contents of the README file below. + +====================================================================================== +====================================================================================== +====================================================================================== + + +Name + + qhull, rbox 2015.2 2016/01/18 + +Convex hull, Delaunay triangulation, Voronoi diagrams, Halfspace intersection + + Documentation: + html/index.htm + + + Available from: + + + (git@github.com:qhull/qhull.git) + + News and a paper: + + + + Version 1 (simplicial only): + + +Purpose + + Qhull is a general dimension convex hull program that reads a set + of points from stdin, and outputs the smallest convex set that contains + the points to stdout. It also generates Delaunay triangulations, Voronoi + diagrams, furthest-site Voronoi diagrams, and halfspace intersections + about a point. + + Rbox is a useful tool in generating input for Qhull; it generates + hypercubes, diamonds, cones, circles, simplices, spirals, + lattices, and random points. + + Qhull produces graphical output for Geomview. This helps with + understanding the output. + +Environment requirements + + Qhull and rbox should run on all 32-bit and 64-bit computers. Use + an ANSI C or C++ compiler to compile the program. The software is + self-contained. It comes with examples and test scripts. + + Qhull's C++ interface uses the STL. The C++ test program uses QTestLib + from the Qt Framework. Qhull's C++ interface may change without + notice. Eventually, it will move into the qhull shared library. + + Qhull is copyrighted software. Please read COPYING.txt and REGISTER.txt + before using or distributing Qhull. + +To cite Qhull, please use + + Barber, C.B., Dobkin, D.P., and Huhdanpaa, H.T., "The Quickhull + algorithm for convex hulls," ACM Trans. on Mathematical Software, + 22(4):469-483, Dec 1996, http://www.qhull.org. + +To modify Qhull, particularly the C++ interface + + Qhull is on GitHub + (http://github.com/qhull/qhull, git@github.com:qhull/qhull.git) + + For internal documentation, see html/qh-code.htm + +To install Qhull + + Qhull is precompiled for Windows 32-bit, otherwise it needs compilation. + + Qhull includes Makefiles for gcc and other targets, CMakeLists.txt for CMake, + .sln/.vcproj/.vcxproj files for Microsoft Visual Studio, and .pro files + for Qt Creator. It compiles under Windows with mingw. + + Install and build instructions follow. + + See the end of this document for a list of distributed files. + +----------------- +Installing Qhull on Windows 10, 8, 7 (32- or 64-bit), Windows XP, and Windows NT + + The zip file contains rbox.exe, qhull.exe, qconvex.exe, qdelaunay.exe, + qhalf.exe, qvoronoi.exe, testqset.exe, user_eg*.exe, documentation files, + and source files. Qhull.exe and user-eg3.exe are compiled with the reentrant + library while the other executables use the non-reentrant library. + + To install Qhull: + - Unzip the files into a directory (e.g., named 'qhull') + - Click on QHULL-GO or open a command window into Qhull's bin directory. + - Test with 'rbox D4 | qhull' + + To uninstall Qhull + - Delete the qhull directory + + To learn about Qhull: + - Execute 'qconvex' for a synopsis and examples. + - Execute 'rbox 10 | qconvex' to compute the convex hull of 10 random points. + - Execute 'rbox 10 | qconvex i TO file' to write results to 'file'. + - Browse the documentation: qhull\html\index.htm + - If an error occurs, Windows sends the error to stdout instead of stderr. + Use 'TO xxx' to send normal output to xxx + + To improve the command window + - Double-click the window bar to increase the size of the window + - Right-click the window bar + - Select Properties + - Check QuickEdit Mode + Select text with right-click or Enter + Paste text with right-click + - Change Font to Lucinda Console + - Change Layout to Screen Buffer Height 999, Window Size Height 55 + - Change Colors to Screen Background White, Screen Text Black + - Click OK + - Select 'Modify shortcut that started this window', then OK + + If you use qhull a lot, install a bash shell such as + MSYS (www.mingw.org/wiki/msys), Road Bash (www.qhull.org/bash), + or Cygwin (www.cygwin.com). + +----------------- +Installing Qhull on Unix with gcc + + To build Qhull, static libraries, shared library, and C++ interface + - Download and extract Qhull (either GitHub, .tgz file, or .zip file) + - make + - export LD_LIBRARY_PATH=$PWD/lib:$LD_LIBRARY_PATH + + The Makefiles may be edited for other compilers. + If 'testqset' exits with an error, qhull is broken + + A simple Makefile for Qhull is in src/libqhull and src/libqhull_r. + To build the Qhull executables and libqhullstatic + - Extract Qhull from qhull...tgz or qhull...zip + - cd src/libqhull_r # cd src/libqhull + - make + + +----------------- +Installing Qhull with CMake 2.6 or later + + See CMakeLists.txt for examples and further build instructions + + To build Qhull, static libraries, shared library, and C++ interface + - Download and extract Qhull (either GitHub, .tgz file, or .zip file) + - cd build + - cmake --help # List build generators + - make -G "" .. && cmake .. + - cmake .. + - make + - make install + + The ".." is important. It refers to the parent directory (i.e., qhull/) + + On Windows, CMake installs to C:/Program Files/qhull. 64-bit generators + have a "Win64" tag. + + If creating a qhull package, please include a pkg-config file based on build/qhull*.pc.in + + If cmake fails with "No CMAKE_C_COMPILER could be found" + - cmake was not able to find the build environment specified by -G "..." + +----------------- +Installing Qhull with Qt + + To build Qhull, including its C++ test (qhulltest) + - Download and extract Qhull (either GitHub, .tgz file, or .zip file) + - Load src/qhull-all.pro into QtCreator + - Build + +------------------- +Working with Qhull's C++ interface + + See html/qh-code.htm#cpp for calling Qhull from C++ programs + + See html/qh-code.htm#reentrant for converting from Qhull-2012 + + Examples of using the C++ interface + user_eg3_r.cpp + qhulltest/*_test.cpp + + Qhull's C++ interface is likely to change. Stay current with GitHub. + + To clone Qhull's next branch from http://github.com/qhull/qhull + git init + git clone git@github.com:qhull/qhull.git + cd qhull + git checkout next + ... + git pull origin next + + Compile qhullcpp and libqhullstatic_r with the same compiler. Both libraries + use the C routines setjmp() and longjmp() for error handling. They must + be compiled with the same compiler. + +------------------- +Calling Qhull from C programs + + See html/qh-code.htm#library for calling Qhull from C programs + + See html/qh-code.htm#reentrant for converting from Qhull-2012 + + Warning: You will need to understand Qhull's data structures and read the + code. Most users will find it easier to call Qhull as an external command. + + The new, reentrant 'C' code (src/libqhull_r), passes a pointer to qhT + to most Qhull routines. This allows multiple instances of Qhull to run + at the same time. It simplifies the C++ interface. + + The non-reentrant 'C' code (src/libqhull) looks unusual. It refers to + Qhull's global data structure, qhT, through a 'qh' macro (e.g., 'qh ferr'). + This allows the same code to use static memory or heap memory. + If qh_QHpointer is defined, qh_qh is a pointer to an allocated qhT; + otherwise qh_qh is a global static data structure of type qhT. + +------------------ +Compiling Qhull with Microsoft Visual C++ + + To compile 32-bit Qhull with Microsoft Visual C++ 2010 and later + - Download and extract Qhull (either GitHub, .tgz file, or .zip file) + - Load solution build/qhull-32.sln + - Build target 'Win32' + - Project qhulltest requires Qt for DevStudio (http://www.qt.io) + Set the QTDIR environment variable to your Qt directory (e.g., c:/qt/5.2.0/5.2.0/msvc2012) + If QTDIR is incorrect, precompile will fail with 'Can not locate the file specified' + + To compile 64-bit Qhull with Microsoft Visual C++ 2010 and later + - 64-bit Qhull has larger data structures due to 64-bit pointers + - Download and extract Qhull (either GitHub, .tgz file, or .zip file) + - Load solution build/qhull-64.sln + - Build target 'Win32' + - Project qhulltest requires Qt for DevStudio (http://www.qt.io) + Set the QTDIR environment variable to your Qt directory (e.g., c:/qt/5.2.0/5.2.0/msvc2012_64) + If QTDIR is incorrect, precompile will fail with 'Can not locate the file specified' + + To compile Qhull with Microsoft Visual C++ 2005 (vcproj files) + - Download and extract Qhull (either GitHub, .tgz file, or .zip file) + - Load solution build/qhull.sln + - Build target 'win32' (not 'x64') + - Project qhulltest requires Qt for DevStudio (http://www.qt.io) + Set the QTDIR environment variable to your Qt directory (e.g., c:/qt/4.7.4) + If QTDIR is incorrect, precompile will fail with 'Can not locate the file specified' + +----------------- +Compiling Qhull with Qt Creator + + Qt (http://www.qt.io) is a C++ framework for Windows, Linux, and Macintosh + + Qhull uses QTestLib to test qhull's C++ interface (see src/qhulltest/) + + To compile Qhull with Qt Creator + - Download and extract Qhull (either GitHub, .tgz file, or .zip file) + - Download the Qt SDK + - Start Qt Creator + - Load src/qhull-all.pro + - Build + +----------------- +Compiling Qhull with mingw on Windows + + To compile Qhull with MINGW + - Download and extract Qhull (either GitHub, .tgz file, or .zip file) + - Install Road Bash (http://www.qhull.org/bash) + or install MSYS (http://www.mingw.org/wiki/msys) + - Install MINGW-w64 (http://sourceforge.net/projects/mingw-w64). + Mingw is included with Qt SDK. + - make + +----------------- +Compiling Qhull with cygwin on Windows + + To compile Qhull with cygwin + - Download and extract Qhull (either GitHub, .tgz file, or .zip file) + - Install cygwin (http://www.cygwin.com) + - Include packages for gcc, make, ar, and ln + - make + +----------------- +Compiling from Makfile without gcc + + The file, qhull-src.tgz, contains documentation and source files for + qhull and rbox. + + To unpack the tgz file + - tar zxf qhull-src.tgz + - cd qhull + - Use qhull/Makefile + Simpler Makefiles are qhull/src/libqhull/Makefile and qhull/src/libqhull_r/Makefile + + Compiling qhull and rbox with Makefile + - in Makefile, check the CC, CCOPTS1, PRINTMAN, and PRINTC defines + - the defaults are gcc and enscript + - CCOPTS1 should include the ANSI flag. It defines __STDC__ + - in user.h, check the definitions of qh_SECticks and qh_CPUclock. + - use '#define qh_CLOCKtype 2' for timing runs longer than 1 hour + - type: make + - this builds: qhull qconvex qdelaunay qhalf qvoronoi rbox libqhull.a libqhull_r.a + - type: make doc + - this prints the man page + - See also qhull/html/index.htm + - if your compiler reports many errors, it is probably not a ANSI C compiler + - you will need to set the -ansi switch or find another compiler + - if your compiler warns about missing prototypes for fprintf() etc. + - this is ok, your compiler should have these in stdio.h + - if your compiler warns about missing prototypes for memset() etc. + - include memory.h in qhull_a.h + - if your compiler reports "global.c: storage size of 'qh_qh' isn't known" + - delete the initializer "={0}" in global.c, stat.c and mem.c + - if your compiler warns about "stat.c: improper initializer" + - this is ok, the initializer is not used + - if you have trouble building libqhull.a with 'ar' + - try 'make -f Makefile.txt qhullx' + - if the code compiles, the qhull test case will automatically execute + - if an error occurs, there's an incompatibility between machines + - If you can, try a different compiler + - You can turn off the Qhull memory manager with qh_NOmem in mem.h + - You can turn off compiler optimization (-O2 in Makefile) + - If you find the source of the problem, please let us know + - to install the programs and their man pages: + - define MANDIR and BINDIR + - type 'make install' + + - if you have Geomview (www.geomview.org) + - try 'rbox 100 | qconvex G >a' and load 'a' into Geomview + - run 'q_eg' for Geomview examples of Qhull output (see qh-eg.htm) + +------------------ +Compiling on other machines and compilers + + Qhull may compile with Borland C++ 5.0 bcc32. A Makefile is included. + Execute 'cd src/libqhull; make -f Mborland'. If you use the Borland IDE, set + the ANSI option in Options:Project:Compiler:Source:Language-compliance. + + Qhull may compile with Borland C++ 4.02 for Win32 and DOS Power Pack. + Use 'cd src/libqhull; make -f Mborland -D_DPMI'. Qhull 1.0 compiles with + Borland C++ 4.02. For rbox 1.0, use "bcc32 -WX -w- -O2-e -erbox -lc rbox.c". + Use the same options for Qhull 1.0. [D. Zwick] + + If you have troubles with the memory manager, you can turn it off by + defining qh_NOmem in mem.h. + +----------------- +Distributed files + + README.txt // Instructions for installing Qhull + REGISTER.txt // Qhull registration + COPYING.txt // Copyright notice + QHULL-GO.lnk // Windows icon for eg/qhull-go.bat + Announce.txt // Announcement + CMakeLists.txt // CMake build file (2.6 or later) + CMakeModules/CheckLFS.cmake // enables Large File Support in cmake + File_id.diz // Package descriptor + index.htm // Home page + Makefile // Makefile for gcc and other compilers + qhull*.md5sum // md5sum for all files + + bin/* // Qhull executables and dll (.zip only) + build/qhull*.pc.in // pkg-config templates for qhull_r, qhull, and qhull_p + build/qhull-32.sln // 32-bit DevStudio solution and project files (2010 and later) + build/*-32.vcxproj + build/qhull-64.sln // 64-bit DevStudio solution and project files (2010 and later) + build/*-64.vcxproj + build/qhull.sln // DevStudio solution and project files (2005 and 2009) + build/*.vcproj + eg/* // Test scripts and geomview files from q_eg + html/index.htm // Manual + html/qh-faq.htm // Frequently asked questions + html/qh-get.htm // Download page + html/qhull-cpp.xml // C++ style notes as a Road FAQ (www.qhull.org/road) + src/Changes.txt // Change history for Qhull and rbox + src/qhull-all.pro // Qt project + +eg/ + q_eg // shell script for Geomview examples (eg.01.cube) + q_egtest // shell script for Geomview test examples + q_test // shell script to test qhull + q_test-ok.txt // output from q_test + qhulltest-ok.txt // output from qhulltest (Qt only) + make-vcproj.sh // bash shell script to create vcproj and vcxprog files + qhull-zip.sh // bash shell script for distribution files + +rbox consists of (bin, html): + rbox.exe // Win32 executable (.zip only) + rbox.htm // html manual + rbox.man // Unix man page + rbox.txt + +qhull consists of (bin, html): + qconvex.exe // Win32 executables and dlls (.zip download only) + qhull.exe // Built with the reentrant library (about 2% slower) + qdelaunay.exe + qhalf.exe + qvoronoi.exe + qhull_r.dll + qhull-go.bat // command window + qconvex.htm // html manual + qdelaun.htm + qdelau_f.htm + qhalf.htm + qvoronoi.htm + qvoron_f.htm + qh-eg.htm + qh-code.htm + qh-impre.htm + index.htm + qh-opt*.htm + qh-quick.htm + qh--*.gif // images for manual + normal_voronoi_knauss_oesterle.jpg + qhull.man // Unix man page + qhull.txt + +bin/ + msvcr80.dll // Visual C++ redistributable file (.zip download only) + +src/ + qhull/unix.c // Qhull and rbox applications using non-reentrant libqhullstatic.a + rbox/rbox.c + qconvex/qconvex.c + qhalf/qhalf.c + qdelaunay/qdelaunay.c + qvoronoi/qvoronoi.c + + qhull/unix_r.c // Qhull and rbox applications using reentrant libqhullstatic_r.a + rbox/rbox_r.c + qconvex/qconvex_r.c // Qhull applications built with reentrant libqhull_r/Makefile + qhalf/qhalf_r.c + qdelaunay/qdelaun_r.c + qvoronoi/qvoronoi_r.c + + user_eg/user_eg_r.c // example of using qhull_r.dll from a user program + user_eg2/user_eg2_r.c // example of using libqhullstatic_r.a from a user program + user_eg3/user_eg3_r.cpp // example of Qhull's C++ interface libqhullcpp with libqhullstatic_r.a + qhulltest/qhulltest.cpp // Test of Qhull's C++ interface using Qt's QTestLib + qhull-*.pri // Include files for Qt projects + testqset_r/testqset_r.c // Test of reentrant qset_r.c and mem_r.c + testqset/testqset.c // Test of non-rentrant qset.c and mem.c + + +src/libqhull + libqhull.pro // Qt project for non-rentrant, shared library (qhull.dll) + index.htm // design documentation for libqhull + qh-*.htm + qhull-exports.def // Export Definition file for Visual C++ + Makefile // Simple gcc Makefile for qhull and libqhullstatic.a + Mborland // Makefile for Borland C++ 5.0 + + libqhull.h // header file for qhull + user.h // header file of user definable constants + libqhull.c // Quickhull algorithm with partitioning + user.c // user re-definable functions + usermem.c + userprintf.c + userprintf_rbox.c + + qhull_a.h // include files for libqhull/*.c + geom.c // geometric routines + geom2.c + geom.h + global.c // global variables + io.c // input-output routines + io.h + mem.c // memory routines, this is stand-alone code + mem.h + merge.c // merging of non-convex facets + merge.h + poly.c // polyhedron routines + poly2.c + poly.h + qset.c // set routines, this only depends on mem.c + qset.h + random.c // utilities w/ Park & Miller's random number generator + random.h + rboxlib.c // point set generator for rbox + stat.c // statistics + stat.h + +src/libqhull_r + libqhull_r.pro // Qt project for rentrant, shared library (qhull_r.dll) + index.htm // design documentation for libqhull_r + qh-*_r.htm + qhull-exports_r.def // Export Definition file for Visual C++ + Makefile // Simple gcc Makefile for qhull and libqhullstatic.a + + libqhull_r.h // header file for qhull + user_r.h // header file of user definable constants + libqhull_r.c // Quickhull algorithm wi_r.hpartitioning + user_r.c // user re-definable functions + usermem.c + userprintf.c + userprintf_rbox.c + qhull_ra.h // include files for libqhull/*_r.c + geom_r.c // geometric routines + geom2.c + geom_r.h + global_r.c // global variables + io_r.c // input-output routines + io_r.h + mem_r.c // memory routines, this is stand-alone code + mem.h + merge_r.c // merging of non-convex facets + merge.h + poly_r.c // polyhedron routines + poly2.c + poly_r.h + qset_r.c // set routines, this only depends on mem_r.c + qset.h + random_r.c // utilities w/ Park & Miller's random number generator + random.h + rboxlib_r.c // point set generator for rbox + stat_r.c // statistics + stat.h + +src/libqhullcpp/ + libqhullcpp.pro // Qt project for renentrant, static C++ library + Qhull.cpp // Calls libqhull_r.c from C++ + Qhull.h + qt-qhull.cpp // Supporting methods for Qt + + Coordinates.cpp // input classes + Coordinates.h + + PointCoordinates.cpp + PointCoordinates.h + RboxPoints.cpp // call rboxlib.c from C++ + RboxPoints.h + + QhullFacet.cpp // data structure classes + QhullFacet.h + QhullHyperplane.cpp + QhullHyperplane.h + QhullPoint.cpp + QhullPoint.h + QhullQh.cpp + QhullRidge.cpp + QhullRidge.h + QhullVertex.cpp + QhullVertex.h + + QhullFacetList.cpp // collection classes + QhullFacetList.h + QhullFacetSet.cpp + QhullFacetSet.h + QhullIterator.h + QhullLinkedList.h + QhullPoints.cpp + QhullPoints.h + QhullPointSet.cpp + QhullPointSet.h + QhullSet.cpp + QhullSet.h + QhullSets.h + QhullVertexSet.cpp + QhullVertexSet.h + + functionObjects.h // supporting classes + QhullError.cpp + QhullError.h + QhullQh.cpp + QhullQh.h + QhullStat.cpp + QhullStat.h + RoadError.cpp // Supporting base classes + RoadError.h + RoadLogEvent.cpp + RoadLogEvent.h + usermem_r-cpp.cpp // Optional override for qh_exit() to throw an error + +src/libqhullstatic/ + libqhullstatic.pro // Qt project for non-reentrant, static library + +src/libqhullstatic_r/ + libqhullstatic_r.pro // Qt project for reentrant, static library + +src/qhulltest/ + qhulltest.pro // Qt project for test of C++ interface + Coordinates_test.cpp // Test of each class + PointCoordinates_test.cpp + Qhull_test.cpp + QhullFacet_test.cpp + QhullFacetList_test.cpp + QhullFacetSet_test.cpp + QhullHyperplane_test.cpp + QhullLinkedList_test.cpp + QhullPoint_test.cpp + QhullPoints_test.cpp + QhullPointSet_test.cpp + QhullRidge_test.cpp + QhullSet_test.cpp + QhullVertex_test.cpp + QhullVertexSet_test.cpp + RboxPoints_test.cpp + RoadTest.cpp // Run multiple test files with QTestLib + RoadTest.h + +----------------- +Authors: + + C. Bradford Barber Hannu Huhdanpaa (Version 1.0) + bradb@shore.net hannu@qhull.org + + Qhull 1.0 and 2.0 were developed under NSF grants NSF/DMS-8920161 + and NSF-CCR-91-15793 750-7504 at the Geometry Center and Harvard + University. If you find Qhull useful, please let us know. diff --git a/xs/src/qhull/REGISTER.txt b/xs/src/qhull/REGISTER.txt new file mode 100644 index 000000000..16ccb1a58 --- /dev/null +++ b/xs/src/qhull/REGISTER.txt @@ -0,0 +1,32 @@ +Dear Qhull User + +We would like to find out how you are using our software. Think of +Qhull as a new kind of shareware: you share your science and successes +with us, and we share our software and support with you. + +If you use Qhull, please send us a note telling +us what you are doing with it. + +We need to know: + + (1) What you are working on - an abstract of your work would be + fine. + + (2) How Qhull has helped you, for example, by increasing your + productivity or allowing you to do things you could not do + before. If Qhull had a direct bearing on your work, please + tell us about this. + +We encourage you to cite Qhull in your publications. + +To cite Qhull, please use + + Barber, C.B., Dobkin, D.P., and Huhdanpaa, H.T., "The Quickhull + algorithm for convex hulls," ACM Trans. on Mathematical Software, + 22(4):469-483, Dec 1996, http://www.qhull.org. + +Please send e-mail to + + bradb@shore.net + +Thank you! diff --git a/xs/src/qhull/html/index.htm b/xs/src/qhull/html/index.htm new file mode 100644 index 000000000..ca4789b47 --- /dev/null +++ b/xs/src/qhull/html/index.htm @@ -0,0 +1,935 @@ + + + + + + +Qhull manual + + + + + +

Up: Home page for Qhull
+Up:News about Qhull
+Up: FAQ about Qhull
+To: Qhull manual: Table of Contents +(please wait while loading)
+To: Programs +• Options +• Output +• Formats +• Geomview +• Print +• Qhull +• Precision +• Trace +• Functions
+ +


+ +

[random-fixed] Qhull manual

+ +

Qhull is a general dimension code for computing convex hulls, +Delaunay triangulations, halfspace intersections about a point, Voronoi +diagrams, furthest-site Delaunay triangulations, and +furthest-site Voronoi diagrams. These structures have +applications in science, engineering, statistics, and +mathematics. See Fukuda's +introduction to convex hulls, Delaunay triangulations, +Voronoi diagrams, and linear programming. For a detailed +introduction, see O'Rourke ['94], Computational +Geometry in C. +

+ +

There are six programs. Except for rbox, they use +the same code. Each program includes instructions and examples. +

+
    +
  • qconvex -- convex hulls +
  • qdelaunay -- Delaunay triangulations and + furthest-site Delaunay triangulations +
  • qhalf -- halfspace intersections about a point +
  • qhull -- all structures with additional options +
  • qvoronoi -- Voronoi diagrams and + furthest-site Voronoi diagrams +
  • rbox -- generate point distributions for qhull +
+
+ +

Qhull implements the Quickhull algorithm for computing the +convex hull. Qhull includes options +for hull volume, facet area, multiple output formats, and +graphical output. It can approximate a convex hull.

+ +

Qhull handles roundoff errors from floating point +arithmetic. It generates a convex hull with "thick" facets. +A facet's outer plane is clearly above all of the points; +its inner plane is clearly below the facet's vertices. Any +exact convex hull must lie between the inner and outer plane. + +

Qhull uses merged facets, triangulated output, or joggled +input. Triangulated output triangulates non-simplicial, merged +facets. Joggled input also +guarantees simplicial output, but it +is less accurate than merged facets. For merged facets, Qhull +reports the maximum outer and inner plane. + +

Brad Barber, Arlington, MA

+ +

Copyright © 1995-2015 C.B. Barber

+ +
+ +

»Qhull manual: Table of +Contents

+ + +

»When to use Qhull

+
+ +

Qhull constructs convex hulls, Delaunay triangulations, +halfspace intersections about a point, Voronoi diagrams, furthest-site Delaunay +triangulations, and furthest-site Voronoi diagrams.

+ +

For convex hulls and halfspace intersections, Qhull may be used +for 2-d upto 8-d. For Voronoi diagrams and Delaunay triangulations, Qhull may be +used for 2-d upto 7-d. In higher dimensions, the size of the output +grows rapidly and Qhull does not work well with virtual memory. +If n is the size of +the input and d is the dimension (d>=3), the size of the output +and execution time +grows by n^(floor(d/2) +[see Performance]. For example, do +not try to build a 16-d convex hull of 1000 points. It will +have on the order of 1,000,000,000,000,000,000,000,000 facets. + +

On a 600 MHz Pentium 3, Qhull computes the 2-d convex hull of +300,000 cocircular points in 11 seconds. It computes the +2-d Delaunay triangulation and 3-d convex hull of 120,000 points +in 12 seconds. It computes the +3-d Delaunay triangulation and 4-d convex hull of 40,000 points +in 18 seconds. It computes the +4-d Delaunay triangulation and 5-d convex hull of 6,000 points +in 12 seconds. It computes the +5-d Delaunay triangulation and 6-d convex hull of 1,000 points +in 12 seconds. It computes the +6-d Delaunay triangulation and 7-d convex hull of 300 points +in 15 seconds. It computes the +7-d Delaunay triangulation and 8-d convex hull of 120 points +in 15 seconds. It computes the +8-d Delaunay triangulation and 9-d convex hull of 70 points +in 15 seconds. It computes the +9-d Delaunay triangulation and 10-d convex hull of 50 points +in 17 seconds. The 10-d convex hull of 50 points has about 90,000 facets. + + +

Qhull does not support constrained Delaunay +triangulations, triangulation of non-convex surfaces, mesh +generation of non-convex objects, or medium-sized inputs in 9-D +and higher.

+ +

This is a big package with many options. It is one of the +fastest available. It is the only 3-d code that handles precision +problems due to floating point arithmetic. For example, it +implements the identity function for extreme points (see Imprecision in Qhull).

+ +

[2016] A newly discovered, bad case for Qhull is multiple, nearly incident points within a 10^-13 ball of 3-d and higher +Delaunay triangulations (input sites in the unit cube). Nearly incident points within substantially +smaller or larger balls are OK. Error QH6271 is reported if a problem occurs. A future release of Qhull +will handle this case. For more information, see "Nearly coincident points on an edge" in Limitations of merged facets + +

If you need a short code for convex hull, Delaunay +triangulation, or Voronoi volumes consider Clarkson's hull +program. If you need 2-d Delaunay triangulations consider +Shewchuk's triangle +program. It is much faster than Qhull and it allows +constraints. Both programs use exact arithmetic. They are in http://www.netlib.org/voronoi/. + +

If your input is in general position (i.e., no coplanar or colinear points), +

  • Tomilov's quickhull.hpp () +or Qhull version +1.0 may meet your needs. Both programs detect precision problems, +but do not handle them.

    + +

    CGAL is a library of efficient and reliable +geometric algorithms. It uses C++ templates and the Boost library to produce dimension-specific +code. This allows more efficient use of memory than Qhull's general-dimension +code. CGAL simulates arbitrary precision while Qhull handles round-off error +with thick facets. Compare the two approaches with Robustness Issues in CGAL, +and Imprecision in Qhull. + + +

    Leda is a +library for writing computational +geometry programs and other combinatorial algorithms. It +includes routines for computing 3-d convex +hulls, 2-d Delaunay triangulations, and 3-d Delaunay triangulations. +It provides rational arithmetic and graphical output. It runs on most +platforms. + +

    If your problem is in high dimensions with a few, +non-simplicial facets, try Fukuda's cdd. +It is much faster than Qhull for these distributions.

    + +

    Custom software for 2-d and 3-d convex hulls may be faster +than Qhull. Custom software should use less memory. Qhull uses +general-dimension data structures and code. The data structures +support non-simplicial facets.

    + +

    Qhull is not suitable for mesh generation or triangulation of +arbitrary surfaces. You may use Qhull if the surface is convex or +completely visible from an interior point (e.g., a star-shaped +polyhedron). First, project each site to a sphere that is +centered at the interior point. Then, compute the convex hull of +the projected sites. The facets of the convex hull correspond to +a triangulation of the surface. For mesh generation of arbitrary +surfaces, see Schneiders' +Finite Element Mesh Generation.

    + +

    Qhull is not suitable for constrained Delaunay triangulations. +With a lot of work, you can write a program that uses Qhull to +add constraints by adding additional points to the triangulation.

    + +

    Qhull is not suitable for the subdivision of arbitrary +objects. Use qdelaunay to subdivide a convex object.

    + +
  • +

    »Description of +Qhull

    +
    + +

    »definition

    +
    + +

    The convex hull of a point set P is the smallest +convex set that contains P. If P is finite, the +convex hull defines a matrix A and a vector b such +that for all x in P, Ax+b <= [0,...].

    + +

    Qhull computes the convex hull in 2-d, 3-d, 4-d, and higher +dimensions. Qhull represents a convex hull as a list of facets. +Each facet has a set of vertices, a set of neighboring facets, +and a halfspace. A halfspace is defined by a unit normal and an +offset (i.e., a row of A and an element of b).

    + +

    Qhull accounts for round-off error. It returns +"thick" facets defined by two parallel hyperplanes. The +outer planes contain all input points. The inner planes exclude +all output vertices. See Imprecise +convex hulls.

    + +

    Qhull may be used for the Delaunay triangulation or the +Voronoi diagram of a set of points. It may be used for the +intersection of halfspaces.

    + +
    +

    »input format

    +
    + +

    The input data on stdin consists of:

    + +
      +
    • first line contains the dimension
    • +
    • second line contains the number of input points
    • +
    • remaining lines contain point coordinates
    • +
    + +

    For example:

    + +
    +    3  #sample 3-d input
    +    5
    +    0.4 -0.5 1.0
    +    1000 -1e-5 -100
    +    0.3 0.2 0.1
    +    1.0 1.0 1.0
    +    0 0 0
    +
    + +

    Input may be entered by hand. End the input with a control-D +(^D) character.

    + +

    To input data from a file, use I/O redirection or 'TI file'. The filename may not +include spaces or quotes.

    + +

    A comment starts with a non-numeric character and continues to +the end of line. The first comment is reported in summaries and +statistics. With multiple qhull commands, use option 'FQ' to place a comment in the output.

    + +

    The dimension and number of points can be reversed. Comments +and line breaks are ignored. Error reporting is better if there +is one point per line.

    + +
    +

    »option format

    +
    + +

    Use options to specify the output formats and control +Qhull. The qhull program takes all options. The +other programs use a subset of the options. They disallow +experimental and inappropriate options. + +

    +
      +
    • +qconvex == qhull +
    • +qdelaunay == qhull d Qbb +
    • +qhalf == qhull H +
    • +qvoronoi == qhull v Qbb +
    +
    + +

    Single letters are used for output formats and precision +constants. The other options are grouped into menus for formats +('F'), Geomview ('G '), printing ('P'), Qhull control ('Q '), and tracing ('T'). The menu options may be listed +together (e.g., 'GrD3' for 'Gr' and 'GD3'). Options may be in any +order. Capitalized options take a numeric argument (except for 'PG' and 'F' +options). Use option 'FO' to print +the selected options.

    + +

    Qhull uses zero-relative indexing. If there are n +points, the index of the first point is 0 and the index of +the last point is n-1.

    + +

    The default options are:

    + +
      +
    • summary output ('s')
    • +
    • merged facets ('C-0' in 2-d, + 3-d, 4-d; 'Qx' in 5-d and + up)
    • +
    + +

    Except for bounding box +('Qbk:n', etc.), drop facets +('Pdk:n', etc.), and +Qhull command ('FQ'), only the last +occurence of an option counts. +Bounding box and drop facets may be repeated for each dimension. +Option 'FQ' may be repeated any number of times. + +

    The Unix tcsh and ksh shells make it easy to +try out different options. In Windows 95, use a command window with doskey +and a window scroller (e.g., peruse).

    + +
    +

    »output format

    +
    + +

    To write the results to a file, use I/O redirection or 'TO file'. Windows 95 users should use +'TO file' or the console. If a filename is surrounded by single quotes, +it may include spaces. +

    + +

    The default output option is a short summary ('s') to stdout. There are many +others (see output and formats). You can list vertex incidences, +vertices and facets, vertex coordinates, or facet normals. You +can view Qhull objects with Geomview, Mathematica, or Maple. You can +print the internal data structures. You can call Qhull from your +application (see Qhull library).

    + +

    For example, 'qhull o' lists the +vertices and facets of the convex hull.

    + +

    Error messages and additional summaries ('s') go to stderr. Unless +redirected, stderr is the console.

    + +
    +

    »algorithm

    +
    + +

    Qhull implements the Quickhull algorithm for convex hull +[Barber et al. '96]. This algorithm +combines the 2-d Quickhull algorithm with the n-d +beneath-beyond algorithm [c.f., Preparata & Shamos '85]. It is similar to the randomized +algorithms of Clarkson and others [Clarkson & Shor '89; Clarkson et al. '93; +Mulmuley '94]. For a demonstration, see How Qhull adds a point. The main +advantages of Quickhull are output sensitive performance (in +terms of the number of extreme points), reduced space +requirements, and floating-point error handling.

    + +
    +

    »data structures

    +
    + +

    Qhull produces the following data structures for dimension d: +

    + +
      +
    • A coordinate is a real number in floating point + format.
    • +
    • A point is an array of d coordinates. + With option 'QJ', the + coordinates are joggled by a small amount.
    • +
    • A vertex is an input point.
    • +
    • A hyperplane is d normal coefficients and + an offset. The length of the normal is one. The + hyperplane defines a halfspace. If V is a normal, b + is an offset, and x is a point inside the convex + hull, then Vx+b <0.
    • +
    • An outer plane is a positive + offset from a hyperplane. When Qhull is done, all points + will be below all outer planes.
    • +
    • An inner plane is a negative + offset from a hyperplane. When Qhull is done, all + vertices will be above the corresponding inner planes.
    • +
    • An orientation is either 'top' or 'bottom'. It is the + topological equivalent of a hyperplane's geometric + orientation.
    • +
    • A simplicial facet is a set of + d neighboring facets, a set of d vertices, a + hyperplane equation, an inner plane, an outer plane, and + an orientation. For example in 3-d, a simplicial facet is + a triangle.
    • +
    • A centrum is a point on a facet's hyperplane. A + centrum is the average of a facet's vertices. Neighboring + facets are convex if each centrum is below the + neighbor facet's hyperplane.
    • +
    • A ridge is a set of d-1 vertices, two + neighboring facets, and an orientation. For example in + 3-d, a ridge is a line segment.
    • +
    • A non-simplicial facet is a set of ridges, a + hyperplane equation, a centrum, an outer plane, and an + inner plane. The ridges determine a set of neighboring + facets, a set of vertices, and an orientation. Qhull + produces a non-simplicial facet when it merges two facets + together. For example, a cube has six non-simplicial + facets.
    • +
    + +

    For examples, use option 'f'. See polyhedron operations for further +design documentation.

    + +
    +

    »Imprecision in Qhull

    +
    + +

    See Imprecision in Qhull and Merged facets or joggled input

    + +
    +

    »Examples of Qhull

    +
    + +

    See Examples of Qhull. Most of these examples require Geomview. +Some of the examples have pictures +.

    + +
    +
    +

    »Options for using Qhull

    +
    + +

    See Options.

    + +
    +

    »Qhull internals

    +
    + +

    See Internals.

    + +
    +

    »Geomview, Qhull's +graphical viewer

    +
    + +

    Geomview +is an interactive geometry viewing program. +Geomview provides a good visualization of Qhull's 2-d and 3-d results. + +

    Qhull includes Examples of Qhull that may be viewed with Geomview. + +

    Geomview can help visulalize a 3-d Delaunay triangulation or the surface of a 4-d convex hull, +Use option 'QVn' to select the 3-D facets adjacent to a vertex. + +

    You may use Geomview to create movies that animate your objects (c.f., How can I create a video animation?). +Geomview helped create the mathematical videos "Not Knot", "Outside In", and "The Shape of Space" from the Geometry Center. + + +

    »Installing Geomview

    +
    + +

    Geomview is an open source project +under SourceForge. + +

    +For build instructions see +Downloading Geomview. +Geomview builds under Linux, Unix, Macintosh OS X, and Windows. + +

    Geomview has installable packages for Debian and Ubuntu. +The OS X build needs Xcode, an X11 SDK, and Lesstif or Motif. +The Windows build uses Cygwin (see Building Geomview below for instructions). + +

    If using Xforms (e.g., for Geomview's External Modules), install the 'libXpm-devel' package from cygwin and move the xforms directory into your geomview directory, e.g.,
    mv xforms-1.2.4 geomview-1.9.5/xforms + +

    Geomview's ndview provides multiple views into 4-d and higher objects. +This module is out-of-date (geomview-users: 4dview). +Download NDview-sgi.tar.Z at newpieces and 4dview at Geomview/modules. + +

    +

    »Using Geomview

    +
    + +

    Use Geomview to view Examples of Qhull. You can spin the convex hull, fly a camera through its facets, +and see how Qhull produces thick facets in response to round-off error. + +

    Follow these instructions to view 'eg,01.cube' from Examples of Qhull +

      +
    1. Launch an XTerm command shell +
        +
      • If needed, start the X terminal server, Use 'xinit' or 'startx' in /usr/X11R6/bin
        xinit -- -multiwindow -clipboard
        startx +
      • Start an XTerm command shell. In Windows, click the Cygwin/bash icon on your desktop. +
      • Set the DISPLAY variable, e.g.,
        export DISPLAY=:0
        export DISPLAY=:0 >>~/.bashenv +
      +
    2. Use Qhull's Geomview options to create a geomview object +
        +
      • rbox c D3 | qconvex G >eg.01.cube +
      • On windows, convert the output to Unix text format with 'd2u'
        rbox c D3 | qconvex G | d2u >eg.01.cube
        d2u eg.* +
      +
    3. Run Geomview +
        +
      • Start Geomview with your example
        ./geomview eg.01.cube +
      • Follow the instructions in Gemoview Tutorial +
      • Geomview creates the Geomview control panel with Targets and External Module, the Geomview toolbar with buttons for controlling Geomview, and the Geomview camera window showing a cube. +
      • Clear the camera window by selecting your object in the Targets list and 'Edit > Delete' or 'dd' +
      • Load the Geomview files panel. Select 'Open' in the 'File' menu. +
      • Set 'Filter' in the files panel to your example directory followed by '/*' (e.g., '/usr/local/qhull-2015.2/eg/*') +
      • Click 'Filter' in the files panel to view your examples in the 'Files' list. +
      • Load another example into the camera window by selecting it and clicking 'OK'. +
      • Review the instructions for Interacting with Geomview +
      • When viewing multiple objects at once, you may want to turn off normalization. In the 'Inspect > Apperance' control panel, set 'Normalize' to 'None'. +
      +
    + +

    Geomview defines GCL (a textual API for controlling Geomview) and OOGL (a textual file format for defining objects). +

      +
    • To control Geomview, you may use any program that reads and writes from stdin and stdout. For example, it could report Qhull's information about a vertex identified by a double-click 'pick' event. +
    • GCL command language for controlling Geomview +
    • OOGL file format for defining objects (tutorial). +
    • External Modules for interacting with Geomview via GCL +
    • Interact with your objects via pick commands in response to right-mouse double clicks. Enable pick events with the interest command. +
    + +
    +

    »Building Geomview for Windows

    +
    + +

    Compile Geomview under Cygwin. For detailed instructions, see +Building Savi and Geomview under Windows. These instructions are somewhat out-of-date. Updated +instructions follow. + +

    How to compile Geomview under 32-bit Cygwin (October 2015)

    +
      +
    1. Note: L. Wood has run into multiple issues with Geomview on Cygwin. He recommends Virtualbox/Ubuntu +and a one-click install of geomview via the Ubuntu package. See his Savi/Geomview link above. +
    2. Install 32-bit Cygwin as follows. +For additional guidance, see Cygwin's Installing and Updating Cygwin Packages +and Setup cygwin. +
        +
      • Launch the cygwin installer. +
      • Select a mirror from Cygwin mirrors (e.g., http://mirrors.kernel.org/sourceware/cygwin/ in California). +
      • Select the packages to install. Besides the cygwin packages listed in the Savi/Windows instructions consider adding +
          +
        • Default -- libXm-devel (required for /usr/include/Xm/Xm.h) +
        • Devel -- bashdb, gcc-core (in place of gcc), gdb +
        • Lib -- libGL-devel, libGLU1 (required, obsolete), libGLU-devel (required, obsolete), libjpeg-devel(XForms), libXext-devel (required), libXpm-devel (Xforms) +libGL and lib +
        • Math -- bc +
        • Net -- autossh, inetutils, openssh +
        • System -- chere +
        • Utils -- dos2unix (required for qhull), keychain +
        • If installing perl, ActiveState Perl may be a better choice than cygwin's perl. Perl is not used by Geomview or Qhull. +
        • Cygwin Package Search -- Search for cygwin programs and packages +
        +
      • Click 'Next' to download and install the packages. +
      • If the download is incomplete, try again. +
      • If you try again after a successful install, cygwin will uninstall and reinstall all modules.. +
      • Click on the 'Cywin Terminal' icon on the Desktop. It sets up a user directory in /home from /etc/skel/... +
      • Mount your disk drives
        mount c: /c # Ignore the warning /c does not exist +
      +
    3. Consider installing the Road Bash scripts (/etc/road-*) from Road. +They define aliases and functions for Unix command shells (Unix, Linux, Mac OS X, Windows), +
        +
      • Download Road Bash and unzip the downloaded file +
      • Copy .../bash/etc/road-* to the Cywin /etc directory (by default, C:\cygwin\etc). +
      • Using the cygwin terminal, convert the road scripts to Unix format
        d2u /etc/road-* +
      • Try it
        source /etc/road-home.bashrc +
      • Install it
        cp /etc/road-home.bashrc ~/.bashrc +
      +
    4. Launch the X terminal server from 'Start > All programs > Cygwin-X > Xwin Server'. Alternatively, run 'startx' +
    5. Launch an XTerm shell +
        +
      • Right click the Cywin icon on the system tray in the Windows taskbar. +
      • Select 'System Tools > XTerm' +
      +
    6. Download and extract Geomview -- Downloading Geomview +
    7. Compile Geomview +
        +
      • ./configure +
      • make +
      +
    8. If './configure' fails, check 'config.log' at the failing step. Look carefully for missing libraries, etc. The Geomview FAQ contains suggestions (e.g., "configure claims it can't find OpenGl"). +
    9. If 'make' fails, read the output carefully for error messages. Usually it is a missing include file or package. Locate and install the missing cygwin packages +(Cygwin Package Search). +
    + +
    +
    +

    »What to do if something +goes wrong

    +
    + +

    Please report bugs to qhull_bug@qhull.org +. Please report if Qhull crashes. Please report if Qhull +generates an "internal error". Please report if Qhull +produces a poor approximate hull in 2-d, 3-d or 4-d. Please +report documentation errors. Please report missing or incorrect +links.

    + +

    If you do not understand something, try a small example. The rbox program is an easy way to generate +test cases. The Geomview program helps to +visualize the output from Qhull.

    + +

    If Qhull does not compile, it is due to an incompatibility +between your system and ours. The first thing to check is that +your compiler is ANSI standard. Qhull produces a compiler error +if __STDC__ is not defined. You may need to set a flag (e.g., +'-A' or '-ansi').

    + +

    If Qhull compiles but crashes on the test case (rbox D4), +there's still incompatibility between your system and ours. +Sometimes it is due to memory management. This can be turned off +with qh_NOmem in mem.h. Please let us know if you figure out how +to fix these problems.

    + +

    If you doubt the output from Qhull, add option 'Tv'. It checks that every point is +inside the outer planes of the convex hull. It checks that every +facet is convex with its neighbors. It checks the topology of the +convex hull.

    + +

    Qhull should work on all inputs. It may report precision +errors if you turn off merged facets with option 'Q0'. This can get as bad as facets with +flipped orientation or two facets with the same vertices. You'll +get a long help message if you run into such a case. They are +easy to generate with rbox.

    + +

    If you do find a problem, try to simplify it before reporting +the error. Try different size inputs to locate the smallest one +that causes an error. You're welcome to hunt through the code +using the execution trace ('T4') as +a guide. This is especially true if you're incorporating Qhull +into your own program.

    + +

    When you report an error, please attach a data set to the end +of your message. Include the options that you used with Qhull, +the results of option 'FO', and any +messages generated by Qhull. This allows me to see the error for +myself. Qhull is maintained part-time.

    + +
    +

    »Email

    +
    + +

    Please send correspondence to Brad Barber at qhull@qhull.org +and report bugs to qhull_bug@qhull.org +. Let me know how you use Qhull. If you mention it in a +paper, please send a reference and abstract.

    + +

    If you would like to get Qhull announcements (e.g., a new +version) and news (any bugs that get fixed, etc.), let us know +and we will add you to our mailing list. For Internet news about geometric algorithms +and convex hulls, look at comp.graphics.algorithms and +sci.math.num-analysis. For Qhull news look at qhull-news.html.

    + +
    +

    »Authors

    +
    + +
    +   C. Bradford Barber                    Hannu Huhdanpaa
    +   bradb@shore.net                       hannu@qhull.org
    +
    + +
    +

    »Acknowledgments

    +
    + +

    A special thanks to David Dobkin for his guidance. A special +thanks to Albert Marden, Victor Milenkovic, the Geometry Center, +and Harvard University for supporting this work.

    + +

    A special thanks to Mark Phillips, Robert Miner, and Stuart Levy for running the Geometry + Center web site long after the Geometry Center closed. + Stuart moved the web site to the University of Illinois at Champaign-Urbana. +Mark and Robert are founders of Geometry Technologies. +Mark, Stuart, and Tamara Munzner are the original authors of Geomview. + +

    A special thanks to Endocardial +Solutions, Inc. of St. Paul, Minnesota for their support of the +internal documentation (src/libqhull/index.htm). They use Qhull to build 3-d models of +heart chambers.

    + +

    Qhull 1.0 and 2.0 were developed under National Science Foundation +grants NSF/DMS-8920161 and NSF-CCR-91-15793 750-7504. If you find +it useful, please let us know.

    + +

    The Geometry Center was supported by grant DMS-8920161 from the +National Science Foundation, by grant DOE/DE-FG02-92ER25137 from +the Department of Energy, by the University of Minnesota, and by +Minnesota Technology, Inc.

    + +
    +

    »References

    +
    + +

    Aurenhammer, F., "Voronoi diagrams +-- A survey of a fundamental geometric data structure," ACM +Computing Surveys, 1991, 23:345-405.

    + +

    Barber, C. B., D.P. Dobkin, and H.T. +Huhdanpaa, "The Quickhull Algorithm for Convex Hulls," ACM +Transactions on Mathematical Software, 22(4):469-483, Dec 1996, www.qhull.org +[http://portal.acm.org; +http://citeseerx.ist.psu.edu]. +

    + +

    Clarkson, K.L. and P.W. Shor, +"Applications of random sampling in computational geometry, +II", Discrete Computational Geometry, 4:387-421, 1989

    + +

    Clarkson, K.L., K. Mehlhorn, and R. +Seidel, "Four results on randomized incremental +construction," Computational Geometry: Theory and +Applications, vol. 3, p. 185-211, 1993.

    + +

    Devillers, et. al., +"Walking in a triangulation," ACM Symposium on +Computational Geometry, June 3-5,2001, Medford MA. + +

    Dobkin, D.P. and D.G. Kirkpatrick, +"Determining the separation of preprocessed polyhedra--a +unified approach," in Proc. 17th Inter. Colloq. Automata +Lang. Program., in Lecture Notes in Computer Science, +Springer-Verlag, 443:400-413, 1990.

    + +

    Edelsbrunner, H, Geometry and Topology for Mesh Generation, +Cambridge University Press, 2001. + +

    Gartner, B., "Fast and robust smallest enclosing balls", Algorithms - ESA '99, LNCS 1643. + +

    Golub, G.H. and van Loan, C.F., Matric Computations, Baltimore, Maryland, USA: John Hopkins Press, 1983 + +

    Fortune, S., "Computational +geometry," in R. Martin, editor, Directions in Geometric +Computation, Information Geometers, 47 Stockers Avenue, +Winchester, SO22 5LB, UK, ISBN 1-874728-02-X, 1993.

    + +

    Milenkovic, V., "Robust polygon +modeling," Computer-Aided Design, vol. 25, p. 546-566, +September 1993.

    + +

    Mucke, E.P., I. Saias, B. Zhu, Fast +randomized point location without preprocessing in Two- and +Three-dimensional Delaunay Triangulations, ACM Symposium on +Computational Geometry, p. 274-283, 1996 [GeomDir]. +

    + +

    Mulmuley, K., Computational Geometry, +An Introduction Through Randomized Algorithms, Prentice-Hall, +NJ, 1994.

    + +

    O'Rourke, J., Computational Geometry +in C, Cambridge University Press, 1994.

    + +

    Preparata, F. and M. Shamos, Computational +Geometry, Springer-Verlag, New York, 1985.

    + +
    + +
    + +

    Up: Home page for Qhull
    +Up:News about Qhull
    +Up: FAQ about Qhull
    +To: Qhull manual: Table of Contents
    +To: Programs +• Options +• Output +• Formats +• Geomview +• Print +• Qhull +• Precision +• Trace +• Functions
    +Dn: Imprecision in Qhull
    +Dn: Description of Qhull examples
    +Dn: Qhull internals
    +Dn: Qhull functions, macros, and data +structures + +


    + +

    The Geometry Center +Home Page

    + +

    Comments to: qhull@qhull.org +
    +Created: Sept. 25, 1995 --- Last modified: see top

    + + diff --git a/xs/src/qhull/html/normal_voronoi_knauss_oesterle.jpg b/xs/src/qhull/html/normal_voronoi_knauss_oesterle.jpg new file mode 100644 index 0000000000000000000000000000000000000000..f46d421274edd97de13d25e306985c43e872165b GIT binary patch literal 23924 zcmbTe1ymf((=WU@K|*ksu($EtF09Z^I zI82z=UH~~%NCcRFHo$*27+5$czevb$P*9;2>fZriVc_6k;o%Sv;NhXQeW2w4cuWMW z_iUnw*ouZolnyxT0kJvAz>n2kxJpxJR2)W*-`=3$;S&%NQPa@U(KB#zar5x<@r!*D zmync_{;aH`s-~`?sby?pYG!U>Y31bX;_Bw^;TiZnC^#haM_62ZLSj<#&y>{My!?W~ zqT-U$n%cVhhQ_AmmhPV3zW#y1q2cKn@a)|D!Xjj2b8CBNckj>s!TH7I)%DHo-TlK~ zxnKZr|B40u{#UU7LoQ6HT(I!)aPUZf<${5AgYIyc@CfhO5V1rRkqjNMDcJ*%aX!Z8 zRCm1rawwhQ8aYm(;8AgIP@n%5?H|eh?*#ky|4Xv}670X_f&kFqV4x2V4if+ZjPVU^ zPn$fXdXS{bs%@hWU2{TzxYG3jcxb^>6D37Pk#&IOEM*CWe@|C^%Lb%Owja+yCJ;;tQPP|Hdoy#yW~&0C^ud8mKI`dA0yr;lg-m1AS0dw*SKw zl)>hIdBrjECm!cd)$@^K`P&&;%96r`8Dam5oySL#s#}-sNC(sFXiZ%+EZWC+N*hp_L-O;1i02pv@DCO9x2*OG}3J zZziMblMdLIK!9<0SdPXBAU_I(wVoo~x`p67%Un_M2_Gj(11N1(Oe(N~n-(s?-nz^QGi9m$-nh23f-6uSYQs$LN zN{ee`@^+~Qgm*Fgpfp}lsA}3K|7yjg8pj?M4;znz(66Y_$ASQm3}aLH+cAn^zVu&( z=wSKj?+QAvvXRgKWj%~R7UMH39jxGgm9r@sL*j7&!}ND~&_;~HeChgN91{K!J|WwY zexU#^Rs;)od#a&;aVXCgmU2k~l59%@0YuvpTj|!NS-=X_*_LomA7>*&A9MbaOoRX5 zD)CU2`KQEn+BO^c=5Gm9o&H`USNH!wKwD9*eDA}qO|g3*5Ny$a)^PqS0z+rkk3?G% zuY9O6u$z4LLjO03>Z!g+ z(;MR0?T8xJOLvmW0o4CrdZ5(;mice5K?fV~f35o81QrM&{J%PD5{WW@=`%j`IV3_= z_Mhh<07FL4rnDJZr-5886BdtS6#g$ELeT)&N5i072PNRqJ17}J36y;VB@WUJ&>D1o zG7bN!0Uc6I@EQewm0sLG9H*&-O@i(h)*lUhoIj(UR5cwtgrMoRj_zb7jRR(IEyraT>%lu6j2;+QRg9AzTdx57+bx%;sWN)Js3NtxZ&E<;)lOf+ z9y-C^V0?=Y|JFHaq|AobPD6{ecJPw5OOTlm(BW49y6c9frj4(Z`THsQ^nDUOoXJ!? z*z*yyYr+Plxop3`d!5dndsXx|p+isA6)=aa?&-UWlEl>D)L?7$IC zx>H)20Wf(sIk)2qBzmaErdE$hMu#HZNl07I?-mAm&kPR7G0zc9x={qrgNm)=hKU5; z&7G5>?%hV-y#kPL$TMh;@VM+xZEe_V$(=_xW6$a&AL`~)A ztudc8+WQ2n;s-o7UO&VrR;K=^VjX%!_(tJ!jpP^ zw!eqzO=G|DB>HQVuBX>x-v&A13j=2>yMhI>YCkR$R3v=N_d4E=F5v`M;_(O1uwXk5 zQ%0#Ot;!7MegsfGrA&w@0PB_eOx@-8ZMnXFmHqf8RiYEriY!Cs6$ikrQZfJH09wy^-JPE>IBuEFzzX+IK0ZNdP2B#(t+5_40;~Jg+LtJ zG9~RAiJEUUBCh~Pjqz>!9Mb0&u(o9Y9^I1CG1OQA>xf-wou2S`B3}V<)AR8b$Bk1%1Tz zh;u)y=>Ftfd*9^Ima#=Urkwbj!f~X85ngs2NmO-OcS34tZ-u-mUF{(Aug_U*BdpyV#{xAwORV*{`Ih>kXgmje9}5SZSBB_3R(ZBQJzDo-nkxJdfb(JcgexblE$&c%xsn?22c1!xLL znuRQ697ku1lb?%H$b?-trkmKSmF(l_5A(gU8*u{e*i;;I-F|x z;UyaZA*!*R_K$m~rXS|+g2C0h2O><<0Niu~Eo$`m@dA#pWsa1v4m_8L9-~+(? zu4xX%vP0Gc0uuXF#`RzPJbs=bFa|{h+iJ^)2c>EUAH5IVy0r222T!;sq0oTWG4Tka zCVb8OJ`VT?9=y3JuWHb;*s@CuPg~n;ce(%a3Yg&nJz(xlqU04VtQ_*^yPlmSZJLb; zq;EBw3Hv+B(JYJ)yUK*ipbq9rCGDc`U<;@Qvd3_|ocIw{HB>H3BD8VeaGSK(mnNv& zRrcF|D=s^0O7XDuumJ+kipB(qRFA;6>Lq@3DaLqh`|L=g7^4E1<*M}uLdM`* zz@^b+&!1s;uBnB?B>X1r0zB-jO+pLp`^0C->9%4SinMWcLvApsjai21t8}HLxQS;pe0qVgxOvaj%&IG%`es>^= z!=cW6NWDxX_Y3YNffi@k-pA!Kw9BqP4r|4^8+&KBKkR(RyE0eJBSHwVSfm(wnt&8D zcI)D9o5v>cgiVcX!x51LwLz)@$`|PrgTjd;Vt%=6tUT|e3zlC26H**ZNC)3f^owa1 z)I~Q(=xkR9lxKQK**6vIu;Wpq+ljrRR`as7E+0eN(o*t3oqc{lrT(Z1rD;1j1vlH8 zlgVl)uI5!7^3%6vuK-xsv<+K67X?hqIY$Sy+7GEdK$7Ms9HRc<120RX%xO!*3`|Wu zZib#6!fkcs4PC!u&-kU)9!t{WWh_XKFZPIu$Gc)Cd=#g2?1;nD z60Y7;I{h2zbzhCbgX5cJJ^JO3<&Gb2zp`SGw)lb+RqPDnARY!8M0|NhQIWG!>wyZ5 z85Ji~20ZW0)WoKO6>w|>691I(aBmQ5VQIZVRE)k5-YR+pRMrCHc2718YQLutHbh)F z&72i|DkT#=; zCUL^3NMMZa0i22bAh=~@WknMO1QoVHnHdPVIuaMA+k`3cXcC^==`i5TcI%OP)TIiE z{S*7WxJ|c%3zDp&UpJKlipr)Na>5)vYgG2H0GlaC5O-|HX{r%*nqGBuXp&d(E5P(4 z{gVRl<-QdAE^PhW-X0!UZ8O+&+5HOGs*et1Q=#k|FYs|JFmFwXxfZt#^;>5Qcu?i~ zl7|8y>&}OU4#sQ}46}*px3!_RMwV29>#k(^H)Pj^R5eo_U=KG)=qn&8-;5yo5qLMz zymcsrY+INA4Zd}b%G%47Fp8SIJ?(jF!LQB_4a^@^LJg*bAkWS9$*Zk$^!sRBo85v6 zRGU+^OIEFakZJ1dWQvLsc+sbz!OVCkxlGe_zozr0i_4merRhaJ1>Mmv|k;#B4q@^sBmmaXV^eJSwrJ=ZwjTwoZ zN(><+-(>tiIaq&)IDOsvh!4+IcZx4ODz2nQ{7n=z(RhDbIkQPNQia7C$NmZ^6h#NK z-;DI5!X3A|36({~aYv?*;JWZqTqFLBBqo)LtX?)v&K4TDzp8BPJ)s59tvj+*yb&8o zLQb$}lbK;jq}5brRtaL;LxRCU%gXk0=nyUOFnJA^r2N=K;BJTfnp#$tuQpbU`@-gCy(ZR=1MDa6*Kwgp@d zqQm(`C6eu3BfGdTAS6Q0BUBa#ndU~+T-YL&=32gcFqz_f#*hw*A9c^%{32pdsZ*@y zzGV$FK(JYG!jT*`%4V65?gnI|3SJ`fI*GX!?zt>;)ss6O8)aWc4R@#TP@7)HQ9~Vq z2|er*!XVtOKCz>|(9+CzOHx=VF^)s>JA0A?b=TDY?yzJpHkG*V`t#-a=+Sme0C``K zx^%+F!v$3gN6Qf+i#5&GAk^jNv=TisM3q{`QP)VEz7wU9+L)u!UKQCPYD}anyMUWhYyBW847!B|9yd&HUG7rpM!_*y^ZSWRrLa>Pa^W(xpTv@7*_#~SI z#Fs(F%fpUFBReubF$%VLh)YB?Y`SLpR)pnr=aD(~&$j8#jri&HA0ey3s0ry^L(=0j zsG%{~rHg))yo>&|b*MR?%(1#mSeXyCNhUXB{;p(T|5~q-lSb}TZ++7mkca5V$jR~A z{-mdx83YPquo5;jdJ7cx{&`5s4JtPo{l5HU+lhWotD93-rLto&Vpz(S>F#f-RBs01`5Ka>mj*(!HRf0lE zP*Vhjn4qycG*pM8Fr!LFe@!r=QOUt^tz5JHgIUidyr0&LMO&*{bCdh=v3mIo?g?X7z(tlxk5P`C0zxuSE!y1PeOH7_gD+>)B>|eX}hfXb&}XgDvlQ zEXM$e%`|wb++^)%ps&h}!H?atJS=|>fXH+K!T+kJDMa#R>cD4~g0v9Q(=;4C% zmf$aRf~ddSAwk}4W%A3+ka(}o=B$s{UL4$DVXWxw&-DK^aBIO}6CrW>`CuFd==^4V zxKL5h;Gri@rre+Rb?Z03LG+IL%K-JLC5asJ?}+4geZ0&|+1FdyeC5YvehvGn?d?r) zHqs0X*BhOD*Bc$5M>1guBMV{^_9!IhFOQ|na}Uh+`pC%Qm4n$+kNua5KLzeJM;N>b z@s5JS$hod0%+`3ZUWCP*roK-*C*RVo>N$M2yH%=-SE$asuvql-{ka_G-W##TNKf)^ zMe-w~qduBDeVe8OX=M)SPH%Z3I+sjEi&U=}cLr_^M>I7qcy2aK@y6zk| zalCfpXA&b=_cn=v6^Bf=`t5;N^u}JJSTRX|VeQg^(&DNZ6?@kO!yxPDsF3r+%n+{W zJA1LrouD>4@B3gAsx70auhM^zloaFz&Q;n#o`>*k?wPZN)d3-kx?R}4PE(+^z!K%I z<8My7$zgGOGAnuelcRGNl+w(QO1X8zW+@)F|?MGWg^qJG;~BH6L}gu|dN|6hUdQg@Oc7B~d+j5A~3sNP8iiE4aZm zUQbut-j*=VQUf|9sAkx7KjGC{++>*ta8&4hl8F;}(GWil~i@-lU@88{LPb)7KmGUWcSF@pk| zMiHvWP*bVZSev;;LpP*28;v5If5wn8SCXrNMcc+xi*avi!h_K-cJZQ_za2Ae15JF0 z7FjD=gDf#MB`ug!V1pcwhC45MulbZG-_~2>(0;Y*ff@oRx(%0VM71=OhjYQl`LoD? z5bEds1^Dgg`UL)F4#L>}Nh=s7;~YV$W)L0aYA6VM690HZCOvwfGdF8`Em$v>Q&xdxA1gmfFAVDx5Lz_5CTYjhP- zUl*7$1!DUwd=v;K-v<-@sZ)tMu4)r@4fjzM?+dVtU9NPkYB){hxK`FVi7%$IrpfZ0 zz+vh&43=eg9wZ$gU_3rRkQxu~?AYgi0nR9ZtY)ZO$f_{0qNmUue`}{p6W9z~+Ud>C zdJD;}rPm3C}j!@QGnj48L7RH{{{N^^F zBWd!AxZ_T?-uCSdiXC7ZQMF3_~%9694!)u0Wxfug}dBR<6>WT>0!ggy&4c?g&PQWSbBO_gtXk&mThv z+t}N|f`NrDKhA~M(o0|bNm8*5w&&$>O(WHYt*HrY7w^*EaxAiTF|9o5ORDe+&}$?b zKh(c7FMtnvS<2TFO0$E#x6Ytc?-5Z{CEee)~c()iDkI* zUt6Uv)P5mV@@c~4ZSj42Bt!!DN(V^}DObBO=Hn4g%)cf1NLm68#2sK?EyB_72s4(l^-CSlok|aqnLN0erH>u?9_jCqqXC6$-hBL`F zADlO+co{@QS8)pHDin+F##Rq1h2N?RZj-Ht9eBIsEUtY&m&o!kOLghFR}V)vqG;*Y zp^SMu_t?XGBA^b!-6?28CCztiDH%%o!Y<)~IuCfJ5AfXx4|06cjByRzqwJ&u znmSqcW|>Wdg05V8CmWRbnA%ciNgIv2D~)SSHhTv+WP#@o8uMJybt6=27Vsv>BXkU{ z_$U3%+Z4|VG&R6)mg$ryW|?vr?-j@2vQI&q1rmmM1`^fjk`ngdc z{(>+U9BNzt#WSy?Sv$&O=4MdXua}6~x#LP;&xME9bD*oaMf4N;=V)-)Nwq2_TT>oM zm@B==Zh7p&_EZ>#vN(Vs4@Xf1k**Kt3k)$4 zr*kDaCbdbyTP7u(14%WZ1h3Q!X1_lEsDfh7;cDuZr)sh2YwP{YJurA!ZPJo-n~XN) zWJ8McYJ|U-`Kvs;!;Gn;-#g02unUEE-64wAzcZ$ENwPyDyrT!u_yEonCaND$t>!uI zg1h;Wt?mORMq-2Rqk;RHG44r+J?BfJ)NMQv-M%KChBz{+)xMS+ULg9yoZNCM%R z7axRUfRxdGQ!klk!JB<42M;J4g}k$hlJYKND{4J9R%blLlKB;oXh?NN9OK%%@#EgB zWUEMjM+u>cbFZb*VXuF#xOCUqCiFZY{i{cjzRv!|ikxGd7GCTKo&)1{mI2_!g*G&Y zU#OrV#psYzf}o1V{enn=4X&iC3j$|Mt~1ZaX(Sg1cM;o33R8%p<@1w zw?KpRe1)z%bu#n$QMlcjdUmWYp{%afhC#z;_b|rOrBV&cSQ8@~>a&vJR_7^@@Yo{^ z!FKP!W34}ruA3heS!QD>O)po4RvpzP%J?w$5_x3FKk|QHV!iLi68Q11Qi#BFK1~`; z>&Z!Xe~=Qa&g^8-_J(bC>!oa|k(6KIjm`>n(0H6E|L+jcO@=?IwjrX$KrP*}kQ8pv zRyfzRx0@GpIH#H;`vKkr;lL2Z+iY^%B|j7?n{LLP?qa{tH&nOe-5t^T!)bdQKw%C_ zv^}ti`X@Y$cRkEq?&a46$zv_c$Wf~6_ypqw=CNyns-Jv>N76KWXWx?_R*Gu80@m5C z?FXW*a?s|O3!3Ix$dEHd9!-dGvf8q59_HqQgH}_lov5&%ldcrdLdE7GTTFDXI>z$2Ek)dFe{%58NWBkIbjmT70bl zZBj<{52h{#6x1e(Ql_a)K67ZD>k*C;U)^`N!JOn69%uW|`5XdCz`}{!y=KFaeJc!Q zFdY7?c8687sey~MAi0SauNtquQj=~z1HIAFE8uq2INCwy(!FIuzNtCBhv-YNCJi(7 z&i-Ygue5;rNs+LBmZykChJf~*iLG+4o1aO&0jz@e5)7#Wkqa<0j7|MLxQ*_tr(mtJ z#LJ$TO=fTwGp2zZ7lYbs{nu`@SJg3UYoJf*nv`)edU%WpG{ai^&0Y9OIj2P=4i)(m z8paxPbLhj(6Mg9Q2aG>5mWCx_8*)}lUz%Dle`dFZTW2FtYq7r>g1T_%v|MsKt z@#A8hK-|)&aw2O82NV(SZZeN~&e0$9*+SZ;BDp@E@I0>5C$ny_GH6K>xOx+4 z@0R4Mxb*ejf8cDd@v|8Ir?aFy;T)(1{ooA{Sp} zdOyAg@^m{nz8+8zL9(hMYc;mt@R3xSxtg6xHIJG*p%#+))fcj>{7#!vfCn)|C3K zf;>7%PzF7SWbJLXvX6wnrKkXN&Rk<@e+Y`%nlj$IhNsw(e69Y}R_pl|l;NEDeok2z ztaSo^Q4aRtp1P4r4kC}4*iubbLh3l*O5} zt`Np16UN{V&FpK@!Bo&U(tm9?bV=~*l(OH~OQQ^m{dNVvKA+MO{IMv9ph{%A@KHYA zm`$(^^W+uaPKR`jnC^zXP#?xNm>U3AjUyeyapU{~BMzOv{^u?Y96b!BvMg>zH^e7Uy#k~mvg zR&?mf`1TiCgC{Mms_xSXGwI&V$5e5Q7qCJsAW6uUCR zGKuhvj!S<>@tW1gmzxt!yRJO|+Z-(xc`_V=7%O^}<Xe4WoP4NIUF)+$hM#*xIU26(oBmV^tnb0QAvJ1sy-cL_=ut%5Cr+5V-=h#2{cn4Hloa9_77;}!6xC^%ve{x@)%Vu{^_ zry;QU+WG1H1OcFYms6CckJa5U>OE*`?)s~nY9;T)LE&G0~3vO@PUcE11<;()2-w`UP$7Vr<~E%B?BUB5JL=! zKT9~C=b0CFPge>BdEAtE+CtV8YMo)RQ>a1#WMcCKFPM|QK8N5Zh6~A!+q3@i8+@&u zb?ZlT;fDn|BADSkSid>eR`Sp7d+#KVrDlFzBdZ$gfkLt|+KR6ITItJuDVO((K=L`cIkLY^) zTP@mUU3l?UVISGMP;wGyD9*-yng5piItD+j6~4kUO}FdTTh^{+uy#~61Ak0<*nFP) zyNW0Dm`7j;am;KmPTHL&(tDO{xDR6r-9ys=BHII#N(i?#$=dY_m{jv8Pb-#6)jzD-K&gQnJuOT zQu6S13BFwgch<5nQ^)akVFfdsct#uU%ZuYt3N#}g7HzI_K$kmY8IB8C_4T#4cz9-v z#G!m=dy7^>H?ZT+amAHFEOfls)a*UzYrqx|we0ETLx;I0hZ(X_7>{yd=|U>6nek)D zF{~tOY>cCqhc5Fy*!D!)`n`YzfukYgMKIZdVyu=jWu*GXZjz>mjA~1w^fA@wK=iGu zbxIq%3}-SHxXHj}wY31w_nR1)qPA>{rtdgb zq!%gmpss>K|F5IHkKcy(+ge>gO^G3_)yFUU&x|wW<$fD)&m5ORl*=&3T5TOYR4x2wm97FymJr_gI2ec4Ady==nSD6E^&ocUr=K>_D_AH^;d zqI`$4=kfv5O8R~9+{`1-bCJ$S)SlB@5P)%VcPbaL3c-TL8J*D*){U*NWF9z1$)v0L1h)BI1QqPV^Jr&18u{3YCk}fD?iUU_v?K zB`ea@Ng-8MP8eqA;Q=@bvOjTRQM=Qu2&%LD&W#($$FX!cghG zvk)v{8RI+KGCnmTtw?Yet%HbS-s@xNbLFmB&$C*j8asAa5Ni%RVq&C3H$Uy)@V6%h zR-XlD?8@P<&R#S5o~|gLR5dtSqg-R!rS9pqBrAip( z82UbI6ulk80~q(Wop`jqrfeec{z;=iM@Tb>GIn9G7h%jizpv|>Z0xf6#y+P-fl=E3 znCIf@8{C@lD*$ui%#T4ECad($X5VvIQz3VG%t-(SRj_Bc3F&V0;yW zzJGGS-VpSI!0E=lD{5fLW5^Ojf>Lj~=`KiXXq>0nev^}@Q)>ra)nQLkBIxA17@VNE z@EH55_455eXfumb7ovl~y?X4S8FPChoL8M!Or+-746dLGQB2%3l8qXNuMr;-=rp{n zD$SHsTq7h(jI$i=u&QfxryRQG} zegI2H`fhb^jJ{~x&`2;I36+ic4mgpI zxS2DzzQs);@b#$DICJ{39WUP2nEEj7Bdlrq5oLfX6fMl{t>`i<@&Ilud6)|^8Pk;? zh9v_c2iqky>prm9kqB@gl56ihTN{99SD;W42`fkw==#I=LUd>&2;3y7>vDRV%&)sl zFYZx|)tT{zdJraB!SK2abe438C0pjSsVU;rCTWk6NG9}l5<|GD(oHk$xt|-yibU)i% z^9mViq;{{m9_4Ihk&L#c?~IdU(OZ2GlZ9WKROc0(Q0aHP>5i_gJV{uw`D{K7_P=@EDHw;ve2kRk=1f7la=t&XMFjsiSa7ap?RYHc7=6PS;b1 z&$D&RR4_*Nr^sM0i9e|OCvn`}=B|VW^`y7ZFF%YM&&c0xW{HB@v_H#R$O4yr~%-Dn9n=ZhEX>+=3!$$17JzRa+$`?Jf7c9Jrj6cu=bCa=9BStRGVtjjzi zsFns}dyn4oZ*d9%+H>50>FT#1U#FwHnPiy;^Mdj?C#&H-D`a8nv%Ve4EE3LO)H@vbP22Fh-qvczzI-Hb7wzY+{Fa?-rU0h;wUAR! z(nJ1G$)9LzTgtME@eGP3%1p7J8aA{=D~l5q_^2ekuYhybaF;1o?YLIpo2G2SKkU4> z6I|&1$@GmL6;dss4!>a?+oN_KhmlGMZY$H>%?=I~ye7~NxF7OuR!u4uVwo(b+<;qa z0@AoNu?4G=e()ncLPrfpm9|%ZB05<4-fwSwaXoM^1S;#Pgm2nz`Ydqz0Ef^cj_J*# zpV7<0X13BPWMuH{P$hbQ8&60QOWpr&bFSRnMGYTQ*8pFKSAd}5^I)O@$SNlIt81cu z0@_=S2{^7mmvW1DXAKX*l|3iVmHG^rSCOFpa2<4VWJ_1A-yJro3rUK0-(-ILUL^?y z?=;dY0gi7&v}Whp&q|~tgP4(ux)7M-j_P#@mmxw;x)f(Y8t01 zbKPlzA-EAiHfSdYQWM^@W7e`sJ7&K!zPD6n5LgtDUp!hylwsT?_@t>K1;Aux+0hyQYPVf9sUSJE{ zED7o&G^3T|<+$+r9P0l10IG2U&G`jFa?}7E#V@M^Ouq=$lZVaLhZ-Femjhzs@4|@T z($9q}byK^?&LNigjgP?9<~t}R4ij;-G#MD-%hk4u-SVSRYgKDGN`8m~o4mvb1|+5` z|D!bg`;WRJcF@Wa@%l3{mGM=1rS7-2UjB-fAeqA-@BGk;pvdLc;xmG@wVSQi`)@w5 z!)^(eH&PHn;+MNlP@-nC>`Yzph;{9+CQ@dmZ7k5#&&aa-Iqtm8TfAfY0#1JM=8dEU zk9gi!fR%hl5`!~gW0?gR!LKkL@CTNAZMJxh|Ag8~Wy!{2av*^UEcf!*OR}`SjxG363CER*C|21`0hr z1*;HM#3{hE{~9TcUZp*!y8=z#W}CSdc{o_Vm&(!5zWZ)$`Sg%AUbeWja-goLt1^H?y!0 z^xkJ^?TX^OOYgFVJqmmOG8_dWtGAX3AFc&g>ZYvZz1zNJ@py)LI^^vkWBa9O9Z->N zRc5EfIQTuOW^3X{2-!5<^@RSYmz`D@LK~K4?a)mH;(7?2JNshuV@*+MmR(+8pbi(L z?%h*_O7+PCbd6bA=^1+6{6kbMVli$7Ws=Nd&xbegGVLJ^uFt}TQium+c(qd}g@HQf z=#gdp3v8mq6Fdzn0GzFAu8+j0=nHZrsb(2vR>>$!Nir$k2}tuM9zw14#)m{Jt9hlm ztAhtM%fr(d#T-d|hY>TjyFW#ySBEbsc*Uu{4b1F|kV-yL<4<*m7xftD5fa>2Yj;-1 zPr$R?cW_+gX{MfgQ`-vO)^d$Le9xDi_TIq6F?J%^Vt#<4fi`vpaW^NjS~>SwIc7)U znb5Z#d~qAku{8SQH+~wsvbC2dYn(m~A-_n`!PVZ0z6V#X;$>{7u9#BQrHuf)51g(9 ztjC!t8ZF6(Runc_JkWNhxCeVC;+C7pNvHjT=OBI==+&=?cetuMW1J#$6mYMkK> z`7y+Mo{glWD;DXK!#vz%MC=u?-hAH;Tg&Q=gUu2dJsjc_5)RDN9m<$}o3bHEEw)9- zF}5wQG7;?!j{)wk638+Dk+IXM4ZKb&GFB2LmgP-uDM=AN~R zY4KUO&|e6tTi^Ul-~Yt>%g)B^b&!`36MI>m;wylp2Vmlg^knT{9}4NCKFz|JX6=>5 zwnXAJ;{%*OTPtsphvN79eo&d-4r8Vn6R$|XAjj-Zw?kSZ+#@YbsMYEcs&f+AWDK}5 zF%ZS}9kGVj@`+pG_w?c;WN=>}gEL z7E6;fUM`jRBEKXG>r-~rs`$hlo=0bhjbPpx$C&$X8 zXpv4oRt7(GIJaVm28{`O7IR7yjKJPL*i+dcQoxm4i}blyLzBh+{e9@TYyYU9x>oQJ zgkO@cfszkNLmxrAiQ*fGNo_oXJH)N1B4{Y)*l|!#fnpEFu_{>aW2I{6eh*rzcJ$j& zB`Ky;BPZTb*ZHP~nDpmuBU$JwY#2v{0)V5NqS$Wl%|$Cfit-aglB_pscEv)S<1iHk zRT||kM2yU*4DM16c_)53D%(f$T%D99O{m%a_zh*fsu8RFE)`j)>M>Bomh6Ifg@s?1_Q&jYFV{3K1)KdKEf`^tcaXc4C%NI9;db%SBSYWK(wXo zWUXHTpqolBs#?jL^&8v812SPQ& zs!;t#Bplw6e|eO8D)xgT79=&+pR#cupAZ~|rcH*U59M0gn_6t=IYu}<2X~-9wfE!L z@KajezXF)HVP;zgOV5a@|+3( zSfU+icO@U^A=iyma45Sa42stup!u^3-(cv>gZwu1u{FQhIeQHPUoJlC_^pWnuSL)k5QQ&d!$f$U_8n5l^{*C3(f1aoM+RyErUq~xF_^)!(QVM_8-uUD4idEe zu^J}HfTPJ1Lub0n8k%*ukn&C=KpOA#E7KbmlD>M#(mM23*g#hr)Ew4Y*hg!fSFE$0 zEAUK0s)nwrxtvr7mK}tD#e&Q`E{fg4rPpSjtnOXy%OYCgk9;@*d$?9V{pt-7@EJN$ zaMH!^l>$FUjY&|?a6W$Vajmn`zAz(CsbuLVuRp*$qg~51jWBMeC7)}Vz+Xvup+Bh3 z<&0?`(M%AH8x)oYF}>gbvJg+;^|9Lo-%t=oWnT@)KJ1pc@5#uJV_ALq6r@G?^XJmH zcN+m@dGLOjmSR2YV`G-Q$=c9K_&ewT=@IF1wXg;HmUI|V)0DHo*ykmgIfG0dNnQC_ zQ%V&Z65gJOZ|(a!OkauMBhhD?;Eu+KnbtE=jI(-OZ~KIK>O?-FV`Gm~o00zZ_pOJ# z0&Lw;RqB+>-Tf3N&02|NTT~|#0Z4*W=?GM=LZxlOh-Y&-&_5~wMI7z;^IF>4uX|Z` zo5!@_8b|#UoJ5$wevU(j`*gM(R{cK$@)-^0Pjzqh+>^r1C)@<*rqu)SuRqh`o;dGg zmM4h$dsGlcPT}A4uAAabcfWcf6dFp>k^jFcZJQ488wA)pLc#p(5d%|W! zXe|fbKuPC!Q}y;07lAZ5?ry9l)@LcBXcnVT)V(B;{$-Co_+C8$uQ~D8igj&!#Fv`A z{F24?rzRv;$|?>ORE`b?I#31Zbk@+c$*ylA%#wmIq%bN!gb|U+>yE;(W4xB;-tmNo z&)w$&rx_!t{J)i4@b;nop%dvEmBEc?ZT6G7<_YnEll|7{e+tjoY;Ve3MdVFyAxYxL z2eV_-uTGwn0e@JTCAwK;g&sN4W>UW^432pndCyR4W!2=F7CW~~*jJA)P>jj6dMvAl zU%-J=CXuM!X;4LLZ6xn*?;aIHJ0^0hqp2Nv9@Uv6G;*{THli614Uov+k>m8FGU00r zl`E%1aRlH3V3CA~-s;>Q{RljFt?wO)Y_-cxGI5Wy*+Qy4$Si+Wtlt3JL36Jq(iwGb9WML{n{Q!-Xqqw zJSh@Fx~8W8056klVozx$AqGDJO>$mznIcDZanxrO*+B@j@I|PSR*mnjT5<<0=!0sH zrb~WRLNUopLHpnyL-%7nz1#dh`t>cIw|#0PMU+c#A?q9f_7=OT9kD(Q_ zr|9rop!2_fCvyCcz*Kq)t%jc)IV>#X-ry2(44*83dgF%r*1YOrnd5A3Mj(;U(@0I4 zdKJu3JeD^^Esfq0h#%_T_*{KImle?XW@%!3jSBaRi*3(u&T_q30q#GDdlQP{3uIvO z4RjFCHTIho^|O(yX+>DKcZhw~_s2u*eFav`Tb*`jrblxX^`sZ}qii<=B|qnzkG!9O z{RpFVYjOwCOa_!10Cg=lG-iMuKzrnB(-Uo2?l}OfouqINU!m z{uQ>*?@>IS^&G39?s9Y4T|^6cWzcnK&f)r12A2mF*-FYzNco3;(XDHH|jix=#Y~1OV&z~)x6aN5xuk=68vTn4?l_bJaasL2J&p(momBPhY zWDWwevJi5TFW!v$k8e>`Z%(Oqr`&&OUCFi?)C-9Y;*kU;h-1mg+ku|GoYSMWo?DR# zMpo+EhBkrJns%$CYI>9r!KUf=v)Y$-+^Rqfe~F2}?s)C_)U>xL8CQNUhH&vurbBZN zMH0El1Au)2`eL$x((fey0FO(HWo~mE!Igh8TY7(hE@qiwp7&2%q6-^;JI#;**W`~S zkF`ZUo8jnK3#~^`ykz6cwtzx_k9RpAl`Gr}Q&^ok8;wB7V}{x_wYGAM!Xicswg*$r zJ;|;_+t?P#)Y-BQc0kmosU$aqIRc?YbSzOKo*&DrCan^Ug)+RkG5e7871@D|`O_XGe9h`e2U;ai(~EjeMAOt^wJmJ5~h zBD)P5^6Gy9$4nk+t)OT=H9l{H=l;po&o820OZ&M`er%$Vo!m*94lSr~g3c2KimBAfPx2*tfct1(Hu=tIo*=pAFTD`U6$vkr* zQi_Lh$UU$r`qrawq~Bl5X2NY&?pJwk2O>ShlB@ahYoU|I8r%2@!PMU3drO43GrFme z#L7rQ#BM8*(3-;5Y%T7r&c79=+l_)tiy@3F9+KmzL)RYulmUl%YbL5R`G|9OBzqb) zT(jWeSbaws_N(_Ag{6wy=rGw@-^CM`m8UE~kmGj3q!0idd*Y#%Su~4gxSfgCZxf95 zw}}4$CL{6{ogKq1n@w=5ER1}#EHFs#k~yFY_ZqH;bV|>sUR$E!6x-Ye7#%RKync0h z(%VdU4c(>P%+cCLAMDe_6L~F#%LY7T{*}x$@C8x!DE)r>e~oUdMHhxI-|XrHhTc2| zJpJa!Uep29>DrZ-h`c%WUl7FjfwoC3f9q|D;5z!Rs-8ccVb9>}`)?HKcIme3OuTi9 z-s&HkIg`K9v64aNs(5QqSiaLcM6Kka`G`q7;w`m-^~Oixn)ELM>PNylwaxvE(o3R5 z5^-rFnFLJCHn>0C198t$wCB{9?ggKXx`JrBGWa`8mlr|S{{U8G7%b=RN1sGczmPTJ z@LtN90m>HYa2Z?uZH1ZUcS7{bx15`Y4DzoqZ(EP-WuOViKw^u#g-Naf+YC zwy{ZSn!c8it%c_*%Ahn&{Bbro;Nu6W2fbm(eSZ3UHX`O(Vln{~EO#oO_Ce{KicJk3 zlc8K%OW_$LI(&A`tK1t%MX6;5H2K}wF+3L9qzrzQh4BW_H5Tym#V}imG{p+LPSyfR z2T{}U827CY7~LHj^GDMnd<`}@<8T1MZ0?o67C8R^fKvQGh>asmw}|X?wUDOY;zgPA zzmo&_PzM~!`e?OFh??qIHkiwa+j-2VXh^BT_h zezMErD@YzfER~G+3Drs@zd-qGqH5yoRB7G&58#)D$DOYP^P=%}~f`Fd>zy3EC(i^Jd>O9+1O~pQ$_# z)YOhMP)MiJq-IR|?ynrI_E9~z8$eN=quV(-_pHrw>iYIA5Na0}60l;~er%WmR=@+qVOP4s)8@y43FPZ9miFf_wBM zW#g~!NbVfx4tij)C*Ff^FL>YbrWj=Y<9<|FE*=`x?(|(kS!D@%XLP13%UMY&aT)3l zzi@hkgIBKf*}N&K#i?j4xBeN_!H;C)D#_L0m=27+bJu{w9kX6HAHd(0bRHGGFn`3R z=GIH=9aX%!?NzWiy4fO-|ms6iu%vT< zcqhG9v+&KKk)&;NN|rd+dnjoD_S|-GGv0tD(WSPJ!#b>kPDl>e?gT9%lWbcwZD#jdLGNheXC!?x?||tcDLd=u41~98$Iwz z79QNcnu?3hp*h9}0=(Mx?(0yymgeqxC%Bc1JGsdwkR65H=Z5vGo%-*Gue95#o148V z#?Ea20Koo2N%U&x^z(6TaU83(DzIXN z3^4RQ)RWtT`KC@GtyR!nA_Yq;Qj`maE&L0;f)lR zmgoQg$vYzh`qqz!^`8-GlcX9Q_3UjU1rS2ytcTF2M*Kx}8s4XGG%?$1w+jxifVA>y zcG0RXe}sj=FX~tI#V|On4@r{F?O#xuD|DF}6BD1EgN?mAb?R%X(1+UXXV7%(o0~mi z(oZ(X$m0__ZVeIRsmZ|TdVP8I()5d&1HN3xxNua z+AU6VzB9>Bs+{B6m93y0#PcG`?Q95<&RSE+{4>ygwPwp$o*Q+vzKt~t5z-0rc6#h@ zyocy<>J3&)V0yRdQmv|YB++2G)_7cLcTwEw&|J6nb?^dPOOgc8IQz0je(U4DFvNci zGs4;nY5q0~sVFx3LN)B9a;~U1DF?VGZY!ID$jvfE1WhEa!BrGwvFre^SxqWW_(o^& z?6+{q;&!zdg$7&7Rt_!8_kN@gU_I#Q2bSxXlIpsx&FqcLu2=)q4A)QN8?rR7311K# z1eslbygzr5`0h2zv;<@fkF9NZhkN)JKxP>++}x+>86V1kCDE*om8JNO>^|(owopI! zgr_n;0E8n$fV_L=XSt9&1tEUqdCy)I zu~=fo{{V`6b^ic?=lKd?R8}LXBDb`ue%EI9+02XcC)9TQM;}nNpY~Xn@7>4$00Bln z(x8(&)5tRx?XC|-L&JWA(-FCRVri1%-9p>kpq@1y6gO4>0D!jT z{{Vp0O4Q|npo0GZO=irRWX0nrt3e?g{R0(0_R!P{)X`^NYiv~1*We8_XmL|@;UVbxvx4_t-lNE%cftEbiQSU%9w6rJsA&wsOy>l?r$MB z*9{nO=s;v~(bV!7eMLs5i#T2=rqQK;Hftm^*-QTbJ?&389_#7bkx;8urb%QqJzA@) zRDjbFU>Z<)6#!~*JcMoBdWxpfwoZ!Vjsvc24cZQa5jx;w_F)71C+S3wT=BAh^WOCszN{{RvL z*KekMYn-^yopNJs!}X-lBb7G5TZoj9H*P1_(x%d+lT5x#m`HqOUN;$86pS*k?g+@O zIqYPJoT1{hd@W}Pn&VZ!Ky0+|BxOJH(nI$X@jRbkYHT}~;l8r)b-Y?!wjXG<)CrqX zwUPHSLf>*Q*x}S+w`1>Ou{=qkE{}B+TFNeT%cS$H?o?n)AHSmxj5{B~s@_RHrF%8h z`=pv@F3pal*G1u$4PicutfYfVoS7n&47H@#;zb?7=*!%DfklRL1XYbYPq@^s(%Q}= zB(2+^1Gy)l1Ju>{^hw%fMv^o_*fJd84_=x5D)zZA*sPmO(G}vfaf$9&VoQ_n{nhke z&nB3PciM%vg#?ybEP`v-1uiegVcjD19<9{-3grV8aOy~4bDFwsFn(f9D78`iIixbH z#}urygwG>-j>p!l$>hs4l9c7xw@*_~@`>^8G-f!ntO{QMs_0B(<7V+HYH2W38<*^1M9|6?usk;GduJS+^Imcz^vznZxt_-0O9SAJqdi3cYu@-C zTlnR^@U83`<*>`TTdvE9hkfv$n0-b^y>hntb>+qCS=-ymCDXRWiAT&&rYq9CK)xUF zzPi8hE4#SVLZ!!_9os;GokL|d@;ds}n7$<2-s&v+rkE|nu$yyxYQe6f2l#`q`H!x7 zKB9q`FD0*mU~79vj_btEyJomwnJmHTsp|fxwQ_d%v%z|okxb&{AIuVG=G~mX&V&a`;;13j>=VS=bZUZU5$X`A z{{T~Pe~1t6zh7gK+*K=x9`nF6c05UBlVq zKAwbi2eB30X&O1wA!G;bc2Qjm>uHp6JYqIvAbRdlq;??sPzNL8sfYH|_OUi??E+e$ z9Y1!vN&HUZ_|{sH=qdvtk=fkk*pg4FG~o2aSOG};qw=Q&U+#*8Jvd1~69iB0ih3YI zc4c$a1I0kHQ-qWO)lG839XD1*5;H{h7fO0J%0iRxf(Z9PRMF|)9e=jk-)X0ol|1Nw z>!Ndk?nXsbj!cSTE~`|^)k$8ZN2Uj$cwYW}M^A>{-M`{^Uc4B54b<3E6lX(*=%nixwdkXM8TB%*QZ#|Z*~?)il&{cHs#L( z*7OcR^jGKg^b{=w$Q7zU)mg(uBS#{YbtPF(P)BNdwra#!4NnJ=Py@6(R*KU~Qn1RUO*F@vx3Zd2iv`MtI%$rs6{hT>z*3UkBp7uBdx}Y|;(b0VFDX_8 zkjg;*H$Bg;2Li8br1I#nT+T~K;}}F$9>WwEN2O>O#UYNBfX*;+(xH2NZ9j2>wW={_XJWxvtx2mH6v;BtNbVf`zQHu*f_ zyPp~lH$?EP{EeBr$sfJNs}$3{L2~t0AaI|r_k~}O;2m5EC3QWH?3L``%%l(E@8)`!Vk_1 z{cBn&W{So;IOJrvjO7)0+IsMMikPN5Qf;STF=WzVKX3*70jknzY`=CiVeBiSC_O0( zQD8Y{vs6Dk0e-7Mw~HLczm;^&9(sy}?4o|C2h)ly2S4^$PyDkK@3BC7#w%I$@9sGs z#-V#T)A&OVY5=cc8rfaNb8YfV6K+%JX_DLTk=+PV#GSa{eJWfs95Rr7#XB?sOsuKZ zay>^h zHI}6#lSoA|709Nr;-_V%6`%+Rrj@G%Q%ceTxmsyjvR0T@fFLPND^?2AO3(#zwHqy3 zDNJPcpbDjFrE1YiLX_+ULbRJI*2+z^fUIMr^6JT2LbMF8vlMNq_ELnP3fpMeR)SLb zbi(CLw1zy@zb=n1kX)tZpGrKUdsW{qjg$q-MjAs1tG-<($^zzmma6N9P)EIFU+60; zlMl+>y(_2LNwSrK=5C{=HP?jvISa`jood$!Du&4bvX$bQZ)T826$UF%r?oJrwH6DM zjC9hNtA#0yl)~jkOw@i~HF1)oD@BGuvYWE3ZKT^pfP-Zl4Nta>w5$bvv=q%)+e@~c zfT+-h7$Y@aT|OrM9jDY*p4wfstQRn((^!A2f6P^BEoYCa5$F$3C*1 z95BK6rQp{~C7MVu$`904NpzUo?qyH4D+S709rU_Js}1$9-;Dgr+1I!==^B*qNv3K4 z0A@ngq!zWCKXx<8g#Q4zPaec)n)8WtyNLOW?)ukB;EVQVmqXOiqJrS=f>s|aQjUi` zf$#M_@k?RuY9XnkJc@|NBr?2HAz340tf#RZYDDTP$Tdpxc&6?8(M2#Dk7`0ajTBG= zN}ozXr#&d5fDozlG~I{NiYNgJA4+LM*il6QG^e#Rr?nJN14C8YrM+T0S~dO&QNhD4+!L{i!w|Lq!w}LWk0m z%RZD*KnBC;X?CAVD4+l*wL53E6i@=}K8BPb^rDJF6em3?;XSCLm=Y|0)hfg@_kT(# RpbH2+s%0FGD58Kr|Jfm)sf+*s literal 0 HcmV?d00001 diff --git a/xs/src/qhull/html/qconvex.htm b/xs/src/qhull/html/qconvex.htm new file mode 100644 index 000000000..38a363b08 --- /dev/null +++ b/xs/src/qhull/html/qconvex.htm @@ -0,0 +1,630 @@ + + + + +qconvex -- convex hull + + + + +Up: +Home page for Qhull
    +Up: Qhull manual -- Table of Contents
    +To: Programs +• Options +• Output +• Formats +• Geomview +• Print +• Qhull +• Precision +• Trace +• Functions
    +To: synopsis +• input • outputs +• controls • graphics +• notes • conventions +• options +
    + +

    [cone]qconvex -- convex hull

    + +

    The convex hull of a set of points is the smallest convex set +containing the points. See the detailed introduction by O'Rourke +['94]. See Description of Qhull and How Qhull adds a point.

    + +
    +
    +
    Example: rbox 10 D3 | qconvex s o TO result
    +
    Compute the 3-d convex hull of 10 random points. Write a + summary to the console and the points and facets to + 'result'.
    + +
     
    +
    Example: rbox c | qconvex n
    +
    Print the normals for each facet of a cube.
    +
     
    +
    Example: rbox c | qconvex i Qt
    +
    Print the triangulated facets of a cube.
    +
     
    +
    Example: rbox y 500 W0 | qconvex
    +
    Compute the convex hull of a simplex with 500 + points on its surface.
    +
     
    +
    Example: rbox x W1e-12 1000 | qconvex + QR0
    +
    Compute the convex hull of 1000 points near the + surface of a randomly rotated simplex. Report + the maximum thickness of a facet.
    +
     
    +
    Example: rbox 1000 s | qconvex s FA
    +
    Compute the convex hull of 1000 cospherical + points. Verify the results and print a summary + with the total area and volume.
    +
     
    +
    Example: rbox d D12 | qconvex QR0 FA
    +
    Compute the convex hull of a 12-d diamond. + Randomly rotate the input. Note the large number + of facets and the small volume.
    +
     
    +
    Example: rbox c D7 | qconvex FA TF1000
    +
    Compute the convex hull of the 7-d hypercube. + Report on progress every 1000 facets. Computing + the convex hull of the 9-d hypercube takes too + much time and space.
    +
     
    +
    Example: rbox c d D2 | qconvex Qc s f Fx | more
    +
    Dump all fields of all facets for a square and a + diamond. Also print a summary and a list of + vertices. Note the coplanar points.
    +
     
    +
    +
    + +

    Except for rbox, all of the qhull programs compute a convex hull. + +

    By default, Qhull merges coplanar facets. For example, the convex +hull of a cube's vertices has six facets. + +

    If you use 'Qt' (triangulated output), +all facets will be simplicial (e.g., triangles in 2-d). For the cube +example, it will have 12 facets. Some facets may be +degenerate and have zero area. + +

    If you use 'QJ' (joggled input), +all facets will be simplicial. The corresponding vertices will be +slightly perturbed and identical points will be joggled apart. +Joggled input is less accurate that triangulated +output.See Merged facets or joggled input.

    + +

    The output for 4-d convex hulls may be confusing if the convex +hull contains non-simplicial facets (e.g., a hypercube). See +Why +are there extra points in a 4-d or higher convex hull?
    +

    +

    + +

    The 'qconvex' program is equivalent to +'qhull' in 2-d to 4-d, and +'qhull Qx' +in 5-d and higher. It disables the following Qhull +options: d v H Qbb Qf Qg Qm +Qr Qu Qv Qx Qz TR E V Fp Gt Q0,etc. + +

    Copyright © 1995-2015 C.B. Barber

    + +
    + +

    »qconvex synopsis

    +
    +qconvex- compute the convex hull.
    +    input (stdin): dimension, number of points, point coordinates
    +    comments start with a non-numeric character
    +
    +options (qconvex.htm):
    +    Qt   - triangulated output
    +    QJ   - joggle input instead of merging facets
    +    Tv   - verify result: structure, convexity, and point inclusion
    +    .    - concise list of all options
    +    -    - one-line description of all options
    +
    +output options (subset):
    +    s    - summary of results (default)
    +    i    - vertices incident to each facet
    +    n    - normals with offsets
    +    p    - vertex coordinates (includes coplanar points if 'Qc')
    +    Fx   - extreme points (convex hull vertices)
    +    FA   - compute total area and volume
    +    o    - OFF format (dim, n, points, facets)
    +    G    - Geomview output (2-d, 3-d, and 4-d)
    +    m    - Mathematica output (2-d and 3-d)
    +    QVn  - print facets that include point n, -n if not
    +    TO file- output results to file, may be enclosed in single quotes
    +
    +examples:
    +    rbox c D2 | qconvex s n                    rbox c D2 | qconvex i
    +    rbox c D2 | qconvex o                      rbox 1000 s | qconvex s Tv FA
    +    rbox c d D2 | qconvex s Qc Fx              rbox y 1000 W0 | qconvex s n
    +    rbox y 1000 W0 | qconvex s QJ              rbox d G1 D12 | qconvex QR0 FA Pp
    +    rbox c D7 | qconvex FA TF1000
    +
    + +

    »qconvex +input

    +
    + +

    The input data on stdin consists of:

    +
      +
    • dimension +
    • number of points
    • +
    • point coordinates
    • +
    + +

    Use I/O redirection (e.g., qconvex < data.txt), a pipe (e.g., rbox 10 | qconvex), +or the 'TI' option (e.g., qconvex TI data.txt). + +

    Comments start with a non-numeric character. Error reporting is +simpler if there is one point per line. Dimension +and number of points may be reversed. + +

    Here is the input for computing the convex +hull of the unit cube. The output is the normals, one +per facet.

    + +
    +

    rbox c > data

    +
    +3 RBOX c
    +8
    +  -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.5   -0.5   -0.5
    +   0.5   -0.5    0.5
    +   0.5    0.5   -0.5
    +   0.5    0.5    0.5
    +
    +

    qconvex s n < data

    +
    +
    +Convex hull of 8 points in 3-d:
    +
    +  Number of vertices: 8
    +  Number of facets: 6
    +  Number of non-simplicial facets: 6
    +
    +Statistics for: RBOX c | QCONVEX s n
    +
    +  Number of points processed: 8
    +  Number of hyperplanes created: 11
    +  Number of distance tests for qhull: 35
    +  Number of merged facets: 6
    +  Number of distance tests for merging: 84
    +  CPU seconds to compute hull (after input): 0.081
    +
    +4
    +6
    +     0      0     -1   -0.5
    +     0     -1      0   -0.5
    +     1      0      0   -0.5
    +    -1      0      0   -0.5
    +     0      1      0   -0.5
    +     0      0      1   -0.5
    +
    +
    + +
    +

    »qconvex outputs

    +
    + +

    These options control the output of qconvex. They may be used +individually or together.

    +
    +
    +
     
    +
    Vertices
    +
    Fx
    +
    list extreme points (i.e., vertices). The first line is the number of + extreme points. Each point is listed, one per line. The cube example + has eight vertices.
    +
    Fv
    +
    list vertices for each facet. The first line is the number of facets. + Each remaining line starts with the number of vertices. For the cube example, + each facet has four vertices.
    +
    i
    +
    list vertices for each facet. The first line is the number of facets. The + remaining lines list the vertices for each facet. In 4-d and + higher, triangulate non-simplicial facets by adding an extra point.
    +
     
    +
     
    +
    Coordinates
    +
    o
    +
    print vertices and facets of the convex hull in OFF format. The + first line is the dimension. The second line is the number of + vertices, facets, and ridges. The vertex + coordinates are next, followed by the facets. Each facet starts with + the number of vertices. The cube example has four vertices per facet.
    +
    Ft
    +
    print a triangulation of the convex hull in OFF format. The first line + is the dimension. The second line is the number of vertices and added points, + followed by the number of facets and the number of ridges. + The vertex coordinates are next, followed by the centrum coordinates. There is + one centrum for each non-simplicial facet. + The cube example has six centrums, one per square. + Each facet starts with the number of vertices or centrums. + In the cube example, each facet uses two vertices and one centrum.
    +
    p
    +
    print vertex coordinates. The first line is the dimension and the second + line is the number of vertices. The following lines are the coordinates of each + vertex. The cube example has eight vertices.
    +
    Qc p
    +
    print coordinates of vertices and coplanar points. The first line is the dimension. + The second line is the number of vertices and coplanar points. The coordinates + are next, one line per point. Use 'Qc Qi p' + to print the coordinates of all points.
    +
     
    +
     
    +
    Facets
    +
    Fn
    +
    list neighboring facets for each facet. The first line is the + number of facets. Each remaining line starts with the number of + neighboring facets. The cube example has four neighbors per facet.
    +
    FN
    +
    list neighboring facets for each point. The first line is the + total number of points. Each remaining line starts with the number of + neighboring facets. Each vertex of the cube example has three neighboring + facets. Use 'Qc Qi FN' + to include coplanar and interior points.
    +
    Fa
    +
    print area for each facet. The first line is the number of facets. + Facet area follows, one line per facet. For the cube example, each facet has area one.
    +
    FI
    +
    list facet IDs. The first line is the number of + facets. The IDs follow, one per line.
    + +
     
    +
     
    +
    Coplanar and interior points
    +
    Fc
    +
    list coplanar points for each facet. The first line is the number + of facets. The remaining lines start with the number of coplanar points. + A coplanar point is assigned to one facet.
    +
    Qi Fc
    +
    list interior points for each facet. The first line is the number + of facets. The remaining lines start with the number of interior points. + A coplanar point is assigned to one facet.
    +
    FP
    +
    print distance to nearest vertex for coplanar points. The first line is the + number of coplanar points. Each remaining line starts with the point ID of + a vertex, followed by the point ID of a coplanar point, its facet, and distance. + Use 'Qc Qi + FP' for coplanar and interior points.
    + +
     
    +
     
    +
    Hyperplanes
    +
    n
    +
    print hyperplane for each facet. The first line is the dimension. The + second line is the number of facets. Each remaining line is the hyperplane's + coefficients followed by its offset.
    +
    Fo
    +
    print outer plane for each facet. The output plane is above all points. + The first line is the dimension. The + second line is the number of facets. Each remaining line is the outer plane's + coefficients followed by its offset.
    +
    Fi
    +
    print inner plane for each facet. The inner plane of a facet is + below its vertices. + The first line is the dimension. The + second line is the number of facets. Each remaining line is the inner plane's + coefficients followed by its offset.
    + +
     
    +
     
    +
    General
    +
    s
    +
    print summary for the convex hull. Use 'Fs' and 'FS' if you need numeric data.
    +
    FA
    +
    compute total area and volume for 's' and 'FS'
    +
    m
    +
    Mathematica output for the convex hull in 2-d or 3-d.
    +
    FM
    +
    Maple output for the convex hull in 2-d or 3-d.
    +
    G
    +
    Geomview output for the convex hull in 2-d, 3-d, or 4-d.
    + +
     
    +
     
    +
    Scaling and rotation
    +
    Qbk:n
    +
    scale k'th coordinate to lower bound.
    +
    QBk:n
    +
    scale k'th coordinate to upper bound.
    +
    QbB
    +
    scale input to unit cube centered at the origin.
    +
    QRn
    +
    randomly rotate the input with a random seed of n. If n=0, the + seed is the time. If n=-1, use time for the random seed, but do + not rotate the input.
    +
    Qbk:0Bk:0
    +
    remove k'th coordinate from input. This computes the + convex hull in one lower dimension.
    +
    +
    + +
    +

    »qconvex controls

    +
    + +

    These options provide additional control:

    + +
    +
    +
    Qt
    +
    triangulated output. Qhull triangulates non-simplicial facets. It may produce + degenerate facets of zero area.
    +
    QJ
    +
    joggle the input instead of merging facets. This guarantees simplicial facets + (e.g., triangles in 3-d). It is less accurate than triangulated output ('Qt').
    +
    Qc
    +
    keep coplanar points
    +
    Qi
    +
    keep interior points
    +
    f
    +
    facet dump. Print the data structure for each facet.
    +
    QVn
    +
    select facets containing point n as a vertex,
    +
    QGn
    +
    select facets that are visible from point n + (marked 'good'). Use -n for the remainder.
    +
    PDk:0
    +
    select facets with a negative coordinate for dimension k
    +
    TFn
    +
    report progress after constructing n facets
    +
    Tv
    +
    verify result
    +
    TI file
    +
    input data from file. The filename may not use spaces or quotes.
    +
    TO file
    +
    output results to file. Use single quotes if the filename + contains spaces (e.g., TO 'file with spaces.txt'
    +
    Qs
    +
    search all points for the initial simplex. If Qhull can + not construct an initial simplex, it reports a +descriptive message. Usually, the point set is degenerate and one +or more dimensions should be removed ('Qbk:0Bk:0'). +If not, use option 'Qs'. It performs an exhaustive search for the +best initial simplex. This is expensive is high dimensions.
    +
    +
    + +
    +

    »qconvex graphics

    +
    + +

    Display 2-d, 3-d, and 4-d convex hulls with Geomview ('G').

    + +

    Display 2-d and 3-d convex hulls with Mathematica ('m').

    + +

    To view 4-d convex hulls in 3-d, use 'Pd0d1d2d3' to select the positive +octant and 'GrD2' to drop dimension +2.

    + +
    +

    »qconvex notes

    +
    + +

    Qhull always computes a convex hull. The +convex hull may be used for other geometric structures. The +general technique is to transform the structure into an +equivalent convex hull problem. For example, the Delaunay +triangulation is equivalent to the convex hull of the input sites +after lifting the points to a paraboloid.

    + +
    +

    »qconvex +conventions

    +
    + +

    The following terminology is used for convex hulls in Qhull. +See Qhull's data structures.

    + +
      +
    • point - d coordinates
    • +
    • vertex - extreme point of the input set
    • +
    • ridge - d-1 vertices between two + neighboring facets
    • +
    • hyperplane - halfspace defined by a unit normal + and offset
    • +
    • coplanar point - a nearly incident point to a + hyperplane
    • +
    • centrum - a point on the hyperplane for testing + convexity
    • +
    • facet - a facet with vertices, ridges, coplanar + points, neighboring facets, and hyperplane
    • +
    • simplicial facet - a facet with d + vertices, d ridges, and d neighbors
    • +
    • non-simplicial facet - a facet with more than d + vertices
    • +
    • good facet - a facet selected by 'QVn', etc.
    • +
    +
    +

    »qconvex options

    + +
    +qconvex- compute the convex hull
    +    http://www.qhull.org
    +
    +input (stdin):
    +    first lines: dimension and number of points (or vice-versa).
    +    other lines: point coordinates, best if one point per line
    +    comments:    start with a non-numeric character
    +
    +options:
    +    Qt   - triangulated output
    +    QJ   - joggle input instead of merging facets
    +    Qc   - keep coplanar points with nearest facet
    +    Qi   - keep interior points with nearest facet
    +
    +Qhull control options:
    +    Qbk:n   - scale coord k so that low bound is n
    +      QBk:n - scale coord k so that upper bound is n (QBk is 0.5)
    +    QbB  - scale input to unit cube centered at the origin
    +    Qbk:0Bk:0 - remove k-th coordinate from input
    +    QJn  - randomly joggle input in range [-n,n]
    +    QRn  - random rotation (n=seed, n=0 time, n=-1 time/no rotate)
    +    Qs   - search all points for the initial simplex
    +    QGn  - good facet if visible from point n, -n for not visible
    +    QVn  - good facet if it includes point n, -n if not
    +
    +Trace options:
    +    T4   - trace at level n, 4=all, 5=mem/gauss, -1= events
    +    Tc   - check frequently during execution
    +    Ts   - print statistics
    +    Tv   - verify result: structure, convexity, and point inclusion
    +    Tz   - send all output to stdout
    +    TFn  - report summary when n or more facets created
    +    TI file - input data from file, no spaces or single quotes
    +    TO file - output results to file, may be enclosed in single quotes
    +    TPn  - turn on tracing when point n added to hull
    +     TMn - turn on tracing at merge n
    +     TWn - trace merge facets when width > n
    +    TVn  - stop qhull after adding point n, -n for before (see TCn)
    +     TCn - stop qhull after building cone for point n (see TVn)
    +
    +Precision options:
    +    Cn   - radius of centrum (roundoff added).  Merge facets if non-convex
    +     An  - cosine of maximum angle.  Merge facets if cosine > n or non-convex
    +           C-0 roundoff, A-0.99/C-0.01 pre-merge, A0.99/C0.01 post-merge
    +    Rn   - randomly perturb computations by a factor of [1-n,1+n]
    +    Un   - max distance below plane for a new, coplanar point
    +    Wn   - min facet width for outside point (before roundoff)
    +
    +Output formats (may be combined; if none, produces a summary to stdout):
    +    f    - facet dump
    +    G    - Geomview output (see below)
    +    i    - vertices incident to each facet
    +    m    - Mathematica output (2-d and 3-d)
    +    n    - normals with offsets
    +    o    - OFF file format (dim, points and facets; Voronoi regions)
    +    p    - point coordinates
    +    s    - summary (stderr)
    +
    +More formats:
    +    Fa   - area for each facet
    +    FA   - compute total area and volume for option 's'
    +    Fc   - count plus coplanar points for each facet
    +           use 'Qc' (default) for coplanar and 'Qi' for interior
    +    FC   - centrum for each facet
    +    Fd   - use cdd format for input (homogeneous with offset first)
    +    FD   - use cdd format for numeric output (offset first)
    +    FF   - facet dump without ridges
    +    Fi   - inner plane for each facet
    +    FI   - ID for each facet
    +    Fm   - merge count for each facet (511 max)
    +    FM   - Maple output (2-d and 3-d)
    +    Fn   - count plus neighboring facets for each facet
    +    FN   - count plus neighboring facets for each point
    +    Fo   - outer plane (or max_outside) for each facet
    +    FO   - options and precision constants
    +    FP   - nearest vertex for each coplanar point
    +    FQ   - command used for qconvex
    +    Fs   - summary: #int (8), dimension, #points, tot vertices, tot facets,
    +                      for output: #vertices, #facets,
    +                                  #coplanar points, #non-simplicial facets
    +                    #real (2), max outer plane, min vertex
    +    FS   - sizes:   #int (0)
    +                    #real(2) tot area, tot volume
    +    Ft   - triangulation with centrums for non-simplicial facets (OFF format)
    +    Fv   - count plus vertices for each facet
    +    FV   - average of vertices (a feasible point for 'H')
    +    Fx   - extreme points (in order for 2-d)
    +
    +Geomview output (2-d, 3-d, and 4-d)
    +    Ga   - all points as dots
    +     Gp  -  coplanar points and vertices as radii
    +     Gv  -  vertices as spheres
    +    Gi   - inner planes only
    +     Gn  -  no planes
    +     Go  -  outer planes only
    +    Gc   - centrums
    +    Gh   - hyperplane intersections
    +    Gr   - ridges
    +    GDn  - drop dimension n in 3-d and 4-d output
    +
    +Print options:
    +    PAn  - keep n largest facets by area
    +    Pdk:n - drop facet if normal[k] <= n (default 0.0)
    +    PDk:n - drop facet if normal[k] >= n
    +    Pg   - print good facets (needs 'QGn' or 'QVn')
    +    PFn  - keep facets whose area is at least n
    +    PG   - print neighbors of good facets
    +    PMn  - keep n facets with most merges
    +    Po   - force output.  If error, output neighborhood of facet
    +    Pp   - do not report precision problems
    +
    +    .    - list of all options
    +    -    - one line descriptions of all options
    +
    +
    + + +
    + +

    Up: Home page for Qhull
    +Up: Qhull manual: Table of Contents
    +To: Programs +•Options +• Output +• Formats +• Geomview +• Print +• Qhull +• Precision +• Trace +• Functions
    +To: synopsis +• input • outputs +• controls • graphics +• notes • conventions +• options + +


    + +

    The Geometry Center +Home Page

    + +

    Comments to: qhull@qhull.org +
    +Created: Sept. 25, 1995 --- Last modified: see top

    + + diff --git a/xs/src/qhull/html/qdelau_f.htm b/xs/src/qhull/html/qdelau_f.htm new file mode 100644 index 000000000..d8981e16b --- /dev/null +++ b/xs/src/qhull/html/qdelau_f.htm @@ -0,0 +1,416 @@ + + + + +qdelaunay Qu -- furthest-site Delaunay triangulation + + + + +Up: +Home page for Qhull
    +Up: Qhull manual: Table of Contents
    +To: Programs +• Options +• Output +• Formats +• Geomview +• Print +• Qhull +• Precision +• Trace +• Functions
    +To: synopsis +• input • outputs +• controls • graphics +• notes • conventions +• options + +
    + +

    [delaunay]qdelaunay Qu -- furthest-site Delaunay triangulation

    + +

    The furthest-site Delaunay triangulation corresponds to the upper facets of the Delaunay construction. +Its vertices are the +extreme points of the input sites. +It is the dual of the furthest-site Voronoi diagram. + +

    +
    +
    Example: rbox 10 D2 | qdelaunay Qu Qt s + i TO + result
    +
    Compute the 2-d, furthest-site Delaunay triangulation of 10 random + points. Triangulate the output. + Write a summary to the console and the regions to + 'result'.
    +
     
    +
    Example: rbox 10 D2 | qdelaunay Qu QJ s + i TO + result
    +
    Compute the 2-d, furthest-site Delaunay triangulation of 10 random + points. Joggle the input to guarantee triangular output. + Write a summary to the console and the regions to + 'result'.
    +
     
    +
    Example: rbox r y c G1 D2 | qdelaunay Qu s + Fv TO + result
    +
    Compute the 2-d, furthest-site Delaunay triangulation of a triangle inside + a square. + Write a summary to the console and unoriented regions to 'result'. + Merge regions for cocircular input sites (e.g., the square). + The square is the only furthest-site + Delaunay region.
    +
    +
    + +

    As with the Delaunay triangulation, Qhull computes the +furthest-site Delaunay triangulation by lifting the input sites to a +paraboloid. The lower facets correspond to the Delaunay +triangulation while the upper facets correspond to the +furthest-site triangulation. Neither triangulation includes +"vertical" facets (i.e., facets whose last hyperplane +coefficient is nearly zero). Vertical facets correspond to input +sites that are coplanar to the convex hull of the input. An +example is points on the boundary of a lattice.

    + +

    By default, qdelaunay merges cocircular and cospherical regions. +For example, the furthest-site Delaunay triangulation of a square inside a diamond +('rbox D2 c d G4 | qdelaunay Qu') consists of one region (the diamond). + +

    If you use 'Qt' (triangulated output), +all furthest-site Delaunay regions will be simplicial (e.g., triangles in 2-d). +Some regions may be +degenerate and have zero area. + +

    If you use 'QJ' (joggled input), all furthest-site +Delaunay regions +will be simplicial (e.g., triangles in 2-d). Joggled input +is less accurate than triangulated output ('Qt'). See Merged facets or joggled input.

    + +

    The output for 3-d, furthest-site Delaunay triangulations may be confusing if the +input contains cospherical data. See the FAQ item +Why +are there extra points in a 4-d or higher convex hull? +Avoid these problems with triangulated output ('Qt') or +joggled input ('QJ'). +

    + +

    The 'qdelaunay' program is equivalent to +'qhull d Qbb' in 2-d to 3-d, and +'qhull d Qbb Qx' +in 4-d and higher. It disables the following Qhull +options: d n v H U Qb QB Qc Qf Qg Qi +Qm Qr QR Qv Qx TR E V FC Fi Fo Fp FV Q0,etc. + + +

    Copyright © 1995-2015 C.B. Barber

    + +
    + +

    »furthest-site qdelaunay synopsis

    +
    + +See qdelaunay synopsis. The same +program is used for both constructions. Use option 'Qu' +for furthest-site Delaunay triangulations. + +
    +

    »furthest-site qdelaunay +input

    + +
    +

    The input data on stdin consists of:

    +
      +
    • dimension +
    • number of points
    • +
    • point coordinates
    • +
    + +

    Use I/O redirection (e.g., qdelaunay Qu < data.txt), a pipe (e.g., rbox 10 | qdelaunay Qu), +or the 'TI' option (e.g., qdelaunay Qu TI data.txt). + +

    For example, this is a square containing four random points. +Its furthest-site Delaunay +triangulation contains one square. +

    +

    +rbox c 4 D2 > data +
    +2 RBOX c 4 D2
    +8
    +-0.4999921736307369 -0.3684622117955817
    +0.2556053225468894 -0.0413498678629751
    +0.0327672376602583 -0.2810408135699488
    +-0.452955383763607 0.17886471718444
    +  -0.5   -0.5
    +  -0.5    0.5
    +   0.5   -0.5
    +   0.5    0.5
    +
    + +

    qdelaunay Qu i < data +

    +
    +Furthest-site Delaunay triangulation by the convex hull of 8 points in 3-d:
    +
    +  Number of input sites: 8
    +  Number of Delaunay regions: 1
    +  Number of non-simplicial Delaunay regions: 1
    +
    +Statistics for: RBOX c 4 D2 | QDELAUNAY s Qu i
    +
    +  Number of points processed: 8
    +  Number of hyperplanes created: 20
    +  Number of facets in hull: 11
    +  Number of distance tests for qhull: 34
    +  Number of merged facets: 1
    +  Number of distance tests for merging: 107
    +  CPU seconds to compute hull (after input): 0.02
    +
    +1
    +7 6 4 5
    +
    +
    + +
    +

    »furthest-site qdelaunay +outputs

    +
    + +

    These options control the output of furthest-site Delaunay triangulations:

    +
    + +
    +
    furthest-site Delaunay regions
    +
    i
    +
    list input sites for each furthest-site Delaunay region. The first line is the number of regions. The + remaining lines list the input sites for each region. The regions are + oriented. In 3-d and + higher, report cospherical sites by adding extra points. For the points-in-square example, + the square is the only furthest-site Delaunay region.
    +
    Fv
    +
    list input sites for each furthest-site Delaunay region. The first line is the number of regions. + Each remaining line starts with the number of input sites. The regions + are unoriented. For the points-in-square example, + the square is the only furthest-site Delaunay region.
    +
    Ft
    +
    print a triangulation of the furthest-site Delaunay regions in OFF format. The first line + is the dimension. The second line is the number of input sites and added points, + followed by the number of simplices and the number of ridges. + The input coordinates are next, followed by the centrum coordinates. There is + one centrum for each non-simplicial furthest-site Delaunay region. Each remaining line starts + with dimension+1. The + simplices are oriented. + For the points-in-square example, the square has a centrum at the + origin. It splits the square into four triangular regions.
    +
    Fn
    +
    list neighboring regions for each furthest-site Delaunay region. The first line is the + number of regions. Each remaining line starts with the number of + neighboring regions. Negative indices (e.g., -1) indicate regions + outside of the furthest-site Delaunay triangulation. + For the points-in-square example, the four neighboring regions + are outside of the triangulation. They belong to the regular + Delaunay triangulation.
    +
    FN
    +
    list the furthest-site Delaunay regions for each input site. The first line is the + total number of input sites. Each remaining line starts with the number of + furthest-site Delaunay regions. Negative indices (e.g., -1) indicate regions + outside of the furthest-site Delaunay triangulation. + For the points-in-square example, the four random points belong to no region + while the square's vertices belong to region 0 and three + regions outside of the furthest-site Delaunay triangulation.
    +
    Fa
    +
    print area for each furthest-site Delaunay region. The first line is the number of regions. + The areas follow, one line per region. For the points-in-square example, the + square has unit area.
    + +
     
    +
     
    +
    Input sites
    +
    Fx
    +
    list extreme points of the input sites. These points are vertices of the furthest-point + Delaunay triangulation. They are on the + boundary of the convex hull. The first line is the number of + extreme points. Each point is listed, one per line. The points-in-square example + has four extreme points.
    +
     
    +
     
    +
    General
    +
    FA
    +
    compute total area for 's' + and 'FS'. This is the + same as the area of the convex hull.
    +
    o
    +
    print upper facets of the corresponding convex hull (a + paraboloid)
    +
    m
    +
    Mathematica output for the upper facets of the paraboloid (2-d triangulations).
    +
    FM
    +
    Maple output for the upper facets of the paraboloid (2-d triangulations).
    +
    G
    +
    Geomview output for the paraboloid (2-d or 3-d triangulations).
    +
    s
    +
    print summary for the furthest-site Delaunay triangulation. Use 'Fs' and 'FS' for numeric data.
    +
    +
    + +
    +

    »furthest-site qdelaunay +controls

    +
    + +

    These options provide additional control:

    +
    + +
    +
    Qu
    +
    must be used for furthest-site Delaunay triangulation.
    +
    Qt
    +
    triangulated output. Qhull triangulates non-simplicial facets. It may produce + degenerate facets of zero area.
    +
    QJ
    +
    joggle the input to avoid cospherical and coincident + sites. It is less accurate than triangulated output ('Qt').
    +
    QVn
    +
    select facets adjacent to input site n (marked + 'good').
    +
    Tv
    +
    verify result.
    +
    TI file
    +
    input data from file. The filename may not use spaces or quotes.
    +
    TO file
    +
    output results to file. Use single quotes if the filename + contains spaces (e.g., TO 'file with spaces.txt'
    +
    TFn
    +
    report progress after constructing n facets
    +
    PDk:1
    +
    include upper and lower facets in the output. Set k + to the last dimension (e.g., 'PD2:1' for 2-d inputs).
    +
    f
    +
    facet dump. Print the data structure for each facet (i.e., furthest-site Delaunay region).
    +
    +
    + +
    +

    »furthest-site qdelaunay +graphics

    +
    + +See Delaunay graphics. +They are the same except for Mathematica and Maple output. + +
    +

    »furthest-site +qdelaunay notes

    +
    + +

    The furthest-site Delaunay triangulation does not +record coincident input sites. Use qdelaunay instead. + +

    qdelaunay Qu does not work for purely cocircular +or cospherical points (e.g., rbox c | qdelaunay Qu). Instead, +use qdelaunay Qz -- when all points are vertices of the convex +hull of the input sites, the Delaunay triangulation is the same +as the furthest-site Delaunay triangulation. + +

    A non-simplicial, furthest-site Delaunay region indicates nearly cocircular or +cospherical input sites. To avoid non-simplicial regions triangulate +the output ('Qt') or joggle +the input ('QJ'). Joggled input +is less accurate than triangulated output. +You may also triangulate +non-simplicial regions with option 'Ft'. It adds +the centrum to non-simplicial regions. Alternatively, use an exact arithmetic code.

    + +

    Furthest-site Delaunay triangulations do not include facets that are +coplanar with the convex hull of the input sites. A facet is +coplanar if the last coefficient of its normal is +nearly zero (see qh_ZEROdelaunay). + +

    +

    »furthest-site qdelaunay conventions

    +
    + +

    The following terminology is used for furthest-site Delaunay +triangulations in Qhull. The underlying structure is the upper +facets of a convex hull in one higher dimension. See convex hull conventions, Delaunay conventions, +and Qhull's data structures

    +
    +
      +
    • input site - a point in the input (one dimension + lower than a point on the convex hull)
    • +
    • point - d+1 coordinates. The last + coordinate is the sum of the squares of the input site's + coordinates
    • +
    • vertex - a point on the paraboloid. It + corresponds to a unique input site.
    • +
    • furthest-site Delaunay facet - an upper facet of the + paraboloid. The last coefficient of its normal is + clearly positive.
    • +
    • furthest-site Delaunay region - a furthest-site Delaunay + facet projected to the input sites
    • +
    • non-simplicial facet - more than d + points are cocircular or cospherical
    • +
    • good facet - a furthest-site Delaunay facet with optional + restrictions by 'QVn', etc.
    • +
    +
    +
    +

    »furthest-site qdelaunay options

    +
    + +See qdelaunay options. The same +program is used for both constructions. Use option 'Qu' +for furthest-site Delaunay triangulations. + +
    + +
    + +

    Up: Home page for Qhull
    +Up: Qhull manual: Table of Contents
    +To: Programs +• Options +• Output +• Formats +• Geomview +• Print +• Qhull +• Precision +• Trace +• Functions
    +To: synopsis +• input • outputs +• controls • graphics +• notes • conventions +• options + +


    + +

    The Geometry Center +Home Page

    + +

    Comments to: qhull@qhull.org +
    +Created: Sept. 25, 1995 --- Last modified: see top

    + + diff --git a/xs/src/qhull/html/qdelaun.htm b/xs/src/qhull/html/qdelaun.htm new file mode 100644 index 000000000..a42223c66 --- /dev/null +++ b/xs/src/qhull/html/qdelaun.htm @@ -0,0 +1,628 @@ + + + + +qdelaunay -- Delaunay triangulation + + + + +Up: +Home page for Qhull
    +Up: Qhull manual: Table of Contents
    +To: Programs +• Options +• Output +• Formats +• Geomview +• Print +• Qhull +• Precision +• Trace +• Functions
    +To: synopsis +• input • outputs +• controls • graphics +• notes • conventions +• options + +
    + +

    [delaunay]qdelaunay -- Delaunay triangulation

    + +

    The Delaunay triangulation is the triangulation with empty +circumspheres. It has many useful properties and applications. +See the survey article by Aurenhammer ['91] and the detailed introduction +by O'Rourke ['94].

    + +
    +
    +
    Example: rbox r y c G0.1 D2 | qdelaunay s + Fv TO + result
    +
    Compute the 2-d Delaunay triangulation of a triangle and + a small square. + Write a summary to the console and unoriented regions to 'result'. + Merge regions for cocircular input sites (i.e., the + square).
    +
     
    +
    Example: rbox r y c G0.1 D2 | qdelaunay s + Fv Qt
    +
    Compute the 2-d Delaunay triangulation of a triangle and + a small square. Write a summary and unoriented + regions to the console. Produce triangulated output.
    +
     
    +
    Example: rbox 10 D2 | qdelaunay QJ s + i TO + result
    +
    Compute the 2-d Delaunay triangulation of 10 random + points. Joggle the input to guarantee triangular output. + Write a summary to the console and the regions to + 'result'.
    +
    +
    + +

    Qhull computes the Delaunay triangulation by computing a +convex hull. It lifts the input sites to a paraboloid by adding +the sum of the squares of the coordinates. It scales the height +of the paraboloid to improve numeric precision ('Qbb'). +It computes the convex +hull of the lifted sites, and projects the lower convex hull to +the input. + +

    Each region of the Delaunay triangulation +corresponds to a facet of the lower half of the convex hull. +Facets of the upper half of the convex hull correspond to the furthest-site Delaunay triangulation. +See the examples, Delaunay and +Voronoi diagrams.

    + +

    See Qhull FAQ - Delaunay and +Voronoi diagram questions.

    + +

    By default, qdelaunay merges cocircular and cospherical regions. +For example, the Delaunay triangulation of a square inside a diamond +('rbox D2 c d G4 | qdelaunay') contains one region for the square. + +

    Use option 'Qz' if the input is circular, cospherical, or +nearly so. It improves precision by adding a point "at infinity," above the corresponding paraboloid. + +

    If you use 'Qt' (triangulated output), +all Delaunay regions will be simplicial (e.g., triangles in 2-d). +Some regions may be +degenerate and have zero area. Triangulated output identifies coincident +points. + +

    If you use 'QJ' (joggled input), all Delaunay regions +will be simplicial (e.g., triangles in 2-d). Coincident points will +create small regions since the points are joggled apart. Joggled input +is less accurate than triangulated output ('Qt'). See Merged facets or joggled input.

    + +

    The output for 3-d Delaunay triangulations may be confusing if the +input contains cospherical data. See the FAQ item +Why +are there extra points in a 4-d or higher convex hull? +Avoid these problems with triangulated output ('Qt') or +joggled input ('QJ'). +

    + +

    The 'qdelaunay' program is equivalent to +'qhull d Qbb' in 2-d to 3-d, and +'qhull d Qbb Qx' +in 4-d and higher. It disables the following Qhull +options: d n v H U Qb QB Qc Qf Qg Qi +Qm Qr QR Qv Qx TR E V FC Fi Fo Fp Ft FV Q0,etc. + + +

    Copyright © 1995-2015 C.B. Barber

    + +
    + +

    »qdelaunay synopsis

    + +
    +qdelaunay- compute the Delaunay triangulation.
    +    input (stdin): dimension, number of points, point coordinates
    +    comments start with a non-numeric character
    +
    +options (qdelaun.htm):
    +    Qt   - triangulated output
    +    QJ   - joggle input instead of merging facets
    +    Qu   - furthest-site Delaunay triangulation
    +    Tv   - verify result: structure, convexity, and in-circle test
    +    .    - concise list of all options
    +    -    - one-line description of all options
    +
    +output options (subset):
    +    s    - summary of results (default)
    +    i    - vertices incident to each Delaunay region
    +    Fx   - extreme points (vertices of the convex hull)
    +    o    - OFF format (shows the points lifted to a paraboloid)
    +    G    - Geomview output (2-d and 3-d points lifted to a paraboloid)
    +    m    - Mathematica output (2-d inputs lifted to a paraboloid)
    +    QVn  - print Delaunay regions that include point n, -n if not
    +    TO file- output results to file, may be enclosed in single quotes
    +
    +examples:
    +    rbox c P0 D2 | qdelaunay s o          rbox c P0 D2 | qdelaunay i
    +    rbox c P0 D3 | qdelaunay Fv Qt        rbox c P0 D2 | qdelaunay s Qu Fv
    +    rbox c G1 d D2 | qdelaunay s i        rbox c G1 d D2 | qdelaunay s i Qt
    +    rbox M3,4 z 100 D2 | qdelaunay s      rbox M3,4 z 100 D2 | qdelaunay s Qt
    +
    + + +

    »qdelaunay +input

    + +
    +

    The input data on stdin consists of:

    +
      +
    • dimension +
    • number of points
    • +
    • point coordinates
    • +
    + +

    Use I/O redirection (e.g., qdelaunay < data.txt), a pipe (e.g., rbox 10 | qdelaunay), +or the 'TI' option (e.g., qdelaunay TI data.txt). + +

    For example, this is four cocircular points inside a square. Its Delaunay +triangulation contains 8 triangles and one four-sided +figure. +

    +

    +rbox s 4 W0 c G1 D2 > data +
    +2 RBOX s 4 W0 c D2
    +8
    +-0.4941988586954018 -0.07594397977563715
    +-0.06448037284989526 0.4958248496365813
    +0.4911154367094632 0.09383830681375946
    +-0.348353580869097 -0.3586778257652367
    +    -1     -1
    +    -1      1
    +     1     -1
    +     1      1
    +
    + +

    qdelaunay s i < data +

    +
    +Delaunay triangulation by the convex hull of 8 points in 3-d
    +
    +  Number of input sites: 8
    +  Number of Delaunay regions: 9
    +  Number of non-simplicial Delaunay regions: 1
    +
    +Statistics for: RBOX s 4 W0 c D2 | QDELAUNAY s i
    +
    +  Number of points processed: 8
    +  Number of hyperplanes created: 18
    +  Number of facets in hull: 10
    +  Number of distance tests for qhull: 33
    +  Number of merged facets: 2
    +  Number of distance tests for merging: 102
    +  CPU seconds to compute hull (after input): 0.028
    +
    +9
    +1 7 5
    +6 3 4
    +2 3 6
    +7 2 6
    +2 7 1
    +0 5 4
    +3 0 4
    +0 1 5
    +1 0 3 2
    +
    +
    + +
    +

    »qdelaunay +outputs

    +
    + +

    These options control the output of Delaunay triangulations:

    +
    + +
    +
    Delaunay regions
    +
    i
    +
    list input sites for each Delaunay region. The first line is the number of regions. The + remaining lines list the input sites for each region. The regions are + oriented. In 3-d and + higher, report cospherical sites by adding extra points. Use triangulated + output ('Qt') to avoid non-simpicial regions. For the circle-in-square example, + eight Delaunay regions are triangular and the ninth has four input sites.
    +
    Fv
    +
    list input sites for each Delaunay region. The first line is the number of regions. + Each remaining line starts with the number of input sites. The regions + are unoriented. For the circle-in-square example, + eight Delaunay regions are triangular and the ninth has four input sites.
    +
    Fn
    +
    list neighboring regions for each Delaunay region. The first line is the + number of regions. Each remaining line starts with the number of + neighboring regions. Negative indices (e.g., -1) indicate regions + outside of the Delaunay triangulation. + For the circle-in-square example, the four regions on the square are neighbors to + the region-at-infinity.
    +
    FN
    +
    list the Delaunay regions for each input site. The first line is the + total number of input sites. Each remaining line starts with the number of + Delaunay regions. Negative indices (e.g., -1) indicate regions + outside of the Delaunay triangulation. + For the circle-in-square example, each point on the circle belongs to four + Delaunay regions. Use 'Qc FN' + to include coincident input sites and deleted vertices.
    +
    Fa
    +
    print area for each Delaunay region. The first line is the number of regions. + The areas follow, one line per region. For the circle-in-square example, the + cocircular region has area 0.4.
    +
     
    +
     
    +
    Input sites
    +
    Fc
    +
    list coincident input sites for each Delaunay region. + The first line is the number of regions. The remaining lines start with + the number of coincident sites and deleted vertices. Deleted vertices + indicate highly degenerate input (see'Fs'). + A coincident site is assigned to one Delaunay + region. Do not use 'QJ' with 'Fc'; the joggle will separate + coincident sites.
    +
    FP
    +
    print coincident input sites with distance to + nearest site (i.e., vertex). The first line is the + number of coincident sites. Each remaining line starts with the point ID of + an input site, followed by the point ID of a coincident point, its region, and distance. + Includes deleted vertices which + indicate highly degenerate input (see'Fs'). + Do not use 'QJ' with 'FP'; the joggle will separate + coincident sites.
    +
    Fx
    +
    list extreme points of the input sites. These points are on the + boundary of the convex hull. The first line is the number of + extreme points. Each point is listed, one per line. The circle-in-square example + has four extreme points.
    +
     
    +
     
    +
    General
    +
    FA
    +
    compute total area for 's' + and 'FS'
    +
    o
    +
    print lower facets of the corresponding convex hull (a + paraboloid)
    +
    m
    +
    Mathematica output for the lower facets of the paraboloid (2-d triangulations).
    +
    FM
    +
    Maple output for the lower facets of the paraboloid (2-d triangulations).
    +
    G
    +
    Geomview output for the paraboloid (2-d or 3-d triangulations).
    +
    s
    +
    print summary for the Delaunay triangulation. Use 'Fs' and 'FS' for numeric data.
    +
    +
    + +
    +

    »qdelaunay +controls

    +
    + +

    These options provide additional control:

    +
    + +
    +
    Qt
    +
    triangulated output. Qhull triangulates non-simplicial facets. It may produce +degenerate facets of zero area.
    +
    QJ
    +
    joggle the input to avoid cospherical and coincident + sites. It is less accurate than triangulated output ('Qt').
    +
    Qu
    +
    compute the furthest-site Delaunay triangulation.
    +
    Qz
    +
    add a point above the paraboloid to reduce precision + errors. Use it for nearly cocircular/cospherical input + (e.g., 'rbox c | qdelaunay Qz'). The point is printed for + options 'Ft' and 'o'.
    +
    QVn
    +
    select facets adjacent to input site n (marked + 'good').
    +
    Tv
    +
    verify result.
    +
    TI file
    +
    input data from file. The filename may not use spaces or quotes.
    +
    TO file
    +
    output results to file. Use single quotes if the filename + contains spaces (e.g., TO 'file with spaces.txt'
    +
    TFn
    +
    report progress after constructing n facets
    +
    PDk:1
    +
    include upper and lower facets in the output. Set k + to the last dimension (e.g., 'PD2:1' for 2-d inputs).
    +
    f
    +
    facet dump. Print the data structure for each facet (i.e., Delaunay region).
    +
    +
    + +
    +

    »qdelaunay +graphics

    +
    + +

    For 2-d and 3-d Delaunay triangulations, Geomview ('qdelaunay G') displays the corresponding convex +hull (a paraboloid).

    + +

    To view a 2-d Delaunay triangulation, use 'qdelaunay GrD2' to drop the last dimension. This +is the same as viewing the hull without perspective (see +Geomview's 'cameras' menu).

    + +

    To view a 3-d Delaunay triangulation, use 'qdelaunay GrD3' to drop the last dimension. You +may see extra edges. These are interior edges that Geomview moves +towards the viewer (see 'lines closer' in Geomview's camera +options). Use option 'Gt' to make +the outer ridges transparent in 3-d. See Delaunay and Voronoi examples.

    + +

    For 2-d Delaunay triangulations, Mathematica ('m') and Maple ('FM') output displays the lower facets of the corresponding convex +hull (a paraboloid).

    + +

    For 2-d, furthest-site Delaunay triangulations, Maple and Mathematica output ('Qu m') displays the upper facets of the corresponding convex +hull (a paraboloid).

    + +
    +

    »qdelaunay +notes

    +
    + +

    You can simplify the Delaunay triangulation by enclosing the input +sites in a large square or cube. This is particularly recommended +for cocircular or cospherical input data. + +

    A non-simplicial Delaunay region indicates nearly cocircular or +cospherical input sites. To avoid non-simplicial regions either triangulate +the output ('Qt') or joggle +the input ('QJ'). Triangulated output +is more accurate than joggled input. Alternatively, use an exact arithmetic code.

    + +

    Delaunay triangulations do not include facets that are +coplanar with the convex hull of the input sites. A facet is +coplanar if the last coefficient of its normal is +nearly zero (see qh_ZEROdelaunay). + +

    See Imprecision issues :: Delaunay triangulations +for a discussion of precision issues. Deleted vertices indicate +highly degenerate input. They are listed in the summary output and +option 'Fs'.

    + +

    To compute the Delaunay triangulation of points on a sphere, +compute their convex hull. If the sphere is the unit sphere at +the origin, the facet normals are the Voronoi vertices of the +input. The points may be restricted to a hemisphere. [S. Fortune] +

    + +

    The 3-d Delaunay triangulation of regular points on a half +spiral (e.g., 'rbox 100 l | qdelaunay') has quadratic size, while the Delaunay triangulation +of random 3-d points is +approximately linear for reasonably sized point sets. + +

    With the Qhull library, you +can use qh_findbestfacet in poly2.c to locate the facet +that contains a point. You should first lift the point to the +paraboloid (i.e., the last coordinate is the sum of the squares +of the point's coordinates -- qh_setdelaunay). Do not use options +'Qbb', 'QbB', +'Qbk:n', or 'QBk:n' since these scale the last +coordinate.

    + +

    If a point is interior to the convex hull of the input set, it +is interior to the adjacent vertices of the Delaunay +triangulation. This is demonstrated by the following pipe for +point 0: + +

    +    qdelaunay <data s FQ QV0 p | qconvex s Qb3:0B3:0 p
    +
    + +

    The first call to qdelaunay returns the neighboring points of +point 0 in the Delaunay triangulation. The second call to qconvex +returns the vertices of the convex hull of these points (after +dropping the lifted coordinate). If point 0 is interior to the +original point set, it is interior to the reduced point set.

    + +
    +

    »qdelaunay conventions

    +
    + +

    The following terminology is used for Delaunay triangulations +in Qhull for dimension d. The underlying structure is the +lower facets of a convex hull in dimension d+1. For +further information, see data +structures and convex hull +conventions.

    +
    +
      +
    • input site - a point in the input (one dimension + lower than a point on the convex hull)
    • +
    • point - a point has d+1 coordinates. The + last coordinate is the sum of the squares of the input + site's coordinates
    • +
    • coplanar point - a coincident + input site or a deleted vertex. Deleted vertices + indicate highly degenerate input.
    • +
    • vertex - a point on the paraboloid. It + corresponds to a unique input site.
    • +
    • point-at-infinity - a point added above the + paraboloid by option 'Qz'
    • +
    • lower facet - a facet corresponding to a + Delaunay region. The last coefficient of its normal is + clearly negative.
    • +
    • upper facet - a facet corresponding to a + furthest-site Delaunay region. The last coefficient of + its normal is clearly positive.
    • +
    • Delaunay region - a + lower facet projected to the input sites
    • +
    • upper Delaunay region - an upper facet projected + to the input sites
    • +
    • non-simplicial facet - more than d + input sites are cocircular or cospherical
    • +
    • good facet - a Delaunay region with optional + restrictions by 'QVn', etc.
    • +
    +
    +
    +

    »qdelaunay options

    + +
    +qdelaunay- compute the Delaunay triangulation
    +    http://www.qhull.org
    +
    +input (stdin):
    +    first lines: dimension and number of points (or vice-versa).
    +    other lines: point coordinates, best if one point per line
    +    comments:    start with a non-numeric character
    +
    +options:
    +    Qt   - triangulated output
    +    QJ   - joggle input instead of merging facets
    +    Qu   - compute furthest-site Delaunay triangulation
    +
    +Qhull control options:
    +    QJn  - randomly joggle input in range [-n,n]
    +    Qs   - search all points for the initial simplex
    +    Qz   - add point-at-infinity to Delaunay triangulation
    +    QGn  - print Delaunay region if visible from point n, -n if not
    +    QVn  - print Delaunay regions that include point n, -n if not
    +
    +Trace options:
    +    T4   - trace at level n, 4=all, 5=mem/gauss, -1= events
    +    Tc   - check frequently during execution
    +    Ts   - print statistics
    +    Tv   - verify result: structure, convexity, and in-circle test
    +    Tz   - send all output to stdout
    +    TFn  - report summary when n or more facets created
    +    TI file - input data from file, no spaces or single quotes
    +    TO file - output results to file, may be enclosed in single quotes
    +    TPn  - turn on tracing when point n added to hull
    +     TMn - turn on tracing at merge n
    +     TWn - trace merge facets when width > n
    +    TVn  - stop qhull after adding point n, -n for before (see TCn)
    +     TCn - stop qhull after building cone for point n (see TVn)
    +
    +Precision options:
    +    Cn   - radius of centrum (roundoff added).  Merge facets if non-convex
    +     An  - cosine of maximum angle.  Merge facets if cosine > n or non-convex
    +           C-0 roundoff, A-0.99/C-0.01 pre-merge, A0.99/C0.01 post-merge
    +    Rn   - randomly perturb computations by a factor of [1-n,1+n]
    +    Wn   - min facet width for outside point (before roundoff)
    +
    +Output formats (may be combined; if none, produces a summary to stdout):
    +    f    - facet dump
    +    G    - Geomview output (see below)
    +    i    - vertices incident to each Delaunay region
    +    m    - Mathematica output (2-d only, lifted to a paraboloid)
    +    o    - OFF format (dim, points, and facets as a paraboloid)
    +    p    - point coordinates (lifted to a paraboloid)
    +    s    - summary (stderr)
    +
    +More formats:
    +    Fa   - area for each Delaunay region
    +    FA   - compute total area for option 's'
    +    Fc   - count plus coincident points for each Delaunay region
    +    Fd   - use cdd format for input (homogeneous with offset first)
    +    FD   - use cdd format for numeric output (offset first)
    +    FF   - facet dump without ridges
    +    FI   - ID of each Delaunay region
    +    Fm   - merge count for each Delaunay region (511 max)
    +    FM   - Maple output (2-d only, lifted to a paraboloid)
    +    Fn   - count plus neighboring region for each Delaunay region
    +    FN   - count plus neighboring region for each point
    +    FO   - options and precision constants
    +    FP   - nearest point and distance for each coincident point
    +    FQ   - command used for qdelaunay
    +    Fs   - summary: #int (8), dimension, #points, tot vertices, tot facets,
    +                    for output: #vertices, #Delaunay regions,
    +                                #coincident points, #non-simplicial regions
    +                    #real (2), max outer plane, min vertex
    +    FS   - sizes:   #int (0)
    +                    #real (2), tot area, 0
    +    Fv   - count plus vertices for each Delaunay region
    +    Fx   - extreme points of Delaunay triangulation (on convex hull)
    +
    +Geomview options (2-d and 3-d)
    +    Ga   - all points as dots
    +     Gp  -  coplanar points and vertices as radii
    +     Gv  -  vertices as spheres
    +    Gi   - inner planes only
    +     Gn  -  no planes
    +     Go  -  outer planes only
    +    Gc     - centrums
    +    Gh   - hyperplane intersections
    +    Gr   - ridges
    +    GDn  - drop dimension n in 3-d and 4-d output
    +    Gt   - transparent outer ridges to view 3-d Delaunay
    +
    +Print options:
    +    PAn  - keep n largest Delaunay regions by area
    +    Pdk:n - drop facet if normal[k] <= n (default 0.0)
    +    PDk:n - drop facet if normal[k] >= n
    +    Pg   - print good Delaunay regions (needs 'QGn' or 'QVn')
    +    PFn  - keep Delaunay regions whose area is at least n
    +    PG   - print neighbors of good regions (needs 'QGn' or 'QVn')
    +    PMn  - keep n Delaunay regions with most merges
    +    Po   - force output.  If error, output neighborhood of facet
    +    Pp   - do not report precision problems
    +
    +    .    - list of all options
    +    -    - one line descriptions of all options
    +
    + + +
    + +

    Up: Home page for Qhull
    +Up: Qhull manual: Table of Contents
    +To: Programs +• Options +• Output +• Formats +• Geomview +• Print +• Qhull +• Precision +• Trace +• Functions
    +To: synopsis +• input • outputs +• controls • graphics +• notes • conventions +• options + +


    + +

    The Geometry Center +Home Page

    + +

    Comments to: qhull@qhull.org +
    +Created: Sept. 25, 1995 --- Last modified: see top

    + + diff --git a/xs/src/qhull/html/qh--4d.gif b/xs/src/qhull/html/qh--4d.gif new file mode 100644 index 0000000000000000000000000000000000000000..08be18c8a5a1c89f07da6abb451283cbf176d5b4 GIT binary patch literal 4372 zcmaJ=X*d)L)E+YoGZYir|o5^xf*3;;M12za_bF~wb( zLco*QTn>lBX0w@0cQ-l)Ypm|-Dv!gFaYW)?l{c3XM5Ugjc{TYnQ#mx27n8~2H2AUW zPk7e)vTA+kZf-2TkEbce)QI4taDtFd=fb^_$KVHWI1e~e3ypO@$)Un&`;o54&vJ-x z5&=fU!w9Z0oFS1&rV|JRI>y=x?ux}LlX09NZnHnbgW>M(&Y&|q$R4a3Z+5khXT=Fk z5|MeG%dFtK;oVpcEM_s6WyP|mQuVBG?iTJWBNpc%#~M$-;Vp?-bYceGBht&>0Z!4O z5RIM42gp=6Ptl4yXowt*d|QAs2m9E>0k0trtf5J)(p37$YPCgU~Shy?OJ zSF8?~Xh|S2lDKHhG2J~bq@0Z{42>~BUu04gXk0H21y197a^M&=8BQfCkmX!lSRNkK zg9I{>NZNCp6i>k8@JbY%E0)_F;MMHUY4k&oNEAORJ(5pi;s;+~pV)MJiQ}CXYr_Dz*A{d*F=UqfdJo|i$roi#oSvM z5l1AUN%q<#cV8wEMMO|3giIPGiQyK*wm$|(9+Pu+(O1BrNz@2MCk)I9MW>Uk_Z)CB zl;2b2<%P#!*esR{8t;fw^YCEMsC%FgPM)HW_dp^ONz6horvr329!DY)@uUO-9#3*1 zBFSW87LBXuMU3ItJIL*ccGah$X*3lajuGssZQ}~~e?f%HVH- zOG2E)+x)*r^JXg_Tv0E)G2LkBH)0u6EXak-w{OZw|1cVTgg#JZTLD>n-F3#V#l*0y zW&Q!ss*|ItTt~fA`ZKyd%6iqmsaifU-Mpi7^_7Cs+WGzm_ddSq2)NsDH;i>?x@_!R z?7fODqYvcRuU{Pg=>9r?i(+JxU_oF_jnqBePy1y(AbOUl(bw+Xz4htO(%qacwZMwa zf5seU?w|2uy_$W;3;&v{<#=vmF3_$&>+$i^b>ma_g%>x@1XuI=zW*5H0-culddNTwq5xi&ZEb@LNLneqiX_g=vYb>jLbLS*)D z>UqnDj2E*xk8KwKdY@6!CSN)(sPgSzpw%zOBXx6RSle$-n0}hV4plNDN<|kXk3I;A zN%+YJ-5;K4qY&upbP9R3EesEb2!<%cL4A2Zem(R5ZlsHbf~mNec#Yh1u?z*xk%P^xe9NY!oLDRKbm|OSX;Ea z8KA(Zn$)n!^ui@TmJC1AXDFxW5TLQgdFe|WHY&rE8EGrZg2`8-FbI^DgjqzN)`_NP z2ZGbUx|TY53CRE*3D?1ok;5tvrJO+OKl?>G{<2^$c81nn@lWWtYmOqcB88XjldNzY z3GXv#ZS0|+gN3?EX>E;64B}uqXH&IPLI1^oC`mx*qoD?~#GXoy_E=uSYp&M$Ua3H2 zLD|r0XT;ngC}8L%c;Z=u^>P;UFd;GlvD@h>etH8d`xeYGb1AC3|vv_!H!1kt0qX~rbSAZt+U;-fw1862;5CIz&>U+ohqD#C_M_n zn4B8r7trcLhJt(IHb!N#Yb$}{wRo`=S-?$y)rgQ}xJN#)Vr;XS^4x$D+Kr7`L}@sT zSDOJiw3=uS&1%s>j7d4r1AU=^{wS1UbL6;vjAiS#mjnQ)&PblPkV^CD#ROzeluvYu z&sdVhQ|Y94#bbOw5K3cZkZ}IN(K8|Qi^n`re-ARPJ8q1kM`50WPdH|>Z++1k`yUSV zl`EnOH}{MBonV+uP}ftbDm$-Gifui!yH)m+UJ#E*6agfc9--s@z64o9syrC? z5VuQm%H9{B+`0TLT=B8=`$5XS>_v4muyLdha6U;`f{Ng&REib}bE-lb59%*Ue^74O z)R@dF8)b_*nFyTv zSiz5{DnR*6uOy&^(nM$IqMax8#C^*e(|fVxwiW*z#6|JGfYJ-+ycPb4D&rHN`p=6;=KxPsyD9G_AB`c7I*+@};FaWFq|h(NZ=j+F<9< z@=5UF;|YsKx4^&`2(Y;EJ2}QJWZN}_lGE1B`Ll=gOB=HQk!6${=v{c34)Am(l4XLf|@S6(O`2Z=z8?{(jYU1eW4>lN?JAUjB4Ee9|09giCih!SXx$0 zh{Rog@WMz&<@Wj8>scOMG7}w{N*>vMk{POjSTBK0F<&xHlzp)& zNKRhnV_b~CN=B3d*nTC&Q8i(I6Rb`IosmlYFv)Xly3&~!vCskLM()~iw|U|N;t+(L zxt$5cAt3+74ML+JFa1G|9T4+@ZX}_Akh|4Qr7dvI;y%RyI ze6dj-Mc=xMHdTv@0`u20uAEX!7(qb8)DlKV#553Q{1M1V_a)5;`mxf?M%(D{=91Wv z?C@3b;~mCMaOk0k(y~&h2=0CvW{k)C3b?-iz6f*D-gRSo)N7hg#Ztu$7Z8Gsbi1}f(atE|e%BU9H< z0~Kq{FwdWbR6)VrWXM}P@tL0mq@M}b;h@Ey%HLC!bkF=%e8G5Vo+l#jcc#pWFX-hr z(D&HtnV!@@h+rW!f7dg=1tDNH7k&=SpcF*uOE>WvI8<}JJcSSK6_)0&mYQ?Q3-~f|C`ek5aaCTO{a{^?!PUIjtEyJ@ z=4((7ZJ;nIg#(pn@P(4s>K_IJoljn^@dPr|>&!;Pz82Nh2*AF`%vgLwLUaS-l+>R+ z#+zkzXLlNcw(IKO1gk^yGzo&;(MG}Kl;D8i`dLnGz(5mX>iRFW;^EjD;M*FB@`~<( zDw04(OS}56f_T=BSPt~^RYJ=(X#Tpk)V>F`s_IQ2W1Gv@#I=M18}*jDwd;rDTFjek zl?Pjj2~FZhuBRLU>$cR_Kw{<8NC{kX2@c#&Em6lc{^@3^|>BGkzzF#;K?` zYztE>#UqWZg$*@=wrOPMp>@gk$Qz=T65kQAvaH4>b;;H29Lim{=fa6=lALNJbw=_cUzmt7(fhQ6#xi5jt}}RiN!%N zL-aKxo!KSvB)lfGf~A8)yU^i8UoG7PYn=qLsS()44vtR1`UL3(9K>kZm{~;XAz|R) za6?NR1a4x?DWeu#FZbaZ)ZMz`%|Y!hb+WgH@|%l>9zO8Tw%IGNbQe%Ddvp;P?A{ZW zZYAOMcdud5AQK;iR+O`q1K9DHpN%n8%iJ_1!-BB~m3}ydorXilV-T@cE;)ft0)In0 zxCIx<$gy|fdxcfeXP-XVTSenmxH8j;zM%$=6j)evq*N+RPD=do<45PMj_@qP_V#vA znBmseRv_2l^XJc-A2)3&P-ZfYh)1uluY*`Lx{nT;4AnL@?db0gstBR_f%&H3fBpL9$wL18`7=D5 z0O#P`1ukE|eswDhgLvUTefs2|YHp+t3G(+NJ3-#Pd*^^gdz8n3{AsRP9&}F~hYTN* z6V}9E7iR*o4JBG4U>?ORoTqkq`Gugm;J`XIJc^)Yk43^k{+9z87g@VvjHafiOi5(+ z@8-_7aD5%E+Qvr4K4UH?2ySY^4m0fS?L{Hs_VzHl6bFMC+=D+K?m8dobljI<218(R zfd6yy|Kra90OtSVzh3|-Ndfzmbv?zR{%j=(jyoc*8O#G2c~^Pfs2wVRk>VFdZqz+E zjiTr4dfluaJ&Or!;f~&H7%S5~C7hQ(YiyAcF86)Mut~WJ$l7no&`2p%ISqQ>BJ>Y( z;2DwO_^vbc3^2Q;6)1mb(QrbeS-*^BpZ=xAK26wpSWB+c=;W3CW5G@ZUWq75z2#5v;)M2=m`s!MfK zsj{lZT=VVV)d5PY+VskEZoc^N+?y{4o*laKn`YT?3i?5n>@rGD}rGcz2AKZE-13fT)47$WV%UjYv(QGUB;`@Ul z^$lMZy+*Q)MNvyd3bvx6>?hS8?kOm#Nfo!1cj#}+roP&`+=TKnE(S7;sV^*n9W}(w zzwTZ_e7O-T7pEDuHICgWy?F}G6TOk}dyZ+u#wk@r8JuY1Wt&tW0zR;LD|b%qQ9hM< zW&|5kduF9Zq2$40q>u0NYSdYkkE5t;a!S5TUhS#eY>R-El{&$(>B9PR$7`uTwer#y zS+!S3tHY!$1{|H5!rmOc)`bJe$kAdhHprf$9GyFS)A{L(k|+#6;d=7@$^5pf8zT*2 zC|&k?Vc(+o>J<>#q2Nq!{B$2;$yVk`8C_4Ows63>=NfRF;!s>XsnD~Z^-X|)shwfO zuK|*SCd{?+^!|{SFBm`%3Ad#={9dUEO5B&s|0HbWQK+t(EdQyb==m*&lR%*F!U6fA z?kt7cgsYQuzdphbq8hp|{VggeDB@6`?-B9MM((-Y(@k@Y31qg?s|as3K}EdR9o7Q@ z?(Wi*A_T7jy}NP17!{q5g_KVf0)yOCZvpgJ)x6u5$#omI+*`Bha}n7u3IVb7q84e} zeA12KyJqmx(dQiqGcTjGr`58Whh8Bq+cHmAioHED&6R;@U(xrKCo{l4xV>WL&DYl! z;&Sy9QXhiLw_EB4Rw`4n+$?TAV%>aQyYok41#kQ3M{lQZvmX}3%n~}KWBR44ZTEXe zwfDS{)q3yCYn7>+cBz*#Ej;BW9XI4E1&#W_ZJ#-d!avzrJ0kLs6w~LX>U5Q2vrQr8 zo|U1RA2y8^ylt4}_tbO3S)2?6Z2E-Bl}oy#ButNwGF4s+3O3Q#PAib9xhoeGa^w!S zB_C}UDrSDkvmT)u(<91uv<3jywoXz@;ZgsCwY8t~>A9D49HOd3lwTGY`LC(I9s( zX~hBZ0}c?6zpR_PeC(0ok-ChCt@nUio`NIHETToxf+1&EXY58#S^;tQ_@Bln+}- zE%;3?a%QE$$h~R;O}9n-evhcAKoFh3(1Et&*^O~pmelTGUa5||%ZE2C!tgjR0{c6V za84kzk7}aul{FSS6>tHG8Cvh;IkY>udRz&iYx{x$36m`vlTyr}%^0Z2Yl>gB9Mg!4 z$9mEZ-q7(r$staNR6__^g=l5I)_roGl{xh?bT8jRkIK;d(5I*`kM(px1TgLqiy`g$ z6;l$`^p-^(zJH!pJu$B^YNobxZcyDF7f_ns(d5{@tgRk*Q%;1{S_+|g0Y$mq&HX!C zOZa#tDxWgbS8KDcPv_vI;-%bk_2}wlkDA-NSQYDF=k~v`E(1Z1l1SKj(b|sY7Nx5k zGUC;dVNy%~tay8-4eht8y(}gXDr(evbw?Fu2szlk^ZyM-jk&ALP`Hx?pWqLZ$YW2P zM*>7LX|b*eJRqH#2r%mCBYFc>hV47SCFiB)GMLb88&-6C#kd98ekUh(D_G0qi$vu$ zv~7R?fa<40UbMc>vyo20<@`fYU0RgIdcJ1?gCcxCOhzhiwpC~;6nH(53nG`KV5h=MlwQG~)< z>wf<0){winV3ls7Aib|<1M!Dy!v(Z*3@nGASfUuIRCoU7Oj(TQz#slwN6NNBS> zCi1MFNCdh)BAf45m50u#MKBJErDq!!6#KT$^^B5Sb>Ar#%-%yQugn3;hYq~5>gt26 z?Q4r|Fk8lFQaV2Sg*h}d7KNdqv8WNM3?u0kuSIQ-$nz*vU!Av{Mc`)HWcR=x4=JV{q^ zrg3nvr@#HuVCGUJRA<-{PY!Y2vA?}@0O*3Bz#BTfiHB6e35+C|yGR!I4{B1F!&d}{ zq6De5k=Dn9%8#p8(%y=iv>U6H)*XsHXZ}ICjSQ3x-g%GYEslmobcJ{%^y~>iX(}>3 z!Pje6A><#6%EjGkzY%VaG7LW}H$DF5T0!05+72vMXnm*VvT#GK1ogEV&*fAq=~HFX}BO*SovC`M*hK0sv2{Frg%dFD60w6;7WvA4_WRe-Ip}5 aB{fPSgP!@PkGDE|e!fd8+eHNc9R366^3h-b literal 0 HcmV?d00001 diff --git a/xs/src/qhull/html/qh--dt.gif b/xs/src/qhull/html/qh--dt.gif new file mode 100644 index 0000000000000000000000000000000000000000..b6d4e26727ce648be6ed6d4c8e5a49a367064f20 GIT binary patch literal 3772 zcma)*Ygm#8!^dwx~2`C@Y_>Haw=wijFhu-GjD6?b({Gt=gW==iT$|{rLXA{jcl)>33bfER_be6n`lCsG?l^n>{xg{!DVx?bDx2JHgyPOmtBl)Y+-C{Dl6hZ7H zvB-ZHMUh8VUXo;vOdL!*;T${=l9Eak?2DE(N;7zLU5Zy)WqQV2&e;@hR&KUh z4VA?)^VQDPN}70AY(_be+(^llP(8M}h;|Yg3W`?Zk&{DAk6@SZJ!1_}M0Z52DOR{g z7-x)2&?ksn#Bqi=QHw~tOPtu8C~c53nY83>$?{rxN_C1tqsVpVDT|e=&8p18%q(?Q zc3yT)PL7nxQ5DEa;^|Q(ZtjL{|Ctb^SK8ALI0iqZ2ND=k`&;svj?m4_rG$m+bXOIe%*LFgyE`$EK-kUw(+q^-H~T&2Q-_yz|GQEV0z5 z`dy>Fr3!iLAV%KBLU0`vM=&k)&yUCGmr$_Qt*Mxze}JariK5*5rr8NB{ct(SdWUfbPQn3uu>9bpa0PZGLwK zY66o(b`?E7e-*oe3^``=EMw&inIwh(sCaxSVdFpN+>dh`6)>>zU!@i}>L>N`+o+HJ z{OnC|53*!X@qKDY`K&Z7qx?rrKithD2zA0R5q#-h)z)+K-kvqmbrkKmZBXH{a`@4S<|cAL-*Wrmm=;pSfj%L&u|>j5nH?P-CF$* zu9l)0MhJV_Z4t-A2{KkIymV{pu!_B4uT}QWjr&he>_iSB`pWO-yqJr8Bw8~9g+HMl zdu;|5=e$@A1E$*2JFnHPD3-bvky|mvI$AQUI+;zp`TJhFMkhYt^*_+Fg66Ww%d04F zD{r>{QN@&#paB2vOsDleW1ilk6H5>q&!H!ZaLtXTK`n`S0U7dW67~^o#&zZ(r|^XU zMlr}|(EcM2UZh8X?33ypKJiCzOw0LbDb1U57dWOUif*MZ%P-8+M@IUShoAq-rC*#p zeI)(T!A8U3B)$BJpv5znF!bgWlzbuS8 zlyj$LS*Oo}yf3cYIHRno`T6RXPn)5@_G4m2itzL4Yjp1~_~GQHSDHj;@N6xtXF2gF z{!-@lmvdKt9JZuqk1s%&jd{v@_46S0a7EI!Vf=dPvdxqp88O)~knF!p?K66}?u(+| zBMBFt$Y?uqreCwq*?d%imS5}V{aNjjH-WH@xJwI#6xe=(Ob;mBbf5h-as4RG)XAII zblrsQBTiu0VOA9wFxXph{fBedbCU~87D+qvDP6pXDVgwi`Q1wUKljiiOBt74P(2(Q z&N7^Jovk%zN$`HhF>y6?<$qF1UKZbW#}zGtlw}Q0HyvtGwDtY|)bXGQCv~)FlY$$p zhT>s3>PBEE`KDMMB=2> z?vRDZ?9Fr>6AZjuri}TlG3tDuBu0J*!ejZZ4zA$0gg!goIb4ir^{5znei81^)WCMA z^f#(`h^*e*q%dd!O0NIJ$8qt1%jBuG2U+;Ir>mV7TqQe&zJDv-f@Z3uZdTYq2c{Lu zT%Yw;K;YS{bkczBv`AOgjd!bqY5A19dkmxyNX{u^C=TOBOvynf!{;wa1%T#YQ~Dr3B6B~#8BWO%F&GsHu&rl}ZL;Wk7EA*Ihq>YY65@JVr!Zw*4X%ph8?T7B1IieKz~4Xc!k zUGH=~E@~m*&r{?zh=04T6h>XDw<997V|Vu@GH6(RP4o3M&5L*+Ou!paecbwiGzSSRoE@N*mbFQe2%U)9|!{0PCguR+=8$Dns|sj*#*uJH`cL5Y6);wU=%ge+YC!< zNdISky3hBNK6oR?MVe+D0nG!PbamFBHQGHBxZkNoMD`k@QYoAigO&dIx5i+d{@xS2 z4R+b>uTj52n*;M`^Wu|?)(wA<*B;$?K~jo?ovU1--e>b8qxq;9Q0HP!m~&pYc=a_t0{V(6LFK z?Y^Fys%#9}F7?!jRr3Mg9e*rY-$H?Xsy$rxj7RVglJiH>)}A*ar}5wDCr*!}XtrI5 zIvQF)jvpc_rC09$g~KpWYd9m%jje_yh?~V1Iv9QiD(wz z@SBZYoK(PA8~Xk|e(WcIx8<8SAKWebtN!5<8zJ~WXK?vunw(9MOe5dLDfM*t5+q`% z%@CSKduSE=zjcezAr!oeBB~QZ>gx>1mU5V9@AOUOpSQqo)>gNw!0VDVBu`q2!YsG0 zrpSrZd+z8PXxRi|11SlF?uF2olk7&;b#0yL~=(<3-Vs z6veOW?E9((hPVD(|Nb#U0QbpvM)?eXx7DD89?@TZw#fX04#2qf-^@P)49*l#>0$jP z?P()gRBQ;Su+w&eyF(_%a5X(^hQKd6^U(%asKt&N^=}JpsXH+LRk6E&?~Lo08|$rC znl@f~gI63r7~k(?_67&l4dbQ!AB`G1@ZFfqk5h{0U-|k~+AVV3gFA!DhcsN_1 z8HEyy)F0Kq;PaF`DNG8y#L;EDFOBT!SC8C_*Vxt}r!c-toQj)D)M z^IXgV7p{dIl5D6d5x7O)`sx}`F^1bMa!A%fBldW1E22UUO{l<#K?s@+)vlxat*UXo z<4y=*+mIEy*4<{A>w^xC7$fv8THt1*2-skYWMeoN5dvU_EQqsu$E$QiydC*21aOri9(owFbe|cBTIb?8wyn&DlecVnDrM`Ain*8zZiRVEULv9@!CWf(RK3d+8YvVc!7I_3e$qXgo-Lj z3~av{fvY-vV@KufI!G{PYAMHn5C2w<8QcjD+k}F*-yhRq(h4vUkO^Cgi5N2uj^*Y@ z6V3zTmb>s_?jdK_@_sI2F!Dok|6y_)uit){Aj2%^yY-8N33E@{BHUNhurGiZqeq;! b)E_=_r0*X`jy*hb;`bvK_r5-N2#Eb(@t%X< literal 0 HcmV?d00001 diff --git a/xs/src/qhull/html/qh--geom.gif b/xs/src/qhull/html/qh--geom.gif new file mode 100644 index 0000000000000000000000000000000000000000..9c70b54991a4574015671ec0e0b29102c5346737 GIT binary patch literal 318 zcmV-E0m1%9Nk%v~VJHA70P+9;0002<^YcthOuxUs=H})A0000000000EC2ui04M+` z000C27`oj4Fv>~UxH#+0`w)sJjvN?VLaMH8>$0bbk~9~GUk&U)eV;iGk3cN)M86ql zS8O_aon%l0a9W?X4^FVDUX5PkM#{~EpS)ociXof7VKug`1(pYAaGaPYum^4zeSSk} zgKU9=d{t?FSBP~=icgMTeiwRuBG0`N_cSHMx7af z$;`^-=2P!_)XjVv?iiNh(qpNX^edGGoIqW^3IgjO2Mv;dBgE0zSHlOyiWQ|)*!T

    !gXhA{d6_OPvj%08+$D~O@p^*epL4)B~%AQ;?rVW!NB|J`5yolH)K2<;<@OU*aMr5B{Nu$Z5LzHA9JUO$7OB6Os z6{<*#JT*JY6`VQ=Z;wc# z^M!-6BCrgGwlEgAXHNv5Ul&2f@mv@g1Y$>QA|W)OKPe|L435TwNlZ1IO4kVp;Sw1V z?YSQ&MkPl}gnSv@RIqS9E;=g z4KSn^1nxs12D4cEIP?QN7AbCvioOja942gB_q zQ9{+>yutuU36XF-AvqmY8W$HwhiGGCV-Mrvm2sT@q+F>~S|Z5ZM=K3M`!{EIWeLLJ zD8Id_VqLP3%jOzU8Cs>FO?*s(n8&M549tCt)(lv)E1h-enY6<`}hB1t~Bfe^+4 z{GVC;kADaNp#H+Yz5uY^0i@Z0xmS884A#y_)y$Q?+YOEZ{BG`0|5UR_X!ettp)aP} zAkj@=-tgJkPK0bgH9LH6{sb<4uA3BdDsYdj8=~K@`8+Q(FLOWm5g>DptC_>kc2d;0>s?vRq@=|9~^X4`|Z<{Du z2`QOBdn5SXH0);M^+gMCFFdW`8zz_DvW#7uxAln-Zq*O#rb_HRw={wgMp-^pR{0Y8 z{>i2Dpvw49EBigMUK(#^wDMZ!J$>36w+j<71&{ncZuUh_H^YIqE$;3Sf9u(tvvgVa z#(8xb__~X;b8fWfQxfpk@A7O+B#OrHt)+mAUN;-4KZ$;tA6q>8I8q+;OQR-L{&AvI zgfE=b&ZniVbO<8vtV}a2mo92VxgU(sIHl z6nB@oXbm<*0aXi3^-hSLgezz`&%9qditU5g=N%h;)3U->l>rOI3=q(<W5xH6^8Nsrz$(ttA<{dGYLt zox6^(tbu|&sn|ZF>GJ1`dP1RO&W2E1d<>B@Ra~mhVsnoR&~hoeVieu zF~hniYU7D};)!^>U0q{zvMJ|}+ZaoE@sSg2<8(6_iHOS>uwV3k_YKuUiubu$(+RZh zab$FQ3|Vt?#?9d6Y;%~>bn57u`Y6jAGJ|touwML5NS=kAdArLfcjAD>se@DQc~3el ztQ%*#n5doe{IBoxbn2}KmXk$x#3X%*RUjavgLMQ)THO57`7PkU@LQ%MXJ@5+8Z_aa zltnjx+<_Y(THVoS+xg(>j-B4>&kz3K5{T)` zGLXILRt~gBrlvfdpym8zQgf&M*?SoAMrdHL%lQXt%k|!dKR*>X?Z^N$TfTgJVbENQ zjPo=5vf9q(Zs8#pmwKnUB3s0Uw8|6ACRQvrJ5=_&Fk9HLn?p`7p?II*+}iiui0Ie! zcWz@dpZ{Y!qSm4Ko#BT37QkB0NHMpC;t`Xci5?Cr+E6QjX-S~0nN!wGs!BGLptp@2 z%H0W5i@+Bq@gb%Jr=#ul8S8(v0PIV^FS;7ImI3d2uRr)!!&eS&{OLh&k+`($9ppxo zaJ)r=+;nZ|f&__-bDExE*VakJ$`yTG=qesOK?LCI>8Ad`wF=}=olQ#hom9&av$OB@ z_-Vz0JK;WXVUPud`OTB;(m$IVWxz<&PF^j&pzn8Dp1*hD35$PswzM0kqwgE*_~z?X zz8RB|&q(GuU7N{Ro@uBT$N_3?_l>Ek4lmNzIT<_-t+AZLZ8{;i zbkI3UW=P5+&;c^ynm+T&qfBqQ|3bRk2yH3|r#D0GGo0dty$;w#Zt%0K_K?)QHVH`^ zI62wt@%`ci_U1c##U$XgCCJJ(un;)RS39y<14+KmZP`TE0r+90RS4)zjd?Y25Rs#( zE~+{E`=bke#Bt>pUXR2Zy_d~F!H@-{N&i6DrOSmJe@pMB&SJpe{%dta`NXoUH|!Fd zIuXv0Hq74oa8*Fdr4wit>aJsh6&4SFu^Du%`SJMnRmpBsYJ6}zm#y8yDp1K_Qeth+l+s2KZPX&j~R7$#Ve%$ia z*A*sUb@1N-w`@8#E8J0Qw&_Ga8W&gR4IXna$R};xCVfSA8l1k`ncgyU~py|L4Z?kb!BQt zUU-FaDJ4J8&dx_eIBTFsKr=CWW>;%rWo48tSSmtwh$%R7Igf-!U|3``QBq}=L~3C? z86_UQyuW6TW<)@6JwiT5Nh)KDV@_*dC@Ln{*VbM?L>*2tL4`gWOfW)aC`CX?Z>36Z zrAZhzEleLqgNJoOZ7Y$EkUB(FG7BkIR3VFgUlku9NrNFqJ~=rQJv$#@M<638H6m_T zU4&?Crl+AnP9R~3UHts~`1trQL?T6jG-i%vb7~x6MNmdtNh&cXQdV{#DrQEGLXCq) zNli>NOe#7bBVRy7OEN8HZEsDFE3&b(RXG_GD+9z^TS8AdBPSUuRcu2xGbnCnDmP{#E@CZYlr1u79YGT_kAz2#ghpUkWk^L- zNIG6lkSkLhX=OiPIx|{JGEG|+T}*`^sjR8q-Q6@?B0DFBO>19hNjp|c5a)l{AOd(QcPd<4?e0NG(d|FH>S}8Cz zA3!QA5+8+ziy1dAE>SW@NljE>DKirwID$AgfHz{1GbK77|Ns9eFIOy0Gihu@XI>Y0 zV_^05^`4lSRy#OALO@_jPw(&VGC@2=cp&NN=~X}(Hy9u^PD@r@Qsw34S593VCr&?6 zIN{;pZ>3315IIbRAXPIuQ%z4)R7NNtGGWQVs6Wf zTYf43#iT1PW`$R_ga-XN^k{6i3iZe(JO5}WpFWi8AYQ^u#%{F28xOPqp9HJGHDg$&!k zw2YgHYy*xK2~?WNI_rRNga_Y#3+}k%ihD#Fy3A5Q0c;qPN3pq_gM}S8Y?A6J?lj_3 zgqnQwMn2hYo5{A}WLmD3;UY}#5fXs2?!yJlqRtZMNMXl0E~L7P94fB#j5ExbK*}2= zuv4kF2`FLi!YrHH4ge6xi~t)&^pJ(dE>zQMN*#=2!W8}jAWy*B!g0jPNGojaA7RAo z^tyH6yo&)VCG+zX6i~2c0C0#Mk6!WAG zO$4py8n*1vukZ%~z4N}i?qJ{&eDE@yBtzqGK+e4D2?zl_^zB@Nz4f~HzQp#x5C*j^H)2Z0l{!oX@b2nn*cD_zT4#@3mq&R0!fksk-ecE5R5<$P9XvmZb}yZ z-KYZ#t%raY#&8$qAjvblfI3o40fM5qg)SD5w7!Wg9Gvsq!)Ead4ixTiU);s~K+*^= zjN=n;7zgv5aSM6qLIwuITon@rk8V&;i|p!R7;!d&lDNYQ@OZ-~n74{s9D^3OC`K3( zHb=1~;tzO)8V|e}I-DhuBv+7vCf<+)QGnnaw7>^G!qEgWR7s9xOW4aIS%5l#YL8zW zBS@A(4QIT;8A~X`6D}aiClH_sm#|#stD4^1cM-alnl zvK0cC@WK+H+ES^CWE)ilLQc1u)tqj#n|l(05&+PJF6e*^@X!DYxX}erxb?A4XvQD- z5CtjFm8EtK$r!wl488XCv-r4YJ~^d?OBD97X0XR%7u#6Kwp9y?Q2qxn+HeV6T$UFl zL4_B#uz|hq6|}Tyf<-fGTEmhd5p&=M3s%dG7g)fxYh{BXnsEkToT0L}ElD}3FjL=Z z^|zuVB?EwJ+!v@qxyog(bD=v6MI<5^z_2cDZ3|W0lB5#m;K38T%U_&6p%_iTMk2`2 z1w6EXxywawBI4lMS=izQZK$qwfl&q$(AB;pS;HFaP)}ZF)5D_Veg8!bTb ziBp{7b};y{C`g1NERc>foT0|)Xu}^?+ENUQWD8R$MTdv{VXhXV3K~$cic`E~ZY&tG zGLFNIZH(hHR2ZstCImDxE!XuK)WEQVj#fhlH{$t^x2mw@i$~BI|j$4|B zlKg-Nt{^g)Kl}tUAi@)qXhE5kY-UZUxvg-RZaUul<~(eIrFCckNrn tio%^!)G! zn&62?Ji-!#4mCI6p^dgm_q}kY@|4fOha03I1zQ+GNh(q4UGDkT*d%~OJss#Xiy9tq zU~79Jtm+!4`o^2s#6H%f#BW#{(;&vjrd#ojNqG9aO>S~Hw6Inb*g_F;FgBxCoen-^ z!5U*Y2`YG?U(o&*5Cka3JMLi$ULP32%v}YyV+`)#GkT=93PKZYY8sF$W zf+b)}-H+Q`;OX!|x>HgK9zgWuHFZT39=`H>yra{qRqg{GU=MrDVGeFEhkN0BbB_z0 z571Hx5vUM~P=2?jD`>*Ji+&GZJi;7=@Pyhs?dcsH!VrdtaKNojcL}4R11z~hD3}3s zp$mZQD~G}(2w(;?zysKDpoA}S5Q9XRp$&8Ixa5Hmj1lZy>s)8XezSVdu>=0-!Wf0< zM=yG9@WTqLu!RMxK6yR>p#&|VbttHiXKV%+1ox;w?jf#w-48+uqv(U^h2IBZXrmDP zCc+if9O`@QANS z34>q@W)KDBfCG!*4SRqF(x?V7fEgQr45HYJ=s*B7aEdDz4%k2mUl0RuKnTpJ0@2_N z-jI%qKo0CEjUF(4ZGj4QfCbYa2?ubE^>6@o5C+0nbnn0b3@`wLpbg}(2nI=z=|~RI zD2+!zJcJ<)m4F21dS%DU_So37znc@G%hA00S?H4#)tLl5h?iumzJq z03j)oZuyo6Sp-`d3zQ&oUnvmVAeLAVlgNM!=ztDlAPJ5T4R8sU(SQbi;0T=%3vNIM zcZm>r`I3E!40>P+{m>7R5CnUm288LBhpCuB&m + + + +Qhull code + + + + + +

    Up: Home page for Qhull +
    +Up: Qhull manual: Table of +Contents
    +To: Programs +• Options +• Output +• Formats +• Geomview +• Print +• Qhull +• Precision +• Trace +• Functions
    +To: Qhull code: Table of Contents +(please wait while loading)
    +Dn: Qhull functions, macros, and data +structures +

    + +
    + +

    [4-d cube] Qhull code

    + +

    This section discusses the code for Qhull.

    + +

    Copyright © 1995-2015 C.B. Barber

    + +
    + +

    »Qhull code: Table of +Contents

    + + + +
    + +

    »Reentrant Qhull

    + +

    Qhull-2015 introduces reentrant Qhull (libqhull_r). Reentrant Qhull uses a qhT* argument instead of global data structures. +The qhT* pointer is the first argument to most Qhull routines. It allows multiple instances of Qhull to run at the same time. +It simplifies the C++ interface to Qhull. + +

    New code should be written with libqhull_r. Existing users of libqhull should consider converting to libqhull_r. +Although libqhull will be supported indefinitely, improvements may not be implemented. +Reentrant qhull is 1-2% slower than non-reentrant qhull. + +

    Note: Reentrant Qhull is not thread safe. Do not invoke Qhull routines with the same qhT* pointer from multiple threads. + +

    »How to convert code to reentrant Qhull

    + +

    C++ users need to convert to libqhull_r. +The new C++ interface does a better, but not perfect, job of hiding Qhull's C data structures. +The previous C++ interface was unusual due to Qhull's global data structures. + +

    All other users should consider converting to libqhull_r. The conversion is straight forward. +The original conversion of libqhull to libqhull_r required thousands of changes, mostly global +search and replace. The first run of Qhull (unix_r.c) produced the same +output, and nearly the same log files, as the original (unix.c). + +

    Suggestions to help with conversion. +

      +
    • Compare qconvex_r.c with qconvex.c. Define a qhT object and a pointer it. The qhT* pointer is the first argument to most Qhull functions. +Clear qh_qh-<NOerrext before calling qh_initflags(). Invoke QHULL_LIB_CHECK to check for a compatible Qhull library. +
    • Compare user_eg2_r.c with user_eg2.c +
    • Compare user_eg_r.c with user_eg.c. If you use qhT before invoking qh_init_A, call qh_zero() to clear the qhT object. +user_eg_r.c includes multiple Qhull runs. +
    • Review user_eg3_r.cpp. As with the other programs, invoke QHULL_LIB_CHECK. +Simple C++ programs should compile as is. +
    • Compare QhullFacet.cpp with the same file in Qhull-2012.1. UsingLibQhull was replaced with the macro QH_TRY_() and 'qh_qh-<NOerrext= true'. +
    • For detailed notes on libqhull_r, see "libqhull_r (reentrant Qhull)" and "Source code changes for libqhull_r" in Changes.txt. +
    • For detailed notes on libqhullcpp, see "C++ interface" and following sections in Changes.txt. +
    • For regexps and conversion notes, see README_r.txt (unedited). +
    + +

    »Qhull on 64-bit computers

    + +

    Qhull compiles for 64-bit hosts. Since the size of a pointer on a 64-bit host is double the size on a 32-bit host, +memory consumption increases about 50% for simplicial facets and up-to 100% for non-simplicial facets. + +

    You can check memory consumption with option Ts. It includes the size of +each data structure: +

      +
    • 32-bit -- merge 24 ridge 20 vertex 28 facet 88 normal 24 ridge vertices 16 facet vertices or neighbors 20 +
    • 64-bit -- merge 32 ridge 32 vertex 48 facet 120 normal 32 ridge vertices 40 facet vertices or neighbors 48 +
    + +

    For Qhull 2015, the maximum identifier for ridges, vertices, and facets was increased +from 24-bits to 32-bits. This allows for larger convex hulls, but may increase the size of +the corresponding data structures. The sizes for Qhull 2012.1 were +

      +
    • 32-bit -- merge 24 ridge 16 vertex 24 facet 88 +
    • 64-bit -- merge 32 ridge 32 vertex 40 facet 120 +
    + +

    »Calling Qhull from +C++ programs

    + +

    Qhull 2015 uses reentrant Qhull for its C++ interface. If you used +the C++ interface from qhull 2012.1, you may need to adjust how you initialize and use +the Qhull classes. See How to convert code to reentrant Qhull. + +

    +Qhull's C++ interface allows you to explore the results of running Qhull. +It provides access to Qhull's data structures. +Most of the classes derive from the corresponding qhull data structure. +For example, QhullFacet is an instance of Qhull's facetT. +

    + +

    You can retain most of the data in Qhull and use the C++ interface to explore its results. +Each object contains a reference the Qhull's data structure (via QhullQh), making the C++ representation less memory efficient. +

    + +

    Besides using the C++ interface, you can also use libqhull_r directly. For example, +the FOREACHfacet_(...) macro will visit each facet in turn. +

    + +

    The C++ interface to Qhull is incomplete. You may need to extend the interface. +If so, you will need to understand Qhull's data structures and read the code. + +Example (c.f., user_eg3 eg-100). It prints the facets generated by Qhull. + +

    +    RboxPoints rbox;
    +    rbox.appendRandomPoints("100");
    +    Qhull qhull;
    +    qhull.runQhull("", rbox);
    +    QhullFacetList facets(qhull);
    +    cout<< facets;
    +
    + +

    +The C++ iterface for RboxPoints redefines the fprintf() calls +in rboxlib.c. Instead of writing its output to stdout, RboxPoints appends +the output to a std::vector. +The same technique may be used for calling Qhull from C++. +

    +
    • +Run Qhull with option 'Ta' to annotate the +output with qh_fprintf() identifiers. +
    • +Redefine qh_fprintf() for these identifiers. +
    • +See RboxPoints.cpp for an example. +
    +

    +Since the C++ interface uses reentrant Qhull, multiple threads may run Qhull at the same time. Each thread is +one run of Qhull. +

    + +

    +Do not have two threads accessing the same Qhull instance. Qhull is not thread-safe. +

    + +

    »CoordinateIterator

    +

    +A CoordinateIterator or ConstCoordinateIterator [RboxPoints.cpp] is a std::vector<realT>::iterator for Rbox and Qhull coordinates. +It is the result type of RboxPoints.coordinates(). +

    + +

    Qhull does not use CoordinateIterator for its data structures. A point in Qhull is an array of reals instead of a std::vector. +See QhullPoint. +

    + +

    »Qhull

    +

    +Qhull is the top-level class for running Qhull. +It initializes Qhull, runs the computation, and records errors. +It provides access to the global data structure QhullQh, +Qhull's facets, and vertices. +

    + +

    »QhullError

    +

    +QhullError is derived from std::exception. It reports errors from Qhull and captures the output to stderr. +

    + +

    +If error handling is not set up, Qhull exits with a code from 1 to 5. The codes are defined by +qh_ERR* in libqhull_r.h. The exit is via qh_exit() in usermem_r.c. +The C++ interface does not report the +captured output in QhullError. Call Qhull::setErrorStream to send output to cerr instead. +

    + +

    »QhullFacet

    +

    +A QhullFacet is a facet of the convex hull, a region of the Delaunay triangulation, a vertex of a Voronoi diagram, +or an intersection of the halfspace intersection about a point. +A QhullFacet has a set of QhullVertex, a set of QhullRidge, and +a set of neighboring QhullFacets. +

    + +

    »QhullFacetList

    +

    +A QhullFacetList is a linked list of QhullFacet. The result of Qhull.runQhull is a QhullFacetList stored +in QhullQh. +

    + +

    »QhullFacetSet

    +

    +A QhullFacetSet is a QhullSet of QhullFacet. QhullFacetSet may be ordered or unordered. The neighboring facets of a QhullFacet is a QhullFacetSet. +The neighbors of a QhullFacet is a QhullFacetSet. +The neighbors are ordered for simplicial facets, matching the opposite vertex of the facet. +

    + +

    »QhullIterator

    +

    +QhullIterator contains macros for defining Java-style iterator templates from a STL-style iterator template. +

    + +

    »QhullLinkedList

    +

    +A QhullLinkedLIst is a template for linked lists with next and previous pointers. +QhullFacetList and QhullVertexList are QhullLinkedLists. +

    + +

    »QhullPoint

    +

    +A QhullPoint is an array of point coordinates, typically doubles. The length of the array is QhullQh.hull_dim. +The identifier of a QhullPoint is its 0-based index from QhullQh.first_point followed by QhullQh.other_points. +

    + +

    »QhullPointSet

    +

    +A QhullPointSet is a QhullSet of QhullPoint. The QhullPointSet of a QhullFacet is its coplanar points. +

    + +

    »QhullQh

    +

    +QhullQh is the root of Qhull's data structure. +It contains initialized constants, sets, buffers, and variables. +It contains an array and a set of QhullPoint, +a list of QhullFacet, and a list of QhullVertex. +The points are the input to Qhull. The facets and vertices are the result of running Qhull. +

    + +

    +Qhull's functions access QhullQh through the global variable, qh_qh. +The global data structures, qh_stat and qh_mem, record statistics and manage memory respectively. +

    + +

    »QhullRidge

    + +

    +A QhullRidge represents the edge between two QhullFacet's. +It is always simplicial with qh.hull_dim-1 QhullVertex)'s. +

    + +

    »QhullRidgeSet

    + +

    +A QhullRidgeSet is a QhullSet of QhullRidge. Each QhullFacet contains a QhullRidgeSet. +

    + +

    »QhullSet

    + +

    +A QhullSet is a set of pointers to objects. QhullSets may be ordered or unordered. They are the core data structure for Qhull. +

    + +

    »QhullVertex

    + +

    +A QhullVertex is a vertex of the convex hull. A simplicial QhullFacet has qh.hull_dim-1 vertices. A QhullVertex contains a QhullPoint. +It may list its neighboring QhullFacet's. +

    + +

    »QhullVertexList

    + +

    +A QhullVertexList is a QhullLinkedList of QhullVertex. +The global data structure, QhullQh contains a QhullVertexList of all +the vertices. +

    + +

    »QhullVertexSet

    + +

    +A QhullVertexSet is a QhullSet of QhullVertex. +The QhullVertexSet of a QhullFacet is the vertices of the facet. It is +ordered for simplicial facets and unordered for non-simplicial facets. +

    + +

    »RboxPoints

    + +

    +RboxPoints is a std::vector of point coordinates (QhullPoint). +It's iterator is CoordinateIterator. +

    +

    +RboxPoints.appendRandomPoints() appends points from a variety of distributions such as uniformly distributed within a cube and random points on a sphere. +It can also append a cube's vertices or specific points. +

    + +

    »Cpp questions for Qhull

    + +Developing C++ code requires many conventions, idioms, and technical details. +The following questions have either +mystified the author or do not have a clear answer. See also +C++ and Perl Guidelines. +and FIXUP notes in the code. +Please add notes to Qhull Wiki. + +
      +
    • FIXUP QH11028 Should return reference, but get reference to temporary +
      iterator Coordinates::operator++() { return iterator(++i); }
      +
    • size() as size_t, size_type, or int +
    • Should all containers have a reserve()? +
    • Qhull.feasiblePoint interface +
    • How to avoid copy constructor while logging, maybeThrowQhullMessage() +
    • How to configure Qhull output. Trace and results should go to stdout/stderr +
    • Qhull and RboxPoints messaging. e.g., ~Qhull, hasQhullMessage(). Rename them as QhullErrorMessage? +
    • How to add additional output to an error message, e.g., qh_setprint +
    • Is idx the best name for an index? It's rather cryptic, but BSD strings.h defines index(). +
    • Qhull::feasiblePoint Qhull::useOutputStream as field or getter? +
    • Define virtual functions for user customization of Qhull (e.g., qh_fprintf, qh_memfree,etc.) +
    • Figure out RoadError::global_log. clearQhullMessage currently clearGlobalLog +
    • Should the false QhullFacet be NULL or empty? e.g., QhullFacet::tricoplanarOwner() and QhullFacetSet::end() +
    • Should output format for floats be predefined (qh_REAL_1, 2.2g, 10.7g) or as currently set for stream +
    • Should cout << !point.defined() be blank or 'undefined' +
    • Infinite point as !defined() +
    • qlist and qlinkedlist define pointer, reference, size_type, difference_type, const_pointer, const_reference for the class but not for iterator and const_iterator + vector.h --
      reference operator[](difference_type _Off) const
      +
    • When forwarding an implementation is base() an approriate name (e.g., Coordinates::iterator::base() as std::vector::iterator). +
    • When forwarding an implementation, does not work "returning address of temporary" +
    • Also --, +=, and -= +
      iterator       &operator++() { return iterator(i++); }
      +
    • if vector inheritance is bad, is QhullVertexSet OK? +
    • Should QhullPointSet define pointer and reference data types? +
    + +

    »Calling Qhull from +C programs

    + +

    Warning: Qhull was not designed for calling from C +programs. You may find the C++ interface easier to use. +You will need to understand the data structures and read the code. +Most users will find it easier to call Qhull as an external +command. + +

    For examples of calling Qhull, see GNU Octave's +computational geometry code, +and Qhull's +user_eg_r.c, +user_eg2_r.c, and +user_r.c. To see how Qhull calls its library, read +unix_r.c, +qconvex.c, +qdelaun.c, +qhalf.c, and +qvoronoi.c. The '*_r.c' files are reentrant, otherwise they are non-reentrant. +Either version may be used. New code should use reentrant Qhull. + +

    See Reentrant Qhull functions, macros, and data +structures for internal documentation of Qhull. The +documentation provides an overview and index. To use the library +you will need to read and understand the code. For most users, it +is better to write data to a file, call the qhull program, and +read the results from the output file.

    + +

    If you use non-reentrant Qhull, be aware of the macros "qh" +and "qhstat", e.g., "qh hull_dim". They are +defined in libqhull.h. They allow the global data +structures to be pre-allocated (faster access) or dynamically +allocated (allows multiple copies).

    + +

    Qhull's Makefile produces a library, libqhull_r.a, +for inclusion in your programs. First review libqhull_r.h. +This defines the data structures used by Qhull and provides +prototypes for the top-level functions. +Most users will only need libqhull_r.h in their programs. For +example, the Qhull program is defined with libqhull_r.h and unix_r.c. +To access all functions, use qhull_ra.h. Include the file +with "#include <libqhull_r/qhull_ra.h>". This +avoids potential name conflicts.

    + +

    If you use the Qhull library, you are on your own as far as +bugs go. Start with small examples for which you know the output. +If you get a bug, try to duplicate it with the Qhull program. The +'Tc' option will catch many problems +as they occur. When an error occurs, use 'T4 TPn' +to trace from the last point added to the hull. Compare your +trace with the trace output from the Qhull program.

    + +

    Errors in the Qhull library are more likely than errors in the +Qhull program. These are usually due to feature interactions that +do not occur in the Qhull program. Please report all errors that +you find in the Qhull library. Please include suggestions for +improvement.

    + +

    »How to avoid exit(), fprintf(), stderr, and stdout

    + +

    Qhull sends output to qh.fout and errors, log messages, and summaries to qh.ferr. qh.fout is normally +stdout and qh.ferr is stderr. qh.fout may be redefined by option 'TO' or the caller. qh.ferr may be redirected to qh.fout by option 'Tz'.

    + +

    Qhull does not use stderr, stdout, fprintf(), or exit() directly.

    + +

    Qhull reports errors via qh_errexit() by writting a message to qh.ferr and invoking longjmp(). +This returns the caller to the corresponding setjmp() (c.f., QH_TRY_ in QhullQh.h). If +qh_errexit() is not available, Qhull functions call qh_exit(). qh_exit() normally calls exit(), +but may be redefined by the user. An example is +libqhullcpp/usermem_r-cpp.cpp. It redefines qh_exit() as a 'throw'.

    + +

    If qh_meminit() or qh_new_qhull() is called with ferr==NULL, then they set ferr to stderr. +Otherwise the Qhull libraries use qh->ferr and qh->qhmem.ferr for error output.

    + +

    If an error occurs before qh->ferr is initialized, Qhull invokes qh_fprintf_stderr(). The user +may redefine this function along with qh_exit(), qh_malloc(), and qh_free(). + +

    The Qhull libraries write output via qh_fprintf() [userprintf_r.c]. Otherwise, the Qhull +libraries do not use stdout, fprintf(), or printf(). Like qh_exit(), the user may redefine +qh_fprintf().

    + +

    »sets and quick memory +allocation

    + +

    You can use mem_r.c and qset_r.c individually. Mem_r.c +implements quick-fit memory allocation. It is faster than +malloc/free in applications that allocate and deallocate lots of +memory.

    + +

    Qset_r.c implements sets and related collections. It's +the inner loop of Qhull, so speed is more important than +abstraction. Set iteration is particularly fast. qset_r.c +just includes the functions needed for Qhull.

    + +

    »Delaunay triangulations +and point indices

    + +

    Here some unchecked code to print the point indices of each +Delaunay triangle. Use option 'QJ' if you want to avoid +non-simplicial facets. Note that upper Delaunay regions are +skipped. These facets correspond to the furthest-site Delaunay +triangulation.

    + +
    +
    +  facetT *facet;
    +  vertexT *vertex, **vertexp;
    +
    +  FORALLfacets {
    +    if (!facet->upperdelaunay) {
    +      printf ("%d", qh_setsize (facet->vertices);
    +      FOREACHvertex_(facet->vertices)
    +        printf (" %d", qh_pointid (vertex->point));
    +      printf ("\n");
    +    }
    +  }
    +
    +
    +
    + +

    »locate a facet with +qh_findbestfacet()

    + +

    The routine qh_findbestfacet in poly2_r.c is +particularly useful. It uses a directed search to locate the +facet that is furthest below a point. For Delaunay +triangulations, this facet is the Delaunay triangle that contains +the lifted point. For convex hulls, the distance of a point to +the convex hull is either the distance to this facet or the +distance to a subface of the facet.

    + +
    +

    Warning: If triangulated output ('Qt') and +the best facet is triangulated, qh_findbestfacet() returns one of +the corresponding 'tricoplanar' facets. The actual best facet may be a different +tricoplanar facet. +

    +See qh_nearvertex() in poly2.c for sample code to visit each +tricoplanar facet. To identify the correct tricoplanar facet, +see Devillers, et. al., ['01] +and Mucke, et al ['96]. If you +implement this test in general dimension, please notify +qhull@qhull.org. +

    + +

    qh_findbestfacet performs an exhaustive search if its directed +search returns a facet that is above the point. This occurs when +the point is inside the hull or if the curvature of the convex +hull is less than the curvature of a sphere centered at the point +(e.g., a point near a lens-shaped convex hull). When the later +occurs, the distance function is bimodal and a directed search +may return a facet on the far side of the convex hull.

    + +

    Algorithms that retain the previously constructed hulls +usually avoid an exhaustive search for the best facet. You may +use a hierarchical decomposition of the convex hull [Dobkin and +Kirkpatrick '90].

    + +

    To use qh_findbestfacet with Delaunay triangulations, lift the +point to a paraboloid by summing the squares of its coordinates +(see qh_setdelaunay in geom2_r.c). Do not scale the input with +options 'Qbk', 'QBk', 'QbB' or 'Qbb'. See Mucke, et al ['96] for a good point location +algorithm.

    + +

    The intersection of a ray with the convex hull may be found by +locating the facet closest to a distant point on the ray. +Intersecting the ray with the facet's hyperplane gives a new +point to test.

    + +

    »on-line construction with +qh_addpoint()

    + +

    The Qhull library may be used for the on-line construction of +convex hulls, Delaunay triangulations, and halfspace +intersections about a point. It may be slower than implementations that retain +intermediate convex hulls (e.g., Clarkson's hull +program). These implementations always use a directed search. +For the on-line construction of convex hulls and halfspace +intersections, Qhull may use an exhaustive search +(qh_findbestfacet).

    + +

    You may use qh_findbestfacet and qh_addpoint (libqhull.c) to add a point to +a convex hull. Do not modify the point's coordinates since +qh_addpoint does not make a copy of the coordinates. For Delaunay +triangulations, you need to lift the point to a paraboloid by +summing the squares of the coordinates (see qh_setdelaunay in +geom2.c). Do not scale the input with options 'Qbk', 'QBk', 'QbB' +or 'Qbb'. Do not deallocate the point's coordinates. You need to +provide a facet that is below the point (qh_findbestfacet). +

    + +

    You can not delete points. Another limitation is that Qhull +uses the initial set of points to determine the maximum roundoff +error (via the upper and lower bounds for each coordinate).

    + +

    For many applications, it is better to rebuild the hull from +scratch for each new point. This is especially true if the point +set is small or if many points are added at a time.

    + +

    Calling qh_addpoint from your program may be slower than +recomputing the convex hull with qh_qhull. This is especially +true if the added points are not appended to the qh first_point +array. In this case, Qhull must search a set to determine a +point's ID. [R. Weber]

    + +

    See user_eg.c for examples of the on-line construction of +convex hulls, Delaunay triangulations, and halfspace +intersections. The outline is:

    + +
    +
    +initialize qhull with an initial set of points
    +qh_qhull();
    +
    +for each additional point p
    +   append p to the end of the point array or allocate p separately
    +   lift p to the paraboloid by calling qh_setdelaunay
    +   facet= qh_findbestfacet (p, !qh_ALL, &bestdist, &isoutside);
    +   if (isoutside)
    +      if (!qh_addpoint (point, facet, False))
    +         break;  /* user requested an early exit with 'TVn' or 'TCn' */
    +
    +call qh_check_maxout() to compute outer planes
    +terminate qhull
    +
    + +

    »Constrained +Delaunay triangulation

    + +

    With a fair amount of work, Qhull is suitable for constrained +Delaunay triangulation. See Shewchuk, ACM Symposium on +Computational Geometry, Minneapolis 1998.

    + +

    Here's a quick way to add a constraint to a Delaunay +triangulation: subdivide the constraint into pieces shorter than +the minimum feature separation. You will need an independent +check of the constraint in the output since the minimum feature +separation may be incorrect. [H. Geron]

    + +

    »Tricoplanar facets and option 'Qt'

    + +

    Option 'Qt' triangulates non-simplicial +facets (e.g., a square facet in 3-d or a cubical facet in 4-d). +All facets share the same apex (i.e., the first vertex in facet->vertices). +For each triangulated facet, Qhull +sets facet->tricoplanar true and copies facet->center, facet->normal, facet->offset, and facet->maxoutside. One of +the facets owns facet->normal; its facet->keepcentrum is true. +If facet->isarea is false, facet->triowner points to the owning +facet. + +

    Qhull sets facet->degenerate if the facet's vertices belong +to the same ridge of the non-simplicial facet. + +

    To visit each tricoplanar facet of a non-simplicial facet, +either visit all neighbors of the apex or recursively visit +all neighbors of a tricoplanar facet. The tricoplanar facets +will have the same facet->center.

    + +

    See qh_detvridge for an example of ignoring tricoplanar facets.

    + +

    »Voronoi vertices of a +region

    + +

    The following code iterates over all Voronoi vertices for each +Voronoi region. Qhull computes Voronoi vertices from the convex +hull that corresponds to a Delaunay triangulation. An input site +corresponds to a vertex of the convex hull and a Voronoi vertex +corresponds to an adjacent facet. A facet is +"upperdelaunay" if it corresponds to a Voronoi vertex +"at-infinity". Qhull uses qh_printvoronoi in io.c +for 'qvoronoi o'

    + +
    +
    +/* please review this code for correctness */
    +qh_setvoronoi_all();
    +FORALLvertices {
    +   site_id = qh_pointid (vertex->point);
    +   if (qh hull_dim == 3)
    +      qh_order_vertexneighbors(vertex);
    +   infinity_seen = 0;
    +   FOREACHneighbor_(vertex) {
    +      if (neighbor->upperdelaunay) {
    +        if (!infinity_seen) {
    +          infinity_seen = 1;
    +          ... process a Voronoi vertex "at infinity" ...
    +        }
    +      }else {
    +        voronoi_vertex = neighbor->center;
    +        ... your code goes here ...
    +      }
    +   }
    +}
    +
    +
    + +

    »Voronoi vertices of a +ridge

    + +

    Qhull uses qh_printvdiagram() in io.c to print the ridges of a +Voronoi diagram for option 'Fv'. +The helper function qh_eachvoronoi() does the real work. It calls +the callback 'printvridge' for each ridge of the Voronoi diagram. +

    + +

    You may call qh_printvdiagram2(), qh_eachvoronoi(), or +qh_eachvoronoi_all() with your own function. If you do not need +the total number of ridges, you can skip the first call to +qh_printvdiagram2(). See qh_printvridge() and qh_printvnorm() in +io.c for examples.

    + +

    »vertex neighbors of +a vertex

    + +

    To visit all of the vertices that share an edge with a vertex: +

    + +
      +
    • Generate neighbors for each vertex with + qh_vertexneighbors in poly2.c.
    • +
    • For simplicial facets, visit the vertices of each + neighbor
    • +
    • For non-simplicial facets,
        +
      • Generate ridges for neighbors with qh_makeridges + in merge.c.
      • +
      • Generate ridges for a vertex with qh_vertexridges + in merge.c.
      • +
      • Visit the vertices of these ridges.
      • +
      +
    • +
    + +

    For non-simplicial facets, the ridges form a simplicial +decomposition of the (d-2)-faces between each pair of facets -- +if you need 1-faces, you probably need to generate the full face +graph of the convex hull.

    + +

    »Performance of +Qhull

    + +

    Empirically, Qhull's performance is balanced in the sense that +the average case happens on average. This may always be true if +the precision of the input is limited to at most O(log n) +bits. Empirically, the maximum number of vertices occurs at the +end of constructing the hull.

    + +

    Let n be the number of input points, v be the +number of output vertices, and f_v be the maximum number +of facets for a convex hull of v vertices. If both +conditions hold, Qhull runs in O(n log v) in 2-d and 3-d +and O(n f_v/v) otherwise. The function f_v +increases rapidly with dimension. It is O(v^floor(d/2) / +floor(d/2)!).

    + +

    The time complexity for merging is unknown. Options 'C-0' and 'Qx' +(defaults) handle precision problems due to floating-point +arithmetic. They are optimized for simplicial outputs.

    + +

    When running large data sets, you should monitor Qhull's +performance with the 'TFn' option. +The time per facet is approximately constant. In high-d with many +merged facets, the size of the ridge sets grows rapidly. For +example the product of 8-d simplices contains 18 facets and +500,000 ridges. This will increase the time needed per facet.

    + +

    As dimension increases, the number of facets and ridges in a +convex hull grows rapidly for the same number of vertices. For +example, the convex hull of 300 cospherical points in 6-d has +30,000 facets.

    + +

    If Qhull appears to stop processing facets, check the memory +usage of Qhull. If more than 5-10% of Qhull is in virtual memory, +its performance will degrade rapidly.

    + +

    When building hulls in 20-d and higher, you can follow the +progress of Qhull with option 'T1'. +It reports each major event in processing a point.

    + +

    To reduce memory requirements, recompile Qhull for +single-precision reals (REALfloat in user.h). +Single-precision does not work with joggle ('QJ'). Check qh_MEMalign in user.h +and the match between free list sizes and data structure sizes +(see the end of the statistics report from 'Ts'). If free list sizes do not match, +you may be able to use a smaller qh_MEMalign. Setting +qh_COMPUTEfurthest saves a small amount of memory, as does +clearing qh_MAXoutside (both in user.h).

    + +

    Shewchuk is working on a 3-d version of his triangle +program. It is optimized for 3-d simplicial Delaunay triangulation +and uses less memory than Qhull.

    + +

    To reduce the size of the Qhull executable, consider +qh_NOtrace and qh_KEEPstatistics 0 in user.h. By +changing user.c you can also remove the input/output +code in io.c. If you don't need facet merging, then +version 1.01 of Qhull is much smaller. It contains some bugs that +prevent Qhull from initializing in simple test cases. It is +slower in high dimensions.

    + +

    The precision options, 'Vn', 'Wn', 'Un'. +'A-n', 'C-n', +'An', 'Cn', +and 'Qx', may have large effects on +Qhull performance. You will need to experiment to find the best +combination for your application.

    + +

    The verify option ('Tv') checks +every point after the hull is complete. If facet merging is used, +it checks that every point is inside every facet. This can take a +very long time if there are many points and many facets. You can +interrupt the verify without losing your output. If facet merging +is not used and there are many points and facets, Qhull uses a +directed search instead of an exhaustive search. This should be +fast enough for most point sets. Directed search is not used for +facet merging because directed search was already used for +updating the facets' outer planes.

    + +

    The check-frequently option ('Tc') +becomes expensive as the dimension increases. The verify option +('Tv') performs many of the same +checks before outputting the results.

    + +

    Options 'Q0' (no pre-merging), 'Q3' (no checks for redundant vertices), +'Q5' (no updates for outer planes), +and 'Q8' (no near-interior points) +increase Qhull's speed. The corresponding operations may not be +needed in your application.

    + +

    In 2-d and 3-d, a partial hull may be faster to produce. +Option 'QgGn' only builds facets +visible to point n. Option 'QgVn' +only builds facets that contain point n. In higher-dimensions, +this does not reduce the number of facets.

    + +

    User.h includes a number of performance-related +constants. Changes may improve Qhull performance on your data +sets. To understand their effect on performance, you will need to +read the corresponding code.

    + +

    GNU gprof reports that the dominate cost for 3-d +convex hull of cosperical points is qh_distplane(), mainly called +from qh_findbestnew(). The dominate cost for 3-d Delaunay triangulation +is creating new facets in qh_addpoint(), while qh_distplane() remains +the most expensive function. + +

    +

    »Enhancements to Qhull

    + +

    There are many ways in which Qhull can be improved.

    + +
    +[Jan 2016] Suggestions
    +------------
    +To do for a future verson of Qhull
    + - Add a post-merge pass for Delaunay slivers.  Merge into a neighbor with a circumsphere that includes the opposite point. [M. Treacy]
    + - Add a merge pass before cone creation to remove duplicate subridges between horizon facets
    + - Option to add a bounding box for Delaunay triangulations, e,g., nearly coincident points
    + - Report error when rbox Cn,r,m does not produce points (e.g., 'r' distributions)
    + - Rescale output to match 'QbB' on input [J. Metz, 1/30/2014 12:21p]
    + - Run through valgrind
    + - Notes to compgeom on conformant triangulation and Voronoi volume
    + - Git: Create signed tags for Qhull versions
    + - Implement weighted Delaunay triangulation and weighted Voronoi diagram [A. Liebscher]
    +   e.g., Sugihara, "Three-dimensional convex hull as a fruitful source of diagrams," Theoretical Computer Science, 2000, 235:325-337
    + - testqset: test qh_setdelnth and move-to-front
    + - Makefile: Re-review gcc/g++ warnings.  OK in 2011.
    + - Break up -Wextra into its components or figure out how to override -Wunused-but-set-variable
    +   unused-but-set-variable is reporting incorrectly.  All instances are annotated.
    + - CMakelists.txt:  Why are files duplicated for cmake build
    + - CMakeLists.txt: configure the 'ctest' target
    + - The size of maxpoints in qh_maxsimplex should be d+3 unique points to help avoid QH6154
    +
    + - Can countT be defined as 'int', 'unsigned int', or 64-bit int?
    +   countT is currently defined as 'int' in qset_r.h
    +   Vertex ID and ridge ID perhaps should be countT, They are currently 'unsigned'
    +   Check use of 'int' vs. countT in all cpp code
    +   Check use of 'int' vs. countT in all c code
    +   qset_r.h defines countT -- duplicates code in user_r.h -- need to add to qset.h/user.h
    +   countT -1 used as a flag in Coordinates.mid(), QhullFacet->id()
    +   Also QhullPoints indexOf and lastIndexOf
    +   Also QhullPointSet indexOf and lastIndexOf
    +   Coordinates.indexOf assumes countT is signed (from end)
    +   Coordinates.lastIndexOf assumes countT is signed (from end)
    +   All error messages with countT are wrong, convert to int?
    +   RboxPoints.qh_fprintf_rbox, etc. message 9393 assumes countT but may be int, va_arg(args, countT);  Need to split
    +
    +------------
    +To do for a furture version of the C++ interface
    + - Fix C++ memory leak in user_eg3 [M. Sandim]
    + - Document C++ using Doxygen conventions (//! and //!<)
    + - Should Qhull manage the output formats for doubles?  QH11010 user_r.h defines qh_REAL_1 as %6.8g
    + - Allocate memory for QhullSet using Qhull.qhmem.  Create default constructors for QhullVertexSet etc.  Also mid() etc.
    + - Add interior point for automatic translation?
    + - Add hasNext() to all next() iterators (e.g., QhullVertex)
    + - Add defineAs() to each object
    + - Write a program with concurrent Qhull
    + - Write QhullStat and QhullStat_test
    + - Add QList and vector instance of facetT*, etc.
    + - Generalize QhullPointSetIterator
    + - qh-code.htm: Document changes to C++ interface.
    +      Organize C++ documentation into collection classes, etc.
    + - Review all C++ classes and C++ tests
    + - QhullVertexSet uses QhullSetBase::referenceSetT() to free it's memory.   Probably needed elsewhere
    + - The Boost Graph Library provides C++ classes for graph data structures. It may help
    +   enhance Qhull's C++ interface [Dr. Dobb's 9/00 p. 29-38; OOPSLA '99 p. 399-414].
    +
    +[Jan 2010] Suggestions
    + - Generate vcproj from qtpro files
    +   cd qtpro && qmake -spec win32-msvc2005 -tp vc -recursive
    +   sed -i 's/C\:\/bash\/local\/qhull\/qtpro\///' qhull-all.sln
    +   Change qhullcpp to libqhull.dll
    +   Allow both builds on same host (keep /tmp separate)
    + - Make distribution -- remove tmp, news, .git, leftovers from project, change CRLF
    +     search for 2010.1, Dates
    +     qhulltest --all added to output
    +     Add md5sum
    +     Add test of user_eg3, etc.
    + - C++ class for access to statistics, accumulate vs. add
    + - Add dialog box to RoadError-- a virtual function?
    + - Option 'Gt' does not make visible all facets of the mesh example, rbox 32 M1,0,1 | qhull d Gt
    + - Option to select bounded Voronoi regions [A. Uzunovic]
    + - Merge small volume boundary cells into unbounded regions [Dominik Szczerba]
    + - Postmerge with merge options
    + - Add const to C code
    + - Add modify operators and MutablePointCoordinateIterator to PointCoordinates
    + - Add Qtest::toString() functions for QhullPoint and others.  QByteArray and qstrdup()
    + - Fix option Qt for conformant triangulations of merged facets
    + - Investigate flipped facet -- rbox 100 s D3 t1263080158 | qhull R1e-3 Tcv Qc
    + - Add doc comments to c++ code
    + - Measure performance of Qhull, seconds per point by dimension
    + - Report potential wraparound of 64-bit ints -- e.g., a large set or points
    +
    +Documentation
    +- Qhull::addPoint().  Problems with qh_findbestfacet and otherpoints see
    +   qh-code.htm#inc on-line construction with qh_addpoint()
    +- How to handle 64-bit possible loss of data.  WARN64, ptr_intT, size_t/int
    +- Show custom of qh_fprintf
    +- grep 'qh_mem ' x | sort | awk '{ print $2; }' | uniq -c | grep -vE ' (2|4|6|8|10|12|14|16|20|64|162)[^0-9]'
    +- qtpro/qhulltest contains .pro and Makefile.  Remove Makefiles by setting shadow directory to ../../tmp/projectname
    +- Rules for use of qh_qh and multi processes
    +    UsingQhull
    +    errorIfAnotherUser
    +    ~QhullPoints() needs ownership of qh_qh
    +    Does !qh_pointer work?
    +    When is qh_qh required?  Minimize the time.
    +   qhmem, qhstat.ferr
    +   qhull_inuse==1 when qhull globals active [not useful?]
    +   rbox_inuse==1 when rbox globals active
    +   - Multithreaded -- call largest dimension for infinityPoint() and origin()
    + - Better documentation for qhmem totshort, freesize, etc.
    + - how to change .h, .c, and .cpp to text/html.  OK in Opera
    + - QhullVertex.dimension() is not quite correct, epensive
    + - Check globalAngleEpsilon
    + - Deprecate save_qhull()
    +
    +[Dec 2003] Here is a partial list:
    + - fix finddelaunay() in user_eg.c for tricoplanar facets
    + - write a BGL, C++ interface to Qhull
    +     http://www.boost.org/libs/graph/doc/table_of_contents.html
    + - change qh_save_qhull to swap the qhT structure instead of using pointers
    + - change error handling and tracing to be independent of 'qh ferr'
    + - determine the maximum width for a given set of parameters
    + - prove that directed search locates all coplanar facets
    + - in high-d merging, can a loop of facets become disconnected?
    + - find a way to improve inner hulls in 5-d and higher
    + - determine the best policy for facet visibility ('Vn')
    + - determine the limitations of 'Qg'
    +
    +Precision improvements:
    + - For 'Qt', resolve cross-linked, butterfly ridges.
    +     May allow retriangulation in qh_addpoint().
    + - for Delaunay triangulations ('d' or 'v') under joggled input ('QJ'),
    +     remove vertical facets whose lowest vertex may be coplanar with convex hull
    + - review use of 'Qbb' with 'd QJ'.  Is MAXabs_coord better than MAXwidth?
    + - check Sugihara and Iri's better in-sphere test [Canadian
    +     Conf. on Comp. Geo., 1989; Univ. of Tokyo RMI 89-05]
    + - replace centrum with center of mass and facet area
    + - handle numeric overflow in qh_normalize and elsewhere
    + - merge flipped facets into non-flipped neighbors.
    +     currently they merge into best neighbor (appears ok)
    + - determine min norm for Cramer's rule (qh_sethyperplane_det).  It looks high.
    + - improve facet width for very narrow distributions
    +
    +New features:
    + - implement Matlab's tsearch() using Qhull
    + - compute volume of Voronoi regions.  You need to determine the dual face
    +   graph in all dimensions [see Clarkson's hull program]
    + - compute alpha shapes [see Clarkson's hull program]
    + - implement deletion of Delaunay vertices
    +      see Devillers, ACM Symposium on Computational Geometry, Minneapolis 1999.
    + - compute largest empty circle [see O'Rourke, chapter 5.5.3] [Hase]
    + - list redundant (i.e., coincident) vertices [Spitz]
    + - implement Mucke, et al, ['96] for point location in Delaunay triangulations
    + - implement convex hull of moving points
    + - implement constrained Delaunay diagrams
    +      see Shewchuk, ACM Symposium on Computational Geometry, Minneapolis 1998.
    + - estimate outer volume of hull
    + - automatically determine lower dimensional hulls
    + - allow "color" data for input points
    +      need to insert a coordinate for Delaunay triangulations
    +
    +Input/output improvements:
    + - Support the VTK Visualization Toolkit, http://www.kitware.com/vtk.html
    + - generate output data array for Qhull library [Gautier]
    + - need improved DOS window with screen fonts, scrollbar, cut/paste
    + - generate Geomview output for Voronoi ridges and unbounded rays
    + - generate Geomview output for halfspace intersection
    + - generate Geomview display of furthest-site Voronoi diagram
    + - use 'GDn' to view 5-d facets in 4-d
    + - convert Geomview output for other 3-d viewers
    + - add interactive output option to avoid recomputing a hull
    + - orient vertex neighbors for 'Fv' in 3-d and 2-d
    + - track total number of ridges for summary and logging
    +
    +Performance improvements:
    + - optimize Qhull for 2-d Delaunay triangulations
    + -   use O'Rourke's '94 vertex->duplicate_edge
    + -   add bucketing
    + -   better to specialize all of the code (ca. 2-3x faster w/o merging)
    + - use updated LU decomposition to speed up hyperplane construction
    + -        [Gill et al. 1974, Math. Comp. 28:505-35]
    + - construct hyperplanes from the corresponding horizon/visible facets
    + - for merging in high d, do not use vertex->neighbors
    +
    +
    + +

    Please let us know about your applications and improvements.

    + +
    + +

    Up: Home +page for Qhull
    +Up: Qhull manual: Table of +Contents
    +To: Programs +• Options +• Output +• Formats +• Geomview +• Print +• Qhull +• Precision +• Trace +• Functions
    +To: Qhull code: Table of Contents
    +Dn: Qhull functions, macros, and data +structures + +


    + +

    The Geometry Center +Home Page

    + +

    Comments to: qhull@qhull.org +
    +Created: Sept. 25, 1995 --- Last modified: see changes.txt

    + + diff --git a/xs/src/qhull/html/qh-eg.htm b/xs/src/qhull/html/qh-eg.htm new file mode 100644 index 000000000..a08f0d13f --- /dev/null +++ b/xs/src/qhull/html/qh-eg.htm @@ -0,0 +1,693 @@ + + + + +Examples of Qhull + + + + +

    Up: Home +page for Qhull
    +Up: Qhull manual: Table of Contents
    +To: Programs +• Options +• Output +• Formats +• Geomview +• Print +• Qhull +• Precision +• Trace +• Functions
    +To: Qhull examples: Table of Contents (please wait +while loading)
    + +


    + +

    [halfspace] Examples of Qhull

    + +

    This section of the Qhull manual will introduce you to Qhull +and its options. Each example is a file for viewing with Geomview. You will need to +use a Unix computer with a copy of Geomview. +

    +If you are not running Unix, you can view pictures +for some of the examples. To understand Qhull without Geomview, try the +examples in Programs and +Programs/input. You can also try small +examples that you compute by hand. Use rbox +to generate examples. +

    +To generate the Geomview examples, execute the shell script eg/q_eg. +It uses rbox. The shell script eg/q_egtest generates +test examples, and eg/q_test exercises the code. If you +find yourself viewing the inside of a 3-d example, use Geomview's +normalization option on the 'obscure' menu.

    + +

    Copyright © 1995-2015 C.B. Barber

    + +
    + +

    »Qhull examples: Table of +Contents

    + + + +
    + + +
    + +

    »2-d and 3-d examples

    + +

    »rbox c D3 | qconvex G +>eg.01.cube

    + +

    The first example is a cube in 3-d. The color of each facet +indicates its normal. For example, normal [0,0,1] along the Z +axis is (r=0.5, g=0.5, b=1.0). With the 'Dn' option in rbox, +you can generate hypercubes in any dimension. Above 7-d the +number of intermediate facets grows rapidly. Use 'TFn' to track qconvex's progress. Note +that each facet is a square that qconvex merged from coplanar +triangles.

    + +

    »rbox c d G3.0 | qconvex G +>eg.02.diamond.cube

    + +

    The second example is a cube plus a diamond ('d') scaled by rbox's +'G' option. In higher dimensions, diamonds are much simpler than +hypercubes.

    + +

    »rbox s 100 D3 | qconvex G +>eg.03.sphere

    + +

    The rbox s option generates random points and +projects them to the d-sphere. All points should be on the convex +hull. Notice that random points look more clustered than you +might expect. You can get a smoother distribution by merging +facets and printing the vertices, e.g., rbox 1000 s | qconvex +A-0.95 p | qconvex G >eg.99.

    + +

    »rbox s 100 D2 | qconvex G +>eg.04.circle

    + +

    In 2-d, there are many ways to generate a convex hull. One of +the earliest algorithms, and one of the fastest, is the 2-d +Quickhull algorithm [c.f., Preparata & Shamos '85]. It was the model for +Qhull.

    + +

    »rbox 10 l | qconvex G +>eg.05.spiral

    + +

    One rotation of a spiral.

    + +

    »rbox 1000 D2 | qconvex C-0.03 +Qc Gapcv >eg.06.merge.square

    + +

    This demonstrates how Qhull handles precision errors. Option 'C-0.03' requires a clearly convex angle +between adjacent facets. Otherwise, Qhull merges the facets.

    + +

    This is the convex hull of random points in a square. The +facets have thickness because they must be outside all points and +must include their vertices. The colored lines represent the +original points and the spheres represent the vertices. Floating +in the middle of each facet is the centrum. Each centrum is at +least 0.03 below the planes of its neighbors. This guarantees +that the facets are convex.

    + +

    »rbox 1000 D3 | qconvex G +>eg.07.box

    + +

    Here's the same distribution but in 3-d with Qhull handling +machine roundoff errors. Note the large number of facets.

    + +

    »rbox c G0.4 s 500 | qconvex G +>eg.08a.cube.sphere

    + +

    The sphere is just barely poking out of the cube. Try the same +distribution with randomization turned on ('Qr'). This turns Qhull into a +randomized incremental algorithm. To compare Qhull and +randomization, look at the number of hyperplanes created and the +number of points partitioned. Don't compare CPU times since Qhull's +implementation of randomization is inefficient. The number of +hyperplanes and partitionings indicate the dominant costs for +Qhull. With randomization, you'll notice that the number of +facets created is larger than before. This is especially true as +you increase the number of points. It is because the randomized +algorithm builds most of the sphere before it adds the cube's +vertices.

    + +

    »rbox d G0.6 s 500 | qconvex G +>eg.08b.diamond.sphere

    + +

    This is a combination of the diamond distribution and the +sphere.

    + +

    »rbox 100 L3 G0.5 s | qconvex +G >eg.09.lens

    + +

    Each half of the lens distribution lies on a sphere of radius +three. A directed search for the furthest facet below a point +(e.g., qh_findbest in geom.c) may fail if started from +an arbitrary facet. For example, if the first facet is on the +opposite side of the lens, a directed search will report that the +point is inside the convex hull even though it is outside. This +problem occurs whenever the curvature of the convex hull is less +than a sphere centered at the test point.

    + +

    To prevent this problem, Qhull does not use directed search +all the time. When Qhull processes a point on the edge of the +lens, it partitions the remaining points with an exhaustive +search instead of a directed search (see qh_findbestnew in geom2.c). +

    + +

    »How Qhull adds a point

    + +

    »rbox 100 s P0.5,0.5,0.5 | +qconvex Ga QG0 >eg.10a.sphere.visible

    + +

    The next 4 examples show how Qhull adds a point. The point +[0.5,0.5,0.5] is at one corner of the bounding box. Qhull adds a +point using the beneath-beyond algorithm. First Qhull finds all +of the facets that are visible from the point. Qhull will replace +these facets with new facets.

    + +

    »rbox 100 s +P0.5,0.5,0.5|qconvex Ga QG-0 >eg.10b.sphere.beyond

    + +

    These are the facets that are not visible from the point. +Qhull will keep these facets.

    + +

    »rbox 100 s P0.5,0.5,0.5 | +qconvex PG Ga QG0 >eg.10c.sphere.horizon

    + +

    These facets are the horizon facets; they border the visible +facets. The inside edges are the horizon ridges. Each horizon +ridge will form the base for a new facet.

    + +

    »rbox 100 s P0.5,0.5,0.5 | +qconvex Ga QV0 PgG >eg.10d.sphere.cone

    + +

    This is the cone of points from the new point to the horizon +facets. Try combining this image with eg.10c.sphere.horizon +and eg.10a.sphere.visible. +

    + +

    »rbox 100 s P0.5,0.5,0.5 | +qconvex Ga >eg.10e.sphere.new

    + +

    This is the convex hull after [0.5,0.5,0.5] has been added. +Note that in actual practice, the above sequence would never +happen. Unlike the randomized algorithms, Qhull always processes +a point that is furthest in an outside set. A point like +[0.5,0.5,0.5] would be one of the first points processed.

    + +

    »rbox 100 s P0.5,0.5,0.5 | +qhull Ga QV0g Q0 >eg.14.sphere.corner

    + +

    The 'QVn', 'QGn ' and 'Pdk' +options define good facets for Qhull. In this case 'QV0' defines the 0'th point +[0.5,0.5,0.5] as the good vertex, and 'Qg' +tells Qhull to only build facets that might be part of a good +facet. This technique reduces output size in low dimensions. It +does not work with facet merging.

    + +

    »Triangulated output or joggled input

    + +

    »rbox 500 W0 | qconvex QR0 Qc Gvp >eg.15a.surface

    + +

    This is the convex hull of 500 points on the surface of +a cube. Note the large, non-simplicial facet for each face. +Qhull merges non-convex facets. + +

    If the facets were not merged, Qhull +would report precision problems. For example, turn off facet merging +with option 'Q0'. Qhull may report concave +facets, flipped facets, or other precision errors: +

    +rbox 500 W0 | qhull QR0 Q0 +
    + +

    +

    »rbox 500 W0 | qconvex QR0 Qt Qc Gvp >eg.15b.triangle

    + +

    Like the previous examples, this is the convex hull of 500 points on the +surface of a cube. Option 'Qt' triangulates the +non-simplicial facets. Triangulated output is +particularly helpful for Delaunay triangulations. + +

    +

    »rbox 500 W0 | qconvex QR0 QJ5e-2 Qc Gvp >eg.15c.joggle

    + +

    This is the convex hull of 500 joggled points on the surface of +a cube. The option 'QJ5e-2' +sets a very large joggle to make the effect visible. Notice +that all of the facets are triangles. If you rotate the cube, +you'll see red-yellow lines for coplanar points. +

    +With option 'QJ', Qhull joggles the +input to avoid precision problems. It adds a small random number +to each input coordinate. If a precision +error occurs, it increases the joggle and tries again. It repeats +this process until no precision problems occur. +

    +Joggled input is a simple solution to precision problems in +computational geometry. Qhull can also merge facets to handle +precision problems. See Merged facets or joggled input. + +

    »Delaunay and Voronoi diagrams

    + +

    »qdelaunay Qt +<eg.data.17 GnraD2 >eg.17a.delaunay.2

    + +

    +The input file, eg.data.17, consists of a square, 15 random +points within the outside half of the square, and 6 co-circular +points centered on the square. + +

    The Delaunay triangulation is the triangulation with empty +circumcircles. The input for this example is unusual because it +includes six co-circular points. Every triangular subset of these +points has the same circumcircle. Option 'Qt' +triangulates the co-circular facet.

    + +

    »qdelaunay <eg.data.17 +GnraD2 >eg.17b.delaunay.2i

    + +

    This is the same example without triangulated output ('Qt'). qdelaunay +merges the non-unique Delaunay triangles into a hexagon.

    + +

    »qdelaunay <eg.data.17 +Ga >eg.17c.delaunay.2-3

    + +

    This is how Qhull generated both diagrams. Use Geomview's +'obscure' menu to turn off normalization, and Geomview's +'cameras' menu to turn off perspective. Then load this object +with one of the previous diagrams.

    + +

    The points are lifted to a paraboloid by summing the squares +of each coordinate. These are the light blue points. Then the +convex hull is taken. That's what you see here. If you look up +the Z-axis, you'll see that points and edges coincide.

    + +

    »qvoronoi QJ +<eg.data.17 Gna >eg.17d.voronoi.2

    + +

    The Voronoi diagram is the dual of the Delaunay triangulation. +Here you see the original sites and the Voronoi vertices. +Notice the each +vertex is equidistant from three sites. The edges indicate the +Voronoi region for a site. Qhull does not draw the unbounded +edges. Instead, it draws extra edges to close the unbounded +Voronoi regions. You may find it helpful to enclose the input +points in a square. You can compute the unbounded +rays from option 'Fo'. +

    + +

    Instead +of triangulated output ('Qt'), this +example uses joggled input ('QJ'). +Normally, you should use neither 'QJ' nor 'Qt' for Voronoi diagrams. + +

    »qvoronoi <eg.data.17 +Gna >eg.17e.voronoi.2i

    + +

    This looks the same as the previous diagrams, but take a look +at the data. Run 'qvoronoi p <eg/eg.data.17'. This prints +the Voronoi vertices. + +

    With 'QJ', there are four nearly identical Voronoi vertices +within 10^-11 of the origin. Option 'QJ' joggled the input. After the joggle, +the cocircular +input sites are no longer cocircular. The corresponding Voronoi vertices are +similar but not identical. + +

    This example does not use options 'Qt' or 'QJ'. The cocircular +input sites define one Voronoi vertex near the origin.

    + +

    Option 'Qt' would triangulate the corresponding Delaunay region into +four triangles. Each triangle is assigned the same Voronoi vertex.

    + +

    » rbox c G0.1 d | +qdelaunay Gt Qz <eg.17f.delaunay.3

    + +

    This is the 3-d Delaunay triangulation of a small cube inside +a prism. Since the outside ridges are transparent, it shows the +interior of the outermost facets. If you slice open the +triangulation with Geomview's ginsu, you will see that the innermost +facet is a cube. Note the use of 'Qz' +to add a point "at infinity". This avoids a degenerate +input due to cospherical points.

    + +

    »rbox 10 D2 d | qdelaunay +Qu G >eg.18a.furthest.2-3

    + +

    The furthest-site Voronoi diagram contains Voronoi regions for +points that are furthest from an input site. It is the +dual of the furthest-site Delaunay triangulation. You can +determine the furthest-site Delaunay triangulation from the +convex hull of the lifted points (eg.17c.delaunay.2-3). +The upper convex hull (blue) generates the furthest-site Delaunay +triangulation.

    + +

    »rbox 10 D2 d | qdelaunay +Qu Pd2 G >eg.18b.furthest-up.2-3

    + +

    This is the upper convex hull of the preceding example. The +furthest-site Delaunay triangulation is the projection of the +upper convex hull back to the input points. The furthest-site +Voronoi vertices are the circumcenters of the furthest-site +Delaunay triangles.

    + +

    »rbox 10 D2 d | qvoronoi +Qu Gv >eg.18c.furthest.2

    + +

    This shows an incomplete furthest-site Voronoi diagram. It +only shows regions with more than two vertices. The regions are +artificially truncated. The actual regions are unbounded. You can +print the regions' vertices with 'qvoronoi Qu o'.

    + +

    Use Geomview's 'obscure' menu to turn off normalization, and +Geomview's 'cameras' menu to turn off perspective. Then load this +with the upper convex hull.

    + +

    »rbox 10 D3 | qvoronoi QV5 +p | qconvex G >eg.19.voronoi.region.3

    + +

    This shows the Voronoi region for input site 5 of a 3-d +Voronoi diagram.

    + +

    »Facet merging for imprecision

    + +

    »rbox r s 20 Z1 G0.2 | +qconvex G >eg.20.cone

    + +

    There are two things unusual about this cone. +One is the large flat disk at one end and the other is the +rectangles about the middle. That's how the points were +generated, and if those points were exact, this is the correct +hull. But rbox used floating point arithmetic to +generate the data. So the precise convex hull should have been +triangles instead of rectangles. By requiring convexity, Qhull +has recovered the original design.

    + +

    »rbox 200 s | qhull Q0 +R0.01 Gav Po >eg.21a.roundoff.errors

    + +

    This is the convex hull of 200 cospherical points with +precision errors ignored ('Q0'). To +demonstrate the effect of roundoff error, we've added a random +perturbation ('R0.01') to every +distance and hyperplane calculation. Qhull, like all other convex +hull algorithms with floating point arithmetic, makes +inconsistent decisions and generates wildly wrong results. In +this case, one or more facets are flipped over. These facets have +the wrong color. You can also turn on 'normals' in Geomview's +appearances menu and turn off 'facing normals'. There should be +some white lines pointing in the wrong direction. These +correspond to flipped facets.

    + +

    Different machines may not produce this picture. If your +machine generated a long error message, decrease the number of +points or the random perturbation ('R0.01'). +If it did not report flipped facets, increase the number of +points or perturbation.

    + +

    »rbox 200 s | qconvex Qc +R0.01 Gpav >eg.21b.roundoff.fixed

    + +

    Qhull handles the random perturbations and returns an +imprecise sphere. +In this case, the output is a weak approximation to the points. +This is because a random perturbation of 'R0.01 ' is equivalent to losing all but +1.8 digits of precision. The outer planes float above the points +because Qhull needs to allow for the maximum roundoff error.

    +

    +If you start with a smaller random perturbation, you +can use joggle ('QJn') to avoid +precision problems. You need to set n significantly +larger than the random perturbation. For example, try +'rbox 200 s | qconvex Qc R1e-4 QJ1e-1'. + +

    »rbox 1000 s| qconvex C0.01 +Qc Gcrp >eg.22a.merge.sphere.01

    + +

    »rbox 1000 s| qconvex +C-0.01 Qc Gcrp >eg.22b.merge.sphere.-01

    + +

    »rbox 1000 s| qconvex C0.05 +Qc Gcrpv >eg.22c.merge.sphere.05

    + +

    »rbox 1000 s| qconvex +C-0.05 Qc Gcrpv >eg.22d.merge.sphere.-05

    + +

    The next four examples compare post-merging and pre-merging ('Cn' vs. 'C-n'). +Qhull uses '-' as a flag to indicate pre-merging.

    + +

    Post-merging happens after the convex hull is built. During +post-merging, Qhull repeatedly merges an independent set of +non-convex facets. For a given set of parameters, the result is +about as good as one can hope for.

    + +

    Pre-merging does the same thing as post-merging, except that +it happens after adding each point to the convex hull. With +pre-merging, Qhull guarantees a convex hull, but the facets are +wider than those from post-merging. If a pre-merge option is not +specified, Qhull handles machine round-off errors.

    + +

    You may see coplanar points appearing slightly outside +the facets of the last example. This is becomes Geomview moves +line segments forward toward the viewer. You can avoid the +effect by setting 'lines closer' to '0' in Geomview's camera menu. + +

    »rbox 1000 | qconvex s +Gcprvah C0.1 Qc >eg.23.merge.cube

    + +

    Here's the 3-d imprecise cube with all of the Geomview +options. There's spheres for the vertices, radii for the coplanar +points, dots for the interior points, hyperplane intersections, +centrums, and inner and outer planes. The radii are shorter than +the spheres because this uses post-merging ('C0.1') +instead of pre-merging. + +

    »4-d objects

    + +

    With Qhull and Geomview you can develop an intuitive sense of +4-d surfaces. When you get into trouble, think of viewing the +surface of a 3-d sphere in a 2-d plane.

    + +

    »rbox 5000 D4 | qconvex s GD0v +Pd0:0.5 C-0.02 C0.1 >eg.24.merge.cube.4d-in-3d

    + +

    Here's one facet of the imprecise cube in 4-d. It's projected +into 3-d (the 'GDn' option drops +dimension n). Each ridge consists of two triangles between this +facet and the neighboring facet. In this case, Geomview displays +the topological ridges, i.e., as triangles between three +vertices. That is why the cube looks lopsided.

    + +

    »rbox 5000 D4 | qconvex s +C-0.02 C0.1 Gh >eg.30.4d.merge.cube

    + +

    Here +is the equivalent in 4-d of the imprecise square +and imprecise cube. It's the imprecise convex +hull of 5000 random points in a hypercube. It's a full 4-d object +so Geomview's ginsu does not work. If you view it in +Geomview, you'll be inside the hypercube. To view 4-d objects +directly, either load the 4dview module or the ndview +module. For 4dview, you must have started Geomview +in the same directory as the object. For ndview, +initialize a set of windows with the prefab menu, and load the +object through Geomview. The 4dview module includes an +option for slicing along any hyperplane. If you do this in the x, +y, or z plane, you'll see the inside of a hypercube.

    + +

    The 'Gh' option prints the +geometric intersections between adjacent facets. Note the strong +convexity constraint for post-merging ('C0.1'). +It deletes the small facets.

    + +

    »rbox 20 D3 | qdelaunay G +>eg.31.4d.delaunay

    + +

    The Delaunay triangulation of 3-d sites corresponds to a 4-d +convex hull. You can't see 4-d directly but each facet is a 3-d +object that you can project to 3-d. This is exactly the same as +projecting a 2-d facet of a soccer ball onto a plane.

    + +

    Here we see all of the facets together. You can use Geomview's +ndview to look at the object from several directions. +Try turning on edges in the appearance menu. You'll notice that +some edges seem to disappear. That's because the object is +actually two sets of overlapping facets.

    + +

    You can slice the object apart using Geomview's 4dview. +With 4dview, try slicing along the w axis to get a +single set of facets and then slice along the x axis to look +inside. Another interesting picture is to slice away the equator +in the w dimension.

    + +

    »rbox 30 s D4 | qconvex s G +Pd0d1d2D3

    + +

    This is the positive octant of the convex hull of 30 4-d +points. When looking at 4-d, it's easier to look at just a few +facets at once. If you picked a facet that was directly above +you, then that facet looks exactly the same in 3-d as it looks in +4-d. If you pick several facets, then you need to imagine that +the space of a facet is rotated relative to its neighbors. Try +Geomview's ndview on this example.

    + +

    »Halfspace intersections

    + +

    »rbox 10 r s Z1 G0.3 | +qconvex G >eg.33a.cone

    + +

    »rbox 10 r s Z1 G0.3 | +qconvex FV n | qhalf G >eg.33b.cone.dual

    + +

    »rbox 10 r s Z1 G0.3 | +qconvex FV n | qhalf Fp | qconvex G >eg.33c.cone.halfspace

    + +

    These examples illustrate halfspace intersection. The first +picture is the convex hull of two 20-gons plus an apex. The +second picture is the dual of the first. Try loading both +at once. Each vertex of the second picture corresponds to a facet +or halfspace of the first. The vertices with four edges +correspond to a facet with four neighbors. Similarly the facets +correspond to vertices. A facet's normal coefficients divided by +its negative offset is the vertice's coordinates. The coordinates +are the intersection of the original halfspaces.

    + +

    The third picture shows how Qhull can go back and forth +between equivalent representations. It starts with a cone, +generates the halfspaces that define each facet of the cone, and +then intersects these halfspaces. Except for roundoff error, the +third picture is a duplicate of the first.

    + +
    + +

    Up: Home +page for Qhull
    +Up: Qhull manual: Table of Contents
    +To: Programs +• Options +• Output +• Formats +• Geomview +• Print +• Qhull +• Precision +• Trace +• Functions
    +To: Qhull examples: Table of Contents (please wait +while loading)
    + +


    + +

    The Geometry Center +Home Page

    + +

    Comments to: qhull@qhull.org +
    +Created: Sept. 25, 1995 --- Last modified: see top

    + + diff --git a/xs/src/qhull/html/qh-faq.htm b/xs/src/qhull/html/qh-faq.htm new file mode 100644 index 000000000..feda544a7 --- /dev/null +++ b/xs/src/qhull/html/qh-faq.htm @@ -0,0 +1,1547 @@ + + + + + + +Qhull FAQ + + + + + +

    Up: Home page for Qhull +(http://www.qhull.org)
    +Up: Qhull manual: Table of Contents
    +To: Programs +• Options +• Output +• Formats +• Geomview +• Print +• Qhull +• Precision +• Trace +• Functions
    +To: FAQ: Table of Contents (please +wait while loading)
    + +


    + +

    [4-d cube] Frequently Asked Questions about Qhull

    +

    If your question does not appear here, see:

    + + + +

    Qhull is a general dimension code for computing convex hulls, +Delaunay triangulations, halfspace intersections about a point, +Voronoi diagrams, furthest-site Delaunay triangulations, and +furthest-site Voronoi diagrams. These structures have +applications in science, engineering, statistics, and +mathematics. For a detailed introduction, see O'Rourke ['94], Computational Geometry in C. +

    + +

    There are separate programs for each application of +Qhull. These programs disable experimental and inappropriate +options. If you prefer, you may use Qhull directly. All programs +run the same code. + +

    Version 3.1 added triangulated output ('Qt'). +It should be used for Delaunay triangulations instead of +using joggled input ('QJ'). + +

    Brad Barber, Arlington MA, +2010/01/09

    + +

    Copyright © 1998-2015 C.B. Barber

    + +
    + +

    »FAQ: Table of Contents

    + +

    Within each category, the most recently asked questions are +first. +

      +
    • Startup questions
        +
      • How do I run Qhull from Windows? +
      • How do I enter points for Qhull? +
      • How do I learn to use Qhull?
      • +
      +
    • Convex hull questions
        +
      • How do I report just the area and volume of a + convex hull? +
      • Why are there extra points in a 4-d or higher + convex hull? +
      • How do I report duplicate + vertices?
      • +
      +
    • Delaunay triangulation questions
        +
      • How do I get rid of nearly flat Delaunay + triangles? +
      • How do I find the Delaunay triangle or Voronoi + region that is closest to a point? + +
      • How do I compute the Delaunay triangulation of a + non-convex object? + +
      • How do I mesh a volume from a set of triangulated + surface points? + +
      • Can Qhull produce a triangular mesh for an + object? + +
      • For 3-d Delaunay triangulations, how do I + report the triangles of each tetrahedron? + +
      • How do I construct a 3-d Delaunay triangulation? +
      • How do I get the triangles for a 2-d Delaunay + triangulation and the vertices of its Voronoi diagram? +
      • Can Qhull triangulate a + hundred 16-d points?
      • +
      + +
    • Voronoi diagram questions
        +
      • See also "Delaunay diagram questions". Qhull computes the Voronoi diagram from the Delaunay triagulation. +
      • How do I compute the volume of a Voronoi region? +
      • How do I get the radii of the empty + spheres for each Voronoi vertex? + +
      • What is the Voronoi diagram of a square? + +
      • How do I construct the Voronoi diagram of + cospherical points? +
      • Can Qhull compute the unbounded rays of the + Voronoi diagram? +
      +
    • Approximation questions
        +
      • How do I approximate data + with a simplex?
      • +
      +
    • Halfspace questions
        +
      • How do I compute the + intersection of halfspaces with Qhull?
      • +
      +
    • Qhull library questions
        +
      • Is Qhull available for Mathematica, Matlab, or + Maple? + +
      • Why are there too few ridges? +
      • Can Qhull use coordinates without placing them in + a data file? +
      • How large are Qhull's data structures? +
      • Can Qhull construct convex hulls and Delaunay + triangulations one point at a time? +
      • How do I visit the ridges of a Delaunay + triangulation? +
      • How do I visit the Delaunay facets? +
      • When is a point outside or inside a facet? +
      • How do I find the facet that is closest to a + point? +
      • How do I find the Delaunay triangle or Voronoi + region that is closest to a point? +
      • How do I list the vertices? +
      • How do I test code that uses the Qhull library? +
      • When I compute a plane + equation from a facet, I sometimes get an + outward-pointing normal and sometimes an + inward-pointing normal
      • +
      +
    • +
    + +
    + +

    »Startup questions

    + +

    »How do I run Qhull +from Windows?

    + +

    Qhull is a console program. You will first need a command window +(i.e., a "command prompt"). You can double click on +'eg\Qhull-go.bat'.

    + +
      +
    • Type 'qconvex', 'qdelaunay', 'qhalf', 'qvoronoi, + 'qhull', and 'rbox' for a synopsis of each program. + +
    • Type 'rbox c D2 | qconvex s i' to compute the + convex hull of a square. + +
    • Type 'rbox c D2 | qconvex s i TO results.txt' to + write the results to the file 'results.txt'. A summary is still printed on + the the console. + +
    • Type 'rbox c D2' to see the input format for + qconvex. + +
    • Type 'qconvex < data.txt s i TO results.txt' to + read input data from 'data.txt'. + +
    • If you want to enter data by hand, type 'qconvex s i TO + results.txt' to read input data from the console. Type in + the numbers and end with a ctrl-D.
    • +
    + +

    »How do I enter +points for Qhull?

    + +

    Qhull takes its data from standard input. For example, create +a file named 'data.txt' with the following contents:

    + +
    +
    +2  #sample 2-d input
    +5  #number of points
    +1 2  #coordinates of points
    +-1.1 3
    +3 2.2
    +4 5
    +-10 -10
    +
    +
    + +

    Then call qconvex with 'qconvex < data.txt'. It will print a +summary of the convex hull. Use 'qconvex < data.txt o' to print +the vertices and edges. See also input +format.

    + +

    You can generate sample data with rbox, e.g., 'rbox 10' +generates 10 random points in 3-d. Use a pipe ('|') to run rbox +and qhull together, e.g.,

    + +
    +

    rbox c | qconvex o

    +
    + +

    computes the convex hull of a cube.

    + +

    »How do I learn to +use Qhull?

    + +

    First read:

    + + + +

    Look at Qhull's on-line documentation:

    + +
      +
    • 'qconvex' gives a synopsis of qconvex and its options + +
    • 'rbox' lists all of the options for generating point + sets +
    • 'qconvex - | more' lists the options for qconvex +
    • 'qconvex .' gives a concise list of options +
    • 'qdelaunay', 'qhalf', 'qvoronoi', and 'qhull' also have a synopsis and option list
    • +
    + +

    Then try out the Qhull programs on small examples.

    + +
      +
    • 'rbox c' lists the vertices of a cube +
    • 'rbox c | qconvex' is the convex hull of a cube +
    • 'rbox c | qconvex o' lists the vertices and facets of + a cube +
    • 'rbox c | qconvex Qt o' triangulates the cube +
    • 'rbox c | qconvex QJ o' joggles the input and + triangulates the cube +
    • 'rbox c D2 | qconvex' generates the convex hull of a + square +
    • 'rbox c D4 | qconvex' generates the convex hull of a + hypercube +
    • 'rbox 6 s D2 | qconvex p Fx' lists 6 random points in + a circle and lists the vertices of their convex hull in order +
    • 'rbox c D2 c G2 | qdelaunay' computes the Delaunay + triangulation of two embedded squares. It merges the cospherical facets. +
    • 'rbox c D2 c G2 | qdelaunay Qt' computes the Delaunay + triangulation of two embedded squares. It triangulates the cospherical facets. +
    • 'rbox c D2 c G2 | qvoronoi o' computes the + corresponding Voronoi vertices and regions. +
    • 'rbox c D2 c G2 | qvoronio Fv' shows the Voronoi diagram + for the previous example. Each line is one edge of the + diagram. The first number is 4, the next two numbers list + a pair of input sites, and the last two numbers list the + corresponding pair of Voronoi vertices.
    • +
    + +

    Install Geomview +if you are running SGI Irix, Solaris, SunOS, Linux, HP, IBM +RS/6000, DEC Alpha, or Next. You can then visualize the output of +Qhull. Qhull comes with Geomview examples. +

    + +

    Then try Qhull with a small example of your application. Work +out the results by hand. Then experiment with Qhull's options to +find the ones that you need.

    + +

    You will need to decide how Qhull should handle precision +problems. It can triangulate the output ('Qt'), joggle the input ('QJ'), or merge facets (the default).

    + +
      +
    • With joggle, Qhull produces simplicial (i.e., + triangular) output by joggling the input. After joggle, + no points are cocircular or cospherical. +
    • With facet merging, Qhull produces a better + approximation and does not modify the input. +
    • With triangulated output, Qhull merges facets and triangulates + the result.
    • +
    • See Merged facets or joggled input.
    • +
    + +
    +

    »Convex hull questions

    + +

    »How do I report just the area + and volume of a convex hull?

    + +Use option 'FS'. For example, + +
    +C:\qhull>rbox 10 | qconvex FS
    +0
    +2 2.192915621644613 0.2027867899638665
    +
    +C:\qhull>rbox 10 | qconvex FA
    +
    +Convex hull of 10 points in 3-d:
    +
    +  Number of vertices: 10
    +  Number of facets: 16
    +
    +Statistics for: RBOX 10 | QCONVEX FA
    +
    +  Number of points processed: 10
    +  Number of hyperplanes created: 28
    +  Number of distance tests for qhull: 44
    +  CPU seconds to compute hull (after input):  0
    +  Total facet area:   2.1929156
    +  Total volume:       0.20278679
    +
    + +

    »Why are there extra +points in a 4-d or higher convex hull?

    + +

    You may see extra points if you use options 'i' or 'Ft' + without using triangulated output ('Qt'). +The extra points occur when a facet is non-simplicial (i.e., a +facet with more than d vertices). For example, Qhull +reports the following for one facet of the convex hull of a hypercube. +Option 'Pd0:0.5' returns the facet along the positive-x axis:

    + +
    +
    +rbox c D4 | qconvex i Pd0:0.5
    +12
    +17 13 14 15
    +17 13 12 14
    +17 11 13 15
    +17 14 11 15
    +17 10 11 14
    +17 14 12 8
    +17 12 13 8
    +17 10 14 8
    +17 11 10 8
    +17 13 9 8
    +17 9 11 8
    +17 11 9 13
    +
    +
    + +

    The 4-d hypercube has 16 vertices; so point "17" was +added by qconvex. Qhull adds the point in order to report a +simplicial decomposition of the facet. The point corresponds to +the "centrum" which Qhull computes to test for +convexity.

    + +

    Triangulate the output ('Qt') to avoid the extra points. +Since the hypercube is 4-d, each simplicial facet is a tetrahedron. +

    +
    +C:\qhull3.1>rbox c D4 | qconvex i Pd0:0.5 Qt
    +9
    +9 13 14 15
    +12 9 13 14
    +9 11 13 15
    +11 9 14 15
    +9 10 11 14
    +12 9 14 8
    +9 12 13 8
    +9 10 14 8
    +10 9 11 8
    +
    +
    + +

    Use the 'Fv' option to print the +vertices of simplicial and non-simplicial facets. For example, +here is the same hypercube facet with option 'Fv' instead of 'i': +

    + +
    +
    +C:\qhull>rbox c D4 | qconvex Pd0:0.5 Fv
    +1
    +8 9 10 12 11 13 14 15 8
    +
    +
    + +

    The coordinates of the extra point are printed with the 'Ft' option.

    + +
    +
    +rbox c D4 | qconvex Pd0:0.5 Ft
    +4
    +17 12 3
    +  -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.5   -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.5    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.5    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.5
    +   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.5      0      0      0
    +4 16 13 14 15
    +4 16 13 12 14
    +4 16 11 13 15
    +4 16 14 11 15
    +4 16 10 11 14
    +4 16 14 12 8
    +4 16 12 13 8
    +4 16 10 14 8
    +4 16 11 10 8
    +4 16 13 9 8
    +4 16 9 11 8
    +4 16 11 9 13
    +
    +
    + +

    »How do I report +duplicate vertices?

    + +

    There's no direct way. You can use option +'FP' to +report the distance to the nearest vertex for coplanar input +points. Select the minimum distance for a duplicated vertex, and +locate all input sites less than this distance.

    + +

    For Delaunay triangulations, all coplanar points are nearly +incident to a vertex. If you want a report of coincident input +sites, do not use option 'QJ'. By +adding a small random quantity to each input coordinate, it +prevents coincident input sites.

    + +
    +

    »Delaunay triangulation questions

    + +

    »How do I get rid of +nearly flat Delaunay triangles?

    + +

    Nearly flat triangles occur when boundary points are nearly +collinear or coplanar. They also occur for nearly coincident +points. Both events can easily occur when using joggle. For example +(rbox 10 W0 D2 | qdelaunay QJ Fa) lists the areas of the Delaunay +triangles of 10 points on the boundary of a square. Some of +these triangles are nearly flat. This occurs when one point +is joggled inside of two other points. In this case, nearly flat +triangles do not occur with triangulated output (rbox 10 W0 D2 | qdelaunay Qt Fa). + + +

    Another example, (rbox c P0 P0 D2 | qdelaunay QJ Fa), computes the +areas of the Delaunay triangles for the unit square and two +instances of the origin. Four of the triangles have an area +of 0.25 while two have an area of 2.0e-11. The later are due to +the duplicated origin. With triangulated output (rbox c P0 P0 D2 | qdelaunay Qt Fa) +there are four triangles of equal area. + +

    Nearly flat triangles also occur without using joggle. For +example, (rbox c P0 P0,0.4999999999 | qdelaunay Fa), computes +the areas of the Delaunay triangles for the unit square, +a nearly collinear point, and the origin. One triangle has an +area of 3.3e-11. + +

    Unfortunately, none of Qhull's merging options remove nearly +flat Delaunay triangles due to nearly collinear or coplanar boundary +points. +The merging options concern the empty circumsphere +property of Delaunay triangles. This is independent of the area of +the Delaunay triangles. Qhull does handle nearly coincident points. + +

    If you are calling Qhull from a program, you can merge slivers into an adjacent facet. +In d dimensions with simplicial facets (e.g., from 'Qt'), each facet has +d+1 neighbors. Each neighbor shares d vertices of the facet's d+1 vertices. Let the +other vertex be the opposite vertex. For each neighboring facet, if its circumsphere +includes the opposite.vertex, the two facets can be merged. [M. Treacy] + +

    You can handle collinear or coplanar boundary points by +enclosing the points in a box. For example, +(rbox c P0 P0,0.4999999999 c G1 | qdelaunay Fa), surrounds the +previous points with [(1,1), (1,-1), (-1,-1), (-1, 1)]. +Its Delaunay triangulation does not include a +nearly flat triangle. The box also simplifies the graphical +output from Qhull. + +

    Without joggle, Qhull lists coincident points as "coplanar" +points. For example, (rbox c P0 P0 D2 | qdelaunay Fa), ignores +the duplicated origin and lists four triangles of size 0.25. +Use 'Fc' to list the coincident points (e.g., +rbox c P0 P0 D2 | qdelaunay Fc). + +

    There is no easy way to determine coincident points with joggle. +Joggle removes all coincident, cocircular, and cospherical points +before running Qhull. Instead use facet merging (the default) +or triangulated output ('Qt'). + +

    »How do I compute +the Delaunay triangulation of a non-convex object?

    + +

    A similar question is +"How do I mesh a volume from a set of triangulated surface points?" + +

    This is an instance of the constrained Delaunay Triangulation +problem. Qhull does not handle constraints. The boundary of the +Delaunay triangulation is always convex. But if the input set +contains enough points, the triangulation will include the +boundary. The number of points needed depends on the input. + +

    Shewchuk has developed a theory of constrained Delaunay triangulations. +See his +paper at the +1998 Computational Geometry Conference. Using these ideas, constraints +could be added to Qhull. They would have many applications. + +

    There is a large literature on mesh generation and many commercial +offerings. For pointers see +Owen's International Meshing Roundtable +and Schneiders' +Finite Element Mesh Generation page.

    + +

    »Can Qhull +produce a triangular mesh for an object?

    + +

    Yes for convex objects, no for non-convex objects. For +non-convex objects, it triangulates the concavities. Unless the +object has many points on its surface, triangles may cross the +surface.

    + +

    »For 3-d Delaunay +triangulations, how do I report the triangles of each +tetrahedron?

    + +

    For points in general position, a 3-d Delaunay triangulation +generates tetrahedron. Each face of a tetrahedron is a triangle. +For example, the 3-d Delaunay triangulation of random points on +the surface of a cube, is a cellular structure of tetrahedron.

    + +

    Use triangulated output ('qdelaunay Qt i') or joggled input ('qdelaunay QJ i') +to generate the Delaunay triangulation. +Option 'i' reports each tetrahedron. The triangles are +every combination of 3 vertices. Each triangle is a +"ridge" of the Delaunay triangulation.

    + +

    For example,

    + +
    +        rbox 10 | qdelaunay Qt i
    +        14
    +        9 5 8 7
    +        0 9 8 7
    +        5 3 8 7
    +        3 0 8 7
    +        5 4 8 1
    +        4 6 8 1
    +        2 9 5 8
    +        4 2 5 8
    +        4 2 9 5
    +        6 2 4 8
    +        9 2 0 8
    +        2 6 0 8
    +        2 4 9 1
    +        2 6 4 1
    +
    + +

    is the Delaunay triangulation of 10 random points. Ridge 9-5-8 +occurs twice. Once for tetrahedron 9 5 8 7 and the other for +tetrahedron 2 9 5 8.

    + +

    You can also use the Qhull library to generate the triangles. +See "How do I visit the ridges of a +Delaunay triangulation?"

    + +

    »How do I construct a +3-d Delaunay triangulation?

    + +

    For 3-d Delaunay triangulations with cospherical input sites, +use triangulated output ('Qt') or +joggled input ('QJ'). Otherwise +option 'i' will +triangulate non-simplicial facets by adding a point to the facet. + +

    If you want non-simplicial output for cospherical sites, use +option +'Fv' or 'o'. +For option 'o', ignore the last coordinate. It is the lifted +coordinate for the corresponding convex hull in 4-d. + +

    The following example is a cube +inside a tetrahedron. The 8-vertex facet is the cube. Ignore the +last coordinates.

    + +
    +
    +C:\qhull>rbox r y c G0.1 | qdelaunay Fv
    +4
    +12 20 44
    +   0.5      0      0 0.3055555555555555
    +   0    0.5      0 0.3055555555555555
    +   0      0    0.5 0.3055555555555555
    +  -0.5   -0.5   -0.5 0.9999999999999999
    +  -0.1   -0.1   -0.1 -6.938893903907228e-018
    +  -0.1   -0.1    0.1 -6.938893903907228e-018
    +  -0.1    0.1   -0.1 -6.938893903907228e-018
    +  -0.1    0.1    0.1 -6.938893903907228e-018
    +   0.1   -0.1   -0.1 -6.938893903907228e-018
    +   0.1   -0.1    0.1 -6.938893903907228e-018
    +   0.1    0.1   -0.1 -6.938893903907228e-018
    +   0.1    0.1    0.1 -6.938893903907228e-018
    +4 2 11 1 0
    +4 10 1 0 3
    +4 11 10 1 0
    +4 2 9 0 3
    +4 9 11 2 0
    +4 7 2 1 3
    +4 11 7 2 1
    +4 8 10 0 3
    +4 9 8 0 3
    +5 8 9 10 11 0
    +4 10 6 1 3
    +4 6 7 1 3
    +5 6 8 10 4 3
    +5 6 7 10 11 1
    +4 5 9 2 3
    +4 7 5 2 3
    +5 5 8 9 4 3
    +5 5 6 7 4 3
    +8 5 6 8 7 9 10 11 4
    +5 5 7 9 11 2
    +
    +
    + +

    If you want simplicial output use options +'Qt i' or +'QJ i', e.g., +

    + +
    +
    +rbox r y c G0.1 | qdelaunay Qt i
    +31
    +2 11 1 0
    +11 10 1 0
    +9 11 2 0
    +11 7 2 1
    +8 10 0 3
    +9 8 0 3
    +10 6 1 3
    +6 7 1 3
    +5 9 2 3
    +7 5 2 3
    +9 8 10 11
    +8 10 11 0
    +9 8 11 0
    +6 8 10 4
    +8 6 10 3
    +6 8 4 3
    +6 7 10 11
    +10 6 11 1
    +6 7 11 1
    +8 5 4 3
    +5 8 9 3
    +5 6 4 3
    +6 5 7 3
    +5 9 10 11
    +8 5 9 10
    +7 5 10 11
    +5 6 7 10
    +8 5 10 4
    +5 6 10 4
    +5 9 11 2
    +7 5 11 2
    +
    +
    + +

    »How do I get the +triangles for a 2-d Delaunay triangulation and the vertices of +its Voronoi diagram?

    + +

    To compute the Delaunay triangles indexed by the indices of +the input sites, use

    + +
    +

    rbox 10 D2 | qdelaunay Qt i

    +
    + +

    To compute the Voronoi vertices and the Voronoi region for +each input site, use

    + +
    +

    rbox 10 D2 | qvoronoi o

    +
    + +

    To compute each edge ("ridge") of the Voronoi +diagram for each pair of adjacent input sites, use

    + +
    +

    rbox 10 D2 | qvoronoi Fv

    +
    + +

    To compute the area and volume of the Voronoi region for input site 5 (site 0 is the first one), +use

    + +
    +

    rbox 10 D2 | qvoronoi QV5 p | qconvex s FS

    +
    + +

    To compute the lines ("hyperplanes") that define the +Voronoi region for input site 5, use

    + +
    +

    rbox 10 D2 | qvoronoi QV5 p | qconvex n

    +
    +or +
    +

    rbox 10 D2 | qvoronoi QV5 Fi Fo

    +
    + +

    To list the extreme points of the input sites use

    + +
    +

    rbox 10 D2 | qdelaunay Fx

    +
    + +

    You will get the same point ids with

    + +
    +

    rbox 10 D2 | qconvex Fx

    +
    + +

    »Can Qhull triangulate +a hundred 16-d points?

    + +

    No. This is an immense structure. A triangulation of 19, 16-d +points has 43 simplices. If you add one point at a time, the +triangulation increased as follows: 43, 189, 523, 1289, 2830, +6071, 11410, 20487. The last triangulation for 26 points used 13 +megabytes of memory. When Qhull uses virtual memory, it becomes +too slow to use.

    + +
    +

    »Voronoi +diagram questions

    + +

    »How do I compute the volume of a Voronoi region?

    + +

    For each Voronoi region, compute the convex hull of the region's Voronoi vertices. The volume of each convex hull is the volume +of the corresponding Vornoi region.

    + +

    For example, to compute the volume of the bounded Voronoi region about [0,0,0]: output the origin's Voronoi vertices and +compute the volume of their convex hull. The last number from option 'FS' is the volume.

    +
    +rbox P0 10 | qvoronoi QV0 p | qhull FS
    +0
    +2 1.448134756744281 0.1067973560800857
    +
    + +

    For another example, see How do I get the triangles for a 2-d Delaunay + triangulation and the vertices of its Voronoi diagram?

    + +

    This approach is slow if you are using the command line. A faster approcach is to call Qhull from +a program. The fastest method is Clarkson's hull program. +It computes the volume for all Voronoi regions.

    + +

    An unbounded Voronoi region does not have a volume.

    + +

    »How do I get the radii of the empty + spheres for each Voronoi vertex?

    + +Use option 'Fi' to list each bisector (i.e. Delaunay ridge). Then compute the +minimum distance for each Voronoi vertex. + +

    There's other ways to get the same information. Let me know if you +find a better method. + +

    »What is the Voronoi diagram + of a square?

    + +

    +Consider a square, +

    +C:\qhull>rbox c D2
    +2 RBOX c D2
    +4
    +  -0.5   -0.5
    +  -0.5    0.5
    +   0.5   -0.5
    +   0.5    0.5
    +
    + +

    There's two ways to compute the Voronoi diagram: with facet merging +or with joggle. With facet merging, the +result is: + +

    +C:\qhull>rbox c D2 | qvoronoi Qz
    +
    +Voronoi diagram by the convex hull of 5 points in 3-d:
    +
    +  Number of Voronoi regions and at-infinity: 5
    +  Number of Voronoi vertices: 1
    +  Number of facets in hull: 5
    +
    +Statistics for: RBOX c D2 | QVORONOI Qz
    +
    +  Number of points processed: 5
    +  Number of hyperplanes created: 7
    +  Number of distance tests for qhull: 8
    +  Number of merged facets: 1
    +  Number of distance tests for merging: 29
    +  CPU seconds to compute hull (after input):  0
    +
    +C:\qhull>rbox c D2 | qvoronoi Qz o
    +2
    +2 5 1
    +-10.101 -10.101
    +     0      0
    +2 0 1
    +2 0 1
    +2 0 1
    +2 0 1
    +0
    +
    +C:\qhull>rbox c D2 | qvoronoi Qz Fv
    +4
    +4 0 1 0 1
    +4 0 2 0 1
    +4 1 3 0 1
    +4 2 3 0 1
    +
    + +

    There is one Voronoi vertex at the origin and rays from the origin +along each of the coordinate axes. +The last line '4 2 3 0 1' means that there is +a ray that bisects input points #2 and #3 from infinity (vertex 0) to +the origin (vertex 1). +Option 'Qz' adds an artificial point since the input is cocircular. +Coordinates -10.101 indicate the +vertex at infinity. + +

    With triangulated output, the Voronoi vertex is +duplicated: + +

    +C:\qhull3.1>rbox c D2 | qvoronoi Qt Qz
    +
    +Voronoi diagram by the convex hull of 5 points in 3-d:
    +
    +  Number of Voronoi regions and at-infinity: 5
    +  Number of Voronoi vertices: 2
    +  Number of triangulated facets: 1
    +
    +Statistics for: RBOX c D2 | QVORONOI Qt Qz
    +
    +  Number of points processed: 5
    +  Number of hyperplanes created: 7
    +  Number of facets in hull: 6
    +  Number of distance tests for qhull: 8
    +  Number of distance tests for merging: 33
    +  Number of distance tests for checking: 30
    +  Number of merged facets: 1
    +  CPU seconds to compute hull (after input): 0.05
    +
    +C:\qhull3.1>rbox c D2 | qvoronoi Qt Qz o
    +2
    +3 5 1
    +-10.101 -10.101
    +     0      0
    +     0      0
    +3 2 0 1
    +2 1 0
    +2 2 0
    +3 2 0 1
    +0
    +
    +C:\qhull3.1>rbox c D2 | qvoronoi Qt Qz Fv
    +4
    +4 0 2 0 2
    +4 0 1 0 1
    +4 1 3 0 1
    +4 2 3 0 2
    +
    + + +

    With joggle, the input is no longer cocircular and the Voronoi vertex is +split into two: + +

    +C:\qhull>rbox c D2 | qvoronoi Qt Qz
    +
    +C:\qhull>rbox c D2 | qvoronoi QJ o
    +2
    +3 4 1
    +-10.101 -10.101
    +-4.71511718558304e-012 -1.775812830118184e-011
    +9.020340030474472e-012 -4.02267108512433e-012
    +2 0 1
    +3 2 1 0
    +3 2 0 1
    +2 2 0
    +
    +C:\qhull>rbox c D2 | qvoronoi QJ Fv
    +5
    +4 0 2 0 1
    +4 0 1 0 1
    +4 1 2 1 2
    +4 1 3 0 2
    +4 2 3 0 2
    +
    + +

    Note that the Voronoi diagram includes the same rays as + before plus a short edge between the two vertices.

    + + +

    »How do I construct +the Voronoi diagram of cospherical points?

    + +

    Three-d terrain data can be approximated with cospherical +points. The Delaunay triangulation of cospherical points is the +same as their convex hull. If the points lie on the unit sphere, +the facet normals are the Voronoi vertices [via S. Fortune].

    + +

    For example, consider the points {[1,0,0], [-1,0,0], [0,1,0], +...}. Their convex hull is:

    + +
    +rbox d G1 | qconvex o
    +3
    +6 8 12
    +     0      0     -1
    +     0      0      1
    +     0     -1      0
    +     0      1      0
    +    -1      0      0
    +     1      0      0
    +3 3 1 4
    +3 1 3 5
    +3 0 3 4
    +3 3 0 5
    +3 2 1 5
    +3 1 2 4
    +3 2 0 4
    +3 0 2 5
    +
    + +

    The facet normals are:

    + +
    +rbox d G1 | qconvex n
    +4
    +8
    +-0.5773502691896258  0.5773502691896258  0.5773502691896258 -0.5773502691896258
    + 0.5773502691896258  0.5773502691896258  0.5773502691896258 -0.5773502691896258
    +-0.5773502691896258  0.5773502691896258 -0.5773502691896258 -0.5773502691896258
    + 0.5773502691896258  0.5773502691896258 -0.5773502691896258 -0.5773502691896258
    + 0.5773502691896258 -0.5773502691896258  0.5773502691896258 -0.5773502691896258
    +-0.5773502691896258 -0.5773502691896258  0.5773502691896258 -0.5773502691896258
    +-0.5773502691896258 -0.5773502691896258 -0.5773502691896258 -0.5773502691896258
    + 0.5773502691896258 -0.5773502691896258 -0.5773502691896258 -0.5773502691896258
    +
    + +

    If you drop the offset from each line (the last number), each +line is the Voronoi vertex for the corresponding facet. The +neighboring facets for each point define the Voronoi region for +each point. For example:

    + +
    +rbox d G1 | qconvex FN
    +6
    +4 7 3 2 6
    +4 5 0 1 4
    +4 7 4 5 6
    +4 3 1 0 2
    +4 6 2 0 5
    +4 7 3 1 4
    +
    + +

    The Voronoi vertices {7, 3, 2, 6} define the Voronoi region +for point 0. Point 0 is [0,0,-1]. Its Voronoi vertices are

    + +
    +-0.5773502691896258  0.5773502691896258 -0.5773502691896258
    + 0.5773502691896258  0.5773502691896258 -0.5773502691896258
    +-0.5773502691896258 -0.5773502691896258 -0.5773502691896258
    + 0.5773502691896258 -0.5773502691896258 -0.5773502691896258
    +
    + +

    In this case, the Voronoi vertices are oriented, but in +general they are unordered.

    + +

    By taking the dual of the Delaunay triangulation, you can +construct the Voronoi diagram. For cospherical points, the convex +hull vertices for each facet, define the input sites for each +Voronoi vertex. In 3-d, the input sites are oriented. For +example:

    + +
    +rbox d G1 | qconvex i
    +8
    +3 1 4
    +1 3 5
    +0 3 4
    +3 0 5
    +2 1 5
    +1 2 4
    +2 0 4
    +0 2 5
    +
    + +

    The convex hull vertices for facet 0 are {3, 1, 4}. So Voronoi +vertex 0 (i.e., [-0.577, 0.577, 0.577]) is the Voronoi vertex for +input sites {3, 1, 4} (i.e., {[0,1,0], [0,0,1], [-1,0,0]}).

    + +

    »Can Qhull compute the +unbounded rays of the Voronoi diagram?

    + +

    Use 'Fo' to compute the separating +hyperplanes for unbounded Voronoi regions. The corresponding ray +goes to infinity from the Voronoi vertices. If you enclose the +input sites in a large enough box, the outermost bounded regions +will represent the unbounded regions of the original points.

    + +

    If you do not box the input sites, you can identify the +unbounded regions. They list '0' as a vertex. Vertex 0 represents +"infinity". Each unbounded ray includes vertex 0 in +option 'Fv. See Voronoi graphics and Voronoi notes.

    + +
    +

    »Approximation questions

    + +

    »How do I +approximate data with a simplex

    + +

    Qhull may be used to help select a simplex that approximates a +data set. It will take experimentation. Geomview will help to +visualize the results. This task may be difficult to do in 5-d +and higher. Use rbox options 'x' and 'y' to produce random +distributions within a simplex. Your methods work if you can +recover the simplex.

    + +

    Use Qhull's precision options to get a first approximation to +the hull, say with 10 to 50 facets. For example, try 'C0.05' to +remove small facets after constructing the hull. Use 'W0.05' to +ignore points within 0.05 of a facet. Use 'PA5' to print the five +largest facets by area.

    + +

    Then use other methods to fit a simplex to this data. Remove +outlying vertices with few nearby points. Look for large facets +in different quadrants. You can use option 'Pd0d1d2' to print all +the facets in a quadrant.

    + +

    In 4-d and higher, use the outer planes (option 'Fo' or +'facet->maxoutside') since the hyperplane of an approximate +facet may be below many of the input points.

    + +

    For example, consider fitting a cube to 1000 uniformly random +points in the unit cube. In this case, the first try was good:

    + +
    +
    +rbox 1000 | qconvex W0.05 C0.05 PA6 Fo
    +4
    +6
    +0.35715408374381 0.08706467018177928 -0.9299788727015564 -0.5985514741284483
    +0.995841591359023 -0.02512604712761577 0.08756829720435189 -0.5258834069202866
    +0.02448099521570909 -0.02685210459017302 0.9993396046151313 -0.5158104982631999
    +-0.9990223929415094 -0.01261133513150079 0.04236994958247349 -0.509218270408407
    +-0.0128069014364698 -0.9998380680115362 0.01264203427283151 -0.5002512653670584
    +0.01120895057872914 0.01803671994177704 -0.9997744926535512 -0.5056824072956361
    +
    +
    + +
    +

    »Halfspace questions

    + +

    »How do I compute the + intersection of halfspaces with Qhull?

    + +

    Qhull computes the halfspace intersection about a point. The +point must be inside all of the halfspaces. Given a point, a +duality turns a halfspace intersection problem into a convex +hull problem. + +

    Use linear programming if you +do not know a point in the interior of the halfspaces. +See the notes for qhalf. You will need + a linear programming code. This may require a fair amount of work to + implement.

    + + + +
    +

    »Qhull library +questions

    + +

    »Is Qhull available for Mathematica, Matlab, or Maple?

    + +

    MATLAB + +

    Z. You of MathWorks added qhull to MATLAB 6. +See functions convhulln, + delaunayn, + griddata3, + griddatan, + tsearch, + tsearchn, and + voronoin. V. Brumberg update MATLAB R14 for Qhull 2003.1 and triangulated output. + +

    Engwirda wrote mesh2d for unstructured mesh generation in MATLAB. +It is based on the iterative method of Persson and generally results in better quality meshes than delaunay refinement. + + +

    Mathematica and Maple + +

    See qh-math +for a Delaunay interface to Mathematica. It includes projects for CodeWarrior +on the Macintosh and Visual C++ on Win32 PCs. + +

    See Mathematica ('m') and Maple ('FM') output options. + +

    +

    »Why are there too few ridges?

    + +The following sample code may produce fewer ridges than expected: + +
    +  facetT *facetp;
    +  ridgeT *ridge, **ridgep;
    +
    +  FORALLfacets {
    +    printf("facet f%d\n", facet->id);
    +    FOREACHridge_(facet->ridges) {
    +      printf("   ridge r%d between f%d and f%d\n", ridge->id, ridge->top->id, ridge->bottom->id);
    +    }
    +  }
    +
    + +

    Qhull does not create ridges for simplicial facets. +Instead it computes ridges from facet->neighbors. To make ridges for a +simplicial facet, use qh_makeridges() in merge.c. Usefacet->visit_id to visit +each ridge once (instead of twice). For example, + +

    +  facetT *facet, *neighbor;
    +  ridgeT *ridge, **ridgep;
    +
    +  qh visit_id++;
    +  FORALLfacets {
    +    printf("facet f%d\n", facet->id);
    +    qh_makeridges(facet);
    +    facet->visitId= qh visit_id;
    +    FOREACHridge_(facet->ridges) {
    +        neighbor= otherfacet_(ridge, visible);
    +        if (neighbor->visitid != qh visit_id)
    +            printf("   ridge r%d between f%d and f%d\n", ridge->id, ridge->top->id, ridge->bottom->id);
    +    }
    +  }
    +
    + +

    »Can Qhull use coordinates without placing + them in a data file?

    + +

    You may call Qhull from a program. Please use the reentrant Qhull library (libqhullstatic_r.a, libqhull_r.so, or qhull_r.dll). + +See user_eg.c and "Qhull-template" in user_r.c for examples.. + +See Qhull internals for an introduction to Qhull's reentrant library and its C++ interface. + +

    Hint: Start with a small example for which you know the + answer.

    + +

    »How large are Qhull's data structures?

    + +

    Qhull uses a general-dimension data structure. +The size depends on the dimension. Use option 'Ts' to print +out the memory statistics [e.g., 'rbox D2 10 | qconvex Ts']. + + +

    »Can Qhull construct +convex hulls and Delaunay triangulations one point at a time?

    + +

    The Qhull library may be used to construct convex hulls and +Delaunay triangulations one point at a time. It may not be used +for deleting points or moving points.

    + +

    Qhull is designed for batch processing. Neither Clarkson's +randomized incremental algorithm nor Qhull are designed for +on-line operation. For many applications, it is better to +reconstruct the convex hull or Delaunay triangulation from +scratch for each new point.

    + +

    With random point sets and on-line processing, Clarkson's +algorithm should run faster than Qhull. Clarkson uses the +intermediate facets to reject new, interior points, while Qhull, +when used on-line, visits every facet to reject such points. If +used on-line for n points, Clarkson may take O(n) times as much +memory as the average off-line case, while Qhull's space +requirement does not change.

    + +

    If you triangulate the output before adding all the points +(option 'Qt' and procedure qh_triangulate), you must set +option 'Q11'. It duplicates the +normals of triangulated facets and recomputes the centrums. +This should be avoided for regular use since triangulated facets +are not clearly convex with their neighbors. It appears to +work most of the time, but fails for cases that Qhull normally +handles well [see the test call to qh_triangulate in qh_addpoint]. + +

    »How do I visit the +ridges of a Delaunay triangulation?

    + +

    To visit the ridges of a Delaunay triangulation, visit each +facet. Each ridge will appear twice since it belongs to two +facets. In pseudo-code:

    + +
    +    for each facet of the triangulation
    +        if the facet is Delaunay (i.e., part of the lower convex hull)
    +            for each ridge of the facet
    +                if the ridge's neighboring facet has not been visited
    +                    ... process a ridge of the Delaunay triangulation ...
    +
    + +

    In undebugged, C code:

    + +
    +    qh visit_id++;
    +    FORALLfacets_(facetlist)
    +        if (!facet->upperdelaunay) {
    +            facet->visitid= qh visit_id;
    +            qh_makeridges(facet);
    +            FOREACHridge_(facet->ridges) {
    +                neighbor= otherfacet_(ridge, facet);
    +                if (neighbor->visitid != qh visit_id) {
    +                    /* Print ridge here with facet-id and neighbor-id */
    +                    /*fprintf(fp, "f%d\tf%d\t",facet->id,neighbor->ID);*/
    +                    FOREACHvertex_(ridge->vertices)
    +                        fprintf(fp,"%d ",qh_pointid (vertex->point) );
    +                    qh_printfacetNvertex_simplicial (fp, facet, format);
    +                    fprintf(fp," ");
    +                    if(neighbor->upperdelaunay)
    +                        fprintf(fp," -1 -1 -1 -1 ");
    +                    else
    +                        qh_printfacetNvertex_simplicial (fp, neighbor, format);
    +                    fprintf(fp,"\n");
    +                }
    +            }
    +        }
    +    }
    +
    + +

    Qhull should be redesigned as a class library, or at least as +an API. It currently provides everything needed, but the +programmer has to do a lot of work. Hopefully someone will write +C++ wrapper classes or a Python module for Qhull.

    + +

    »How do I visit the +Delaunay regions?

    + +

    Qhull constructs a Delaunay triangulation by lifting the +input sites to a paraboloid. The Delaunay triangulation +corresponds to the lower convex hull of the lifted points. To +visit each facet of the lower convex hull, use:

    + +
    +    facetT *facet;
    +
    +    ...
    +    FORALLfacets {
    +        if (!facet->upperdelaunay) {
    +            ... only facets for Delaunay regions ...
    +        }
    +    }
    +
    + +

    »When is a point +outside or inside a facet?

    + +

    A point is outside of a facet if it is clearly outside the +facet's outer plane. The outer plane is defined by an offset +(facet->maxoutside) from the facet's hyperplane.

    + +
    +    facetT *facet;
    +    pointT *point;
    +    realT dist;
    +
    +    ...
    +    qh_distplane(point, facet, &dist);
    +    if (dist > facet->maxoutside + 2 * qh DISTround) {
    +        /* point is clearly outside of facet */
    +    }
    +
    + +

    A point is inside of a facet if it is clearly inside the +facet's inner plane. The inner plane is computed as the maximum +distance of a vertex to the facet. It may be computed for an +individual facet, or you may use the maximum over all facets. For +example:

    + +
    +    facetT *facet;
    +    pointT *point;
    +    realT dist;
    +
    +    ...
    +    qh_distplane(point, facet, &dist);
    +    if (dist < qh min_vertex - 2 * qh DISTround) {
    +        /* point is clearly inside of facet */
    +    }
    +
    + +

    Both tests include two qh.DISTrounds because the computation +of the furthest point from a facet may be off by qh.DISTround and +the computation of the current distance to the facet may be off +by qh.DISTround.

    + +

    »How do I find the +facet that is closest to a point?

    + +

    Use qh_findbestfacet(). For example,

    + +
    +    coordT point[ DIM ];
    +    boolT isoutside;
    +    realT bestdist;
    +    facetT *facet;
    +
    +    ... set coordinates for point ...
    +
    +    facet= qh_findbestfacet (point, qh_ALL, &bestdist, &isoutside);
    +
    +    /* 'facet' is the closest facet to 'point' */
    +
    + +

    qh_findbestfacet() performs a directed search for the facet +furthest below the point. If the point lies inside this facet, +qh_findbestfacet() performs an exhaustive search of all facets. +An exhaustive search may be needed because a facet on the far +side of a lens-shaped distribution may be closer to a point than +all of the facet's neighbors. The exhaustive search may be +skipped for spherical distributions.

    + +

    Also see, "How do I find the +Delaunay triangle that is closest to a +point?"

    + +

    »How do I find the +Delaunay triangle or Voronoi region that is closest to a point?

    + +

    A Delaunay triangulation subdivides the plane, or in general +dimension, subdivides space. Given a point, how do you determine +the subdivision containing the point? Or, given a set of points, +how do you determine the subdivision containing each point of the set? +Efficiency is important -- an exhaustive search of the subdivision +is too slow. + +

    First compute the Delaunay triangle with qh_new_qhull() in user_r.c or Qhull::runQhull(). +Lift the point to the paraboloid by summing the squares of the +coordinates. Use qh_findbestfacet() [poly2.c] to find the closest Delaunay +triangle. Determine the closest vertex to find the corresponding +Voronoi region. Do not use options +'Qbb', 'QbB', +'Qbk:n', or 'QBk:n' since these scale the last +coordinate. Optimizations of qh_findbestfacet() should +be possible for Delaunay triangulations.

    + +

    You first need to lift the point to the paraboloid (i.e., the +last coordinate is the sum of the squares of the point's coordinates). +The +routine, qh_setdelaunay() [geom2.c], lifts an array of points to the +paraboloid. The following excerpt is from findclosest() in +user_eg.c.

    + +
    +    coordT point[ DIM + 1];  /* one extra coordinate for lifting the point */
    +    boolT isoutside;
    +    realT bestdist;
    +    facetT *facet;
    +
    +    ... set coordinates for point[] ...
    +
    +    qh_setdelaunay (DIM+1, 1, point);
    +    facet= qh_findbestfacet (point, qh_ALL, &bestdist, &isoutside);
    +    /* 'facet' is the closest Delaunay triangle to 'point' */
    +
    + +

    The returned facet either contains the point or it is the +closest Delaunay triangle along the convex hull of the input set. + +

    Point location is an active research area in Computational +Geometry. For a practical approach, see Mucke, et al, "Fast randomized +point location without preprocessing in two- and +three-dimensional Delaunay triangulations," Computational +Geometry '96, p. 274-283, May 1996. +For an introduction to planar point location see [O'Rourke '93]. +Also see, "How do I find the facet that is closest to a +point?"

    + +

    To locate the closest Voronoi region, determine the closest +vertex of the closest Delaunay triangle.

    + +
    +    realT dist, bestdist= REALmax;
    +        vertexT *bestvertex= NULL, *vertex, **vertexp;
    +
    +    /* 'facet' is the closest Delaunay triangle to 'point' */
    +
    +    FOREACHvertex_( facet->vertices ) {
    +        dist= qh_pointdist( point, vertex->point, DIM );
    +        if (dist < bestdist) {
    +            bestdist= dist;
    +            bestvertex= vertex;
    +        }
    +    }
    +    /* 'bestvertex' represents the Voronoi region closest to 'point'.  The corresponding
    +       input site is 'bestvertex->point' */
    +
    + +

    »How do I list the +vertices?

    + +

    To list the vertices (i.e., extreme points) of the convex hull +use

    + +
    +
    +    vertexT *vertex;
    +
    +    FORALLvertices {
    +      ...
    +      // vertex->point is the coordinates of the vertex
    +      // qh_pointid(vertex->point) is the point ID of the vertex
    +      ...
    +    }
    +    
    +
    + +

    »How do I test code +that uses the Qhull library?

    + +

    Compare the output from your program with the output from the +Qhull program. Use option 'T1' or 'T4' to trace what Qhull is +doing. Prepare a small example for which you know the +output. Run the example through the Qhull program and your code. +Compare the trace outputs. If you do everything right, the two +trace outputs should be almost the same. The trace output will +also guide you to the functions that you need to review.

    + +

    »When I compute a +plane equation from a facet, I sometimes get an outward-pointing +normal and sometimes an inward-pointing normal

    + +

    Qhull orients simplicial facets, and prints oriented output +for 'i', 'Ft', and other options. The orientation depends on both +the vertex order and the flag facet->toporient.

    + +

    Qhull does not orient + non-simplicial facets. Instead it orients the facet's ridges. These are + printed with the 'Qt' and 'Ft' option. The facet's hyperplane is oriented.

    + +
    +
    + +

    Up: Home page for Qhull
    +Up: Qhull manual: Table of Contents
    +To: Programs +• Options +• Output +• Formats +• Geomview +• Print +• Qhull +• Precision +• Trace +• Functions
    +To: FAQ: Table of Contents
    + +


    + +

    The Geometry Center +Home Page

    + +

    Comments to: qhull@qhull.org +
    +Created: +Sept. 25, 1995 --- Last modified: see top +

    + + diff --git a/xs/src/qhull/html/qh-get.htm b/xs/src/qhull/html/qh-get.htm new file mode 100644 index 000000000..c39ed2256 --- /dev/null +++ b/xs/src/qhull/html/qh-get.htm @@ -0,0 +1,106 @@ + + + + +Qhull Downloads + + + + +

    Up: Qhull Home Page
    +

    + +
    + +

    [CONE] Qhull Downloads

    + + + +
    + +

    Up: Qhull Home Page
    +

    + +
    + +

    [HOME] The Geometry Center Home Page

    + +

    Comments to: qhull@qhull.org
    + + diff --git a/xs/src/qhull/html/qh-impre.htm b/xs/src/qhull/html/qh-impre.htm new file mode 100644 index 000000000..cfbe0acb8 --- /dev/null +++ b/xs/src/qhull/html/qh-impre.htm @@ -0,0 +1,826 @@ + + + + +Imprecision in Qhull + + + + +

    Up: Home +page for Qhull
    +Up: Qhull manual: Table of +Contents
    +To: Programs +• Options +• Output +• Formats +• Geomview +• Print +• Qhull +• Precision +• Trace +• Functions
    +To: Qhull imprecision: Table of Contents +(please wait while loading) + +


    + +

    [4-d cube] Imprecision in Qhull

    + +

    This section of the Qhull manual discusses the problems caused +by coplanar points and why Qhull uses options 'C-0' or 'Qx' +by default. If you ignore precision issues with option 'Q0', the output from Qhull can be +arbitrarily bad. Qhull avoids precision problems if you merge facets (the default) or joggle +the input ('QJ').

    + +

    Use option 'Tv' to verify the +output from Qhull. It verifies that adjacent facets are clearly +convex. It verifies that all points are on or below all facets.

    + +

    Qhull automatically tests for convexity if it detects +precision errors while constructing the hull.

    + +

    Copyright © 1995-2015 C.B. Barber

    + +
    + +

    »Qhull +imprecision: Table of Contents

    + + + +
    + +

    »Precision problems

    + +

    Since Qhull uses floating point arithmetic, roundoff error +occurs with each calculation. This causes problems for +geometric algorithms. Other floating point codes for convex +hulls, Delaunay triangulations, and Voronoi diagrams also suffer +from these problems. Qhull handles most of them.

    + +

    There are several kinds of precision errors:

    + +
      +
    • Representation error occurs when there are not enough + digits to represent a number, e.g., 1/3.
    • +
    • Measurement error occurs when the input coordinates are + from measurements.
    • +
    • Roundoff error occurs when a calculation is rounded to a + fixed number of digits, e.g., a floating point + calculation.
    • +
    • Approximation error occurs when the user wants an + approximate result because the exact result contains too + much detail.
    • +
    + +

    Under imprecision, calculations may return erroneous results. +For example, roundoff error can turn a small, positive number +into a small, negative number. See Milenkovic ['93] for a discussion of strict +robust geometry. Qhull does not meet Milenkovic's criterion +for accuracy. Qhull's error bound is empirical instead of +theoretical.

    + +

    Qhull 1.0 checked for precision errors but did not handle +them. The output could contain concave facets, facets with +inverted orientation ("flipped" facets), more than two +facets adjacent to a ridge, and two facets with exactly the same +set of vertices.

    + +

    Qhull 2.4 and later automatically handles errors due to +machine round-off. Option 'C-0' or 'Qx' is set by default. In 5-d and +higher, the output is clearly convex but an input point could be +outside of the hull. This may be corrected by using option 'C-0', but then the output may contain +wide facets.

    + +

    Qhull 2.5 and later provides option 'QJ' +to joggled input. Each input coordinate is modified by a +small, random quantity. If a precision error occurs, a larger +modification is tried. When no precision errors occur, Qhull is +done.

    + +

    Qhull 3.1 and later provides option 'Qt' +for triangulated output. This removes the need for +joggled input ('QJ'). +Non-simplicial facets are triangulated. +The facets may have zero area. +Triangulated output is particularly useful for Delaunay triangulations.

    + +

    By handling round-off errors, Qhull can provide a variety of +output formats. For example, it can return the halfspace that +defines each facet ('n'). The +halfspaces include roundoff error. If the halfspaces were exact, +their intersection would return the original extreme points. With +imprecise halfspaces and exact arithmetic, nearly incident points +may be returned for an original extreme point. By handling +roundoff error, Qhull returns one intersection point for each of +the original extreme points. Qhull may split or merge an extreme +point, but this appears to be unlikely.

    + +

    The following pipe implements the identity function for +extreme points (with roundoff): +

    + qconvex FV n | qhalf Fp +
    + +

    Bernd Gartner published his +Miniball +algorithm ["Fast and robust smallest enclosing balls", Algorithms - ESA '99, LNCS 1643]. +It uses floating point arithmetic and a carefully designed primitive operation. +It is practical to 20-D or higher, and identifies at least two points on the +convex hull of the input set. Like Qhull, it is an incremental algorithm that +processes points furthest from the intermediate result and ignores +points that are close to the intermediate result. + +

    »Merged facets or joggled input

    + +

    This section discusses the choice between merged facets and joggled input. +By default, Qhull uses merged facets to handle +precision problems. With option 'QJ', +the input is joggled. See examples +of joggled input and triangulated output. +

      +
    • Use merged facets (the default) +when you want non-simplicial output (e.g., the faces of a cube). +
    • Use merged facets and triangulated output ('Qt') when +you want simplicial output and coplanar facets (e.g., triangles for a Delaunay triangulation). +
    • Use joggled input ('QJ') when you need clearly-convex, +simplicial output. +
    + +

    The choice between merged facets and joggled input depends on +the application. Both run about the same speed. Joggled input may +be faster if the initial joggle is sufficiently large to avoid +precision errors. + +

    Most applications should used merged facets +with triangulated output.

    + +

    Use merged facets (the +default, 'C-0') +or triangulated output ('Qt') if

    + +
      +
    • Your application supports non-simplicial facets, or + it allows degenerate, simplicial facets (option 'Qt').
    • +
    • You do not want the input modified.
    • +
    • You want to set additional options for approximating the + hull.
    • +
    • You use single precision arithmetic (realT). +
    • +
    + +

    Use joggled input ('QJ') if

    + +
      +
    • Your application needs clearly convex, simplicial output
    • +
    • Your application supports perturbed input points and narrow triangles.
    • +
    • Seven significant digits is sufficient accuracy.
    • +
    + +

    You may use both techniques or combine joggle with +post-merging ('Cn').

    + +

    Other researchers have used techniques similar to joggled +input. Sullivan and Beichel [ref?] randomly perturb the input +before computing the Delaunay triangulation. Corkum and Wyllie +[news://comp.graphics, 1990] randomly rotate a polytope before +testing point inclusion. Edelsbrunner and Mucke [Symp. Comp. +Geo., 1988] and Yap [J. Comp. Sys. Sci., 1990] symbolically +perturb the input to remove singularities.

    + +

    Merged facets ('C-0') handles +precision problems directly. If a precision problem occurs, Qhull +merges one of the offending facets into one of its neighbors. +Since all precision problems in Qhull are associated with one or +more facets, Qhull will either fix the problem or attempt to merge the +last remaining facets.

    + +

    »Delaunay +triangulations

    + +

    Programs that use Delaunay triangulations traditionally assume +a triangulated input. By default, qdelaunay +merges regions with cocircular or cospherical input sites. +If you want a simplicial triangulation +use triangulated output ('Qt') or joggled +input ('QJ'). + +

    For Delaunay triangulations, triangulated +output should produce good results. All points are within roundoff error of +a paraboloid. If two points are nearly incident, one will be a +coplanar point. So all points are clearly separated and convex. +If qhull reports deleted vertices, the triangulation +may contain serious precision faults. Deleted vertices are reported +in the summary ('s', 'Fs'

    + +

    You should use option 'Qbb' with Delaunay +triangulations. It scales the last coordinate and may reduce +roundoff error. It is automatically set for qdelaunay, +qvoronoi, and option 'QJ'.

    + +

    Edelsbrunner, H, Geometry and Topology for Mesh Generation, Cambridge University Press, 2001. +Good mathematical treatise on Delaunay triangulation and mesh generation for 2-d +and 3-d surfaces. The chapter on surface simplification is +particularly interesting. It is similar to facet merging in Qhull. + +

    Veron and Leon published an algorithm for shape preserving polyhedral +simplification with bounded error [Computers and Graphics, 22.5:565-585, 1998]. +It remove nodes using front propagation and multiple remeshing. + +

    »Halfspace intersection

    + +

    +The identity pipe for Qhull reveals some precision questions for +halfspace intersections. The identity pipe creates the convex hull of +a set of points and intersects the facets' hyperplanes. It should return the input +points, but narrow distributions may drop points while offset distributions may add +points. It may be better to normalize the input set about the origin. +For example, compare the first results with the later two results: [T. Abraham] +

    + rbox 100 s t | tee r | qconvex FV n | qhalf Fp | cat - r | /bin/sort -n | tail +
    + rbox 100 L1e5 t | tee r | qconvex FV n | qhalf Fp | cat - r | /bin/sort -n | tail +
    + rbox 100 s O10 t | tee r | qconvex FV n | qhalf Fp | cat - r | /bin/sort -n | tail +
    + + +

    »Merged facets

    + +

    Qhull detects precision +problems when computing distances. A precision problem occurs if +the distance computation is less than the maximum roundoff error. +Qhull treats the result of a hyperplane computation as if it +were exact.

    + +

    Qhull handles precision problems by merging non-convex facets. +The result of merging two facets is a thick facet defined by an inner +plane and an outer plane. The inner and outer planes +are offsets from the facet's hyperplane. The inner plane is +clearly below the facet's vertices. At the end of Qhull, the +outer planes are clearly above all input points. Any exact convex +hull must lie between the inner and outer planes.

    + +

    Qhull tests for convexity by computing a point for each facet. +This point is called the facet's centrum. It is the +arithmetic center of the facet's vertices projected to the +facet's hyperplane. For simplicial facets with d +vertices, the centrum is equivalent to the centroid or center of +gravity.

    + +

    Two neighboring facets are convex if each centrum is clearly +below the other hyperplane. The 'Cn' +or 'C-n' options sets the centrum's +radius to n . A centrum is clearly below a hyperplane if +the computed distance from the centrum to the hyperplane is +greater than the centrum's radius plus two maximum roundoff +errors. Two are required because the centrum can be the maximum +roundoff error above its hyperplane and the distance computation +can be high by the maximum roundoff error.

    + +

    With the 'C-n' or 'A-n ' options, Qhull merges non-convex +facets while constructing the hull. The remaining facets are +clearly convex. With the 'Qx ' +option, Qhull merges coplanar facets after constructing the hull. +While constructing the hull, it merges coplanar horizon facets, +flipped facets, concave facets and duplicated ridges. With 'Qx', coplanar points may be missed, but +it appears to be unlikely.

    + +

    If the user sets the 'An' or 'A-n' option, then all neighboring +facets are clearly convex and the maximum (exact) cosine of an +angle is n.

    + +

    If 'C-0' or 'Qx' is used without other precision +options (default), Qhull tests vertices instead of centrums for +adjacent simplices. In 3-d, if simplex abc is adjacent to +simplex bcd, Qhull tests that vertex a is clearly +below simplex bcd , and vertex d is clearly below +simplex abc. When building the hull, Qhull tests vertices +if the horizon is simplicial and no merges occur.

    + +

    »How Qhull merges facets

    + +

    If two facets are not clearly convex, then Qhull removes one +or the other facet by merging the facet into a neighbor. It +selects the merge which minimizes the distance from the +neighboring hyperplane to the facet's vertices. Qhull also +performs merges when a facet has fewer than d neighbors (called a +degenerate facet), when a facet's vertices are included in a +neighboring facet's vertices (called a redundant facet), when a +facet's orientation is flipped, or when a ridge occurs between +more than two facets.

    + +

    Qhull performs merges in a series of passes sorted by merge +angle. Each pass merges those facets which haven't already been +merged in that pass. After a pass, Qhull checks for redundant +vertices. For example, if a vertex has only two neighbors in 3-d, +the vertex is redundant and Qhull merges it into an adjacent +vertex.

    + +

    Merging two simplicial facets creates a non-simplicial facet +of d+1 vertices. Additional merges create larger facets. +When merging facet A into facet B, Qhull retains facet B's +hyperplane. It merges the vertices, neighbors, and ridges of both +facets. It recomputes the centrum if a wide merge has not +occurred (qh_WIDEcoplanar) and the number of extra vertices is +smaller than a constant (qh_MAXnewcentrum).

    + + +

    »Limitations of merged +facets

    + +
      +
    • Uneven dimensions -- +If one coordinate has a larger absolute value than other +coordinates, it may dominate the effect of roundoff errors on +distance computations. You may use option 'QbB' to scale points to the unit cube. +For Delaunay triangulations and Voronoi diagrams, qdelaunay +and qvoronoi always set +option 'Qbb'. It scales the last +coordinate to [0,m] where m is the maximum width of the +other coordinates. Option 'Qbb' is +needed for Delaunay triangulations of integer coordinates +and nearly cocircular points. + +

      For example, compare +

      +        rbox 1000 W0 t | qconvex Qb2:-1e-14B2:1e-14
      +
      +with +
      +        rbox 1000 W0 t | qconvex
      +
      +The distributions are the same but the first is compressed to a 2e-14 slab. +

      +

    • Post-merging of coplanar facets -- In 5-d and higher, option 'Qx' +(default) delays merging of coplanar facets until post-merging. +This may allow "dents" to occur in the intermediate +convex hulls. A point may be poorly partitioned and force a poor +approximation. See option 'Qx' for +further discussion.

      + +

      This is difficult to produce in 5-d and higher. Option 'Q6' turns off merging of concave +facets. This is similar to 'Qx'. It may lead to serious precision errors, +for example, +

      +        rbox 10000 W1e-13  | qhull Q6  Tv
      +
      + +

      +

    • Maximum facet width -- +Qhull reports the maximum outer plane and inner planes (if +more than roundoff error apart). There is no upper bound +for either figure. This is an area for further research. Qhull +does a good job of post-merging in all dimensions. Qhull does a +good job of pre-merging in 2-d, 3-d, and 4-d. With the 'Qx' option, it does a good job in +higher dimensions. In 5-d and higher, Qhull does poorly at +detecting redundant vertices.

      + +

      In the summary ('s'), look at the +ratio between the maximum facet width and the maximum width of a +single merge, e.g., "(3.4x)". Qhull usually reports a +ratio of four or lower in 3-d and six or lower in 4-d. If it +reports a ratio greater than 10, this may indicate an +implementation error. Narrow distributions (see following) may +produce wide facets. + +

      For example, if special processing for narrow distributions is +turned off ('Q10'), qhull may produce +a wide facet:

      +
      +         rbox 1000 L100000 s G1e-16 t1002074964 | qhull Tv Q10
      +
      + +

      +

    • Narrow distribution -- In 3-d, a narrow distribution may result in a poor +approximation. For example, if you do not use qdelaunay nor option +'Qbb', the furthest-site +Delaunay triangulation of nearly cocircular points may produce a poor +approximation: +
      +         rbox s 5000 W1e-13 D2 t1002151341 | qhull d Qt
      +         rbox 1000 s W1e-13 t1002231672 | qhull d Tv
      +
      + +

      During +construction of the hull, a point may be above two +facets with opposite orientations that span the input +set. Even though the point may be nearly coplanar with both +facets, and can be distant from the precise convex +hull of the input sites. Additional facets leave the point distant from +a facet. To fix this problem, add option 'Qbb' +(it scales the last coordinate). Option 'Qbb' +is automatically set for qdelaunay and qvoronoi. + +

      Qhull generates a warning if the initial simplex is narrow. +For narrow distributions, Qhull changes how it processes coplanar +points -- it does not make a point coplanar until the hull is +finished. +Use option 'Q10' to try Qhull without +special processing for narrow distributions. +For example, special processing is needed for: +

      +         rbox 1000 L100000 s G1e-16 t1002074964 | qhull Tv Q10
      +
      + +

      You may turn off the warning message by reducing +qh_WARNnarrow in user.h or by setting option +'Pp'.

      + +

      Similar problems occur for distributions with a large flat facet surrounded +with many small facet at a sharp angle to the large facet. +Qhull 3.1 fixes most of these problems, but a poor approximation can occur. +A point may be left outside of the convex hull ('Tv'). +Examples include +the furthest-site Delaunay triangulation of nearly cocircular points plus the origin, and the convex hull of a cone of nearly cocircular points. The width of the band is 10^-13. +

      +        rbox s 1000 W1e-13 P0 D2 t996799242 | qhull d Tv
      +        rbox 1000 s Z1 G1e-13 t1002152123 | qhull Tv
      +        rbox 1000 s Z1 G1e-13 t1002231668 | qhull Tv
      +
      + +

      +

    • Quadratic running time -- If the output contains large, non-simplicial +facets, the running time for Qhull may be quadratic in the size of the triangulated +output. For example, rbox 1000 s W1e-13 c G2 | qhull d is 4 times +faster for 500 points. The convex hull contains two large nearly spherical facets and +many nearly coplanar facets. Each new point retriangulates the spherical facet and repartitions the remaining points into all of the nearly coplanar facets. +In this case, quadratic running time is avoided if you use qdelaunay, +add option 'Qbb', +or add the origin ('P0') to the input. +

      +

    • Nearly coincident points within 1e-13 -- +Multiple, nearly coincident points within a 1e-13 ball of points in the unit cube +may lead to wide facets or quadratic running time. +For example, the convex hull a 1000 coincident, cospherical points in 4-D, +or the 3-D Delaunay triangulation of nearly coincident points, may lead to very +wide facets (e.g., 2267021951.3x). + +

      For Delaunay triangulations, the problem typically occurs for extreme points of the input +set (i.e., on the edge between the upper and lower convex hull). After multiple facet merges, four +facets may share the same, duplicate ridge and must be merged. +Some of these facets may be long and narrow, leading to a very wide merged facet. +If so, error QH6271 is reported. It may be overriden with option 'Q12'. + +

      Duplicate ridges occur when the horizon facets for a new point is "pinched". +In a duplicate ridge, a subridge (e.g., a line segment in 3-d) is shared by two horizon facets. +At least two of its vertices are nearly coincident. It is easy to generate coincident points with +option 'Cn,r,m' of rbox. It generates n points within an r ball for each of m input sites. For example, +every point of the following distributions has a nearly coincident point within a 1e-13 ball. +Substantially smaller or larger balls do not lead to pinched horizons. +

      +        rbox 1000 C1,1e-13 D4 s t | qhull
      +        rbox 75 C1,1e-13 t | qhull d
      +
      +For Delaunay triangulations, a bounding box may alleviate this error (e.g., rbox 500 C1,1E-13 t c G1 | qhull d). +A later release of qhull will avoid pinched horizons by merging duplicate subridges. A subridge is +merged by merging adjacent vertices. +

      +

    • Facet with zero-area -- +It is possible for a zero-area facet to be convex with its +neighbors. This can occur if the hyperplanes of neighboring +facets are above the facet's centrum, and the facet's hyperplane +is above the neighboring centrums. Qhull computes the facet's +hyperplane so that it passes through the facet's vertices. The +vertices can be collinear.

      + +

      +

    • No more facets -- Qhull reports an error if there are d+1 facets left +and two of the facets are not clearly convex. This typically +occurs when the convexity constraints are too strong or the input +points are degenerate. The former is more likely in 5-d and +higher -- especially with option 'C-n'.

      + +

      +

    • Deleted cone -- Lots of merging can end up deleting all +of the new facets for a point. This is a rare event that has +only been seen while debugging the code. + +

      +

    • Triangulated output leads to precision problems -- With sufficient +merging, the ridges of a non-simplicial facet may have serious topological +and geometric problems. A ridge may be between more than two +neighboring facets. If so, their triangulation ('Qt') +will fail since two facets have the same vertex set. Furthermore, +a triangulated facet may have flipped orientation compared to its +neighbors.
    • + +

      The triangulation process detects degenerate facets with +only two neighbors. These are marked degenerate. They have +zero area. + +

      +

    • Coplanar points -- +Option 'Qc' is determined by +qh_check_maxout() after constructing the hull. Qhull needs to +retain all possible coplanar points in the facets' coplanar sets. +This depends on qh_RATIOnearInside in user.h. +Furthermore, the cutoff for a coplanar point is arbitrarily set +at the minimum vertex. If coplanar points are important to your +application, remove the interior points by hand (set 'Qc Qi') or +make qh_RATIOnearInside sufficiently large.

      + +

      +

    • Maximum roundoff error -- Qhull computes the maximum roundoff error from the maximum +coordinates of the point set. Usually the maximum roundoff error +is a reasonable choice for all distance computations. The maximum +roundoff error could be computed separately for each point or for +each distance computation. This is expensive and it conflicts +with option 'C-n'. + +

      +

    • All flipped or upper Delaunay -- When a lot of merging occurs for +Delaunay triangulations, a new point may lead to no good facets. For example, +try a strong convexity constraint: +
      +        rbox 1000 s t993602376 | qdelaunay C-1e-3
      +
      + +
    + +

    »Joggled input

    + +

    Joggled input is a simple work-around for precision problems +in computational geometry ["joggle: to shake or jar +slightly," Amer. Heritage Dictionary]. Other names are +jostled input or random perturbation. +Qhull joggles the +input by modifying each coordinate by a small random quantity. If +a precision problem occurs, Qhull joggles the input with a larger +quantity and the algorithm is restarted. This process continues +until no precision problems occur. Unless all inputs incur +precision problems, Qhull will terminate. Qhull adjusts the inner +and outer planes to account for the joggled input.

    + +

    Neither joggle nor merged facets has an upper bound for the width of the output +facets, but both methods work well in practice. Joggled input is +easier to justify. Precision errors occur when the points are +nearly singular. For example, four points may be coplanar or +three points may be collinear. Consider a line and an incident +point. A precision error occurs if the point is within some +epsilon of the line. Now joggle the point away from the line by a +small, uniformly distributed, random quantity. If the point is +changed by more than epsilon, the precision error is avoided. The +probability of this event depends on the maximum joggle. Once the +maximum joggle is larger than epsilon, doubling the maximum +joggle will halve the probability of a precision error.

    + +

    With actual data, an analysis would need to account for each +point changing independently and other computations. It is easier +to determine the probabilities empirically ('TRn') . For example, consider +computing the convex hull of the unit cube centered on the +origin. The arithmetic has 16 significant decimal digits.

    + +
    +

    Convex hull of unit cube

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    joggleerror prob.
    1.0e-150.983
    2.0e-150.830
    4.0e-150.561
    8.0e-150.325
    1.6e-140.185
    3.2e-140.099
    6.4e-140.051
    1.3e-130.025
    2.6e-130.010
    5.1e-130.004
    1.0e-120.002
    2.0e-120.001
    +
    + +

    A larger joggle is needed for multiple points. Since the +number of potential singularities increases, the probability of +one or more precision errors increases. Here is an example.

    + +
    +

    Convex hull of 1000 points on unit cube

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    joggleerror prob.
    1.0e-120.870
    2.0e-120.700
    4.0e-120.450
    8.0e-120.250
    1.6e-110.110
    3.2e-110.065
    6.4e-110.030
    1.3e-100.010
    2.6e-100.008
    5.1e-090.003
    +
    + +

    Other distributions behave similarly. No distribution should +behave significantly worse. In Euclidean space, the probability +measure of all singularities is zero. With floating point +numbers, the probability of a singularity is non-zero. With +sufficient digits, the probability of a singularity is extremely +small for random data. For a sufficiently large joggle, all data +is nearly random data.

    + +

    Qhull uses an initial joggle of 30,000 times the maximum +roundoff error for a distance computation. This avoids most +potential singularities. If a failure occurs, Qhull retries at +the initial joggle (in case bad luck occurred). If it occurs +again, Qhull increases the joggle by ten-fold and tries again. +This process repeats until the joggle is a hundredth of the width +of the input points. Qhull reports an error after 100 attempts. +This should never happen with double-precision arithmetic. Once +the probability of success is non-zero, the probability of +success increases about ten-fold at each iteration. The +probability of repeated failures becomes extremely small.

    + +

    Merged facets produces a significantly better approximation. +Empirically, the maximum separation between inner and outer +facets is about 30 times the maximum roundoff error for a +distance computation. This is about 2,000 times better than +joggled input. Most applications though will not notice the +difference.

    + +

    »Exact arithmetic

    + +

    Exact arithmetic may be used instead of floating point. +Singularities such as coplanar points can either be handled +directly or the input can be symbolically perturbed. Using exact +arithmetic is slower than using floating point arithmetic and the +output may take more space. Chaining a sequence of operations +increases the time and space required. Some operations are +difficult to do.

    + +

    Clarkson's hull +program and Shewchuk's triangle +program are practical implementations of exact arithmetic.

    + +

    Clarkson limits the input precision to about fifteen digits. +This reduces the number of nearly singular computations. When a +determinant is nearly singular, he uses exact arithmetic to +compute a precise result.

    + +

    »Approximating a +convex hull

    + +

    Qhull may be used for approximating a convex hull. This is +particularly valuable in 5-d and higher where hulls can be +immense. You can use 'Qx C-n' to merge facets as the hull is +being constructed. Then use 'Cn' +and/or 'An' to merge small facets +during post-processing. You can print the n largest facets +with option 'PAn'. You can print +facets whose area is at least n with option 'PFn'. You can output the outer planes +and an interior point with 'FV Fo' and then compute their intersection +with 'qhalf'.

    + +

    To approximate a convex hull in 6-d and higher, use +post-merging with 'Wn' (e.g., qhull +W1e-1 C1e-2 TF2000). Pre-merging with a convexity constraint +(e.g., qhull Qx C-1e-2) often produces a poor approximation or +terminates with a simplex. Option 'QbB' +may help to spread out the data.

    + +

    You will need to experiment to determine a satisfactory set of +options. Use rbox to generate test sets +quickly and Geomview to view +the results. You will probably want to write your own driver for +Qhull using the Qhull library. For example, you could select the +largest facet in each quadrant.

    + + +
    + +

    Up: Home +page for Qhull
    +Up: Qhull manual: Table of +Contents
    +To: Programs +• Options +• Output +• Formats +• Geomview +• Print +• Qhull +• Precision +• Trace +• Functions
    +To: Qhull imprecision: Table of Contents + + +


    + +

    The Geometry Center +Home Page

    + +

    Comments to: qhull@qhull.org +
    +Created: Sept. 25, 1995 --- Last modified: see top

    + + diff --git a/xs/src/qhull/html/qh-optc.htm b/xs/src/qhull/html/qh-optc.htm new file mode 100644 index 000000000..87308180d --- /dev/null +++ b/xs/src/qhull/html/qh-optc.htm @@ -0,0 +1,292 @@ + + + + +Qhull precision options + + + + +

    Up: Home page for Qhull
    +Up: Qhull manual: Table of Contents
    +To: Programs +• Options +• Output +• Formats +• Geomview +• Print +• Qhull +• Precision +• Trace +• Functions

    + +
    + +

    [delaunay] Qhull precision options

    + +This section lists the precision options for Qhull. These options are +indicated by an upper-case letter followed by a number. + +

    Copyright © 1995-2015 C.B. Barber

    + +
    + +

    » Programs + Options +• Output +• Formats +• Geomview +• Print +• Qhull +• Precision +• Trace +• Functions

    + +

    Precision options

    + +

    Most users will not need to set these options. They are best +used for approximating a +convex hull. They may also be used for testing Qhull's handling +of precision errors.

    + +

    By default, Qhull uses options 'C-0' for +2-d, 3-d and 4-d, and 'Qx' for 5-d +and higher. These options use facet merging to handle precision +errors. You may also use joggled input 'QJ' +to avoid precision problems. +For more information see Imprecision in Qhull.

    + +
    +
     
    +
    General
    +
    Cn
    +
    centrum radius for post-merging
    +
    C-n
    +
    centrum radius for pre-merging
    +
    An
    +
    cosine of maximum angle for post-merging
    +
    A-n
    +
    cosine of maximum angle for pre-merging
    +
    Qx
    +
    exact pre-merges (allows coplanar facets)
    +
    C-0
    +
    handle all precision errors
    +
    Wn
    +
    min distance above plane for outside points
    +
    + +
    +
     
    +
    Experimental
    +
    Un
    +
    max distance below plane for a new, coplanar point
    +
    En
    +
    max roundoff error for distance computation
    +
    Vn
    +
    min distance above plane for a visible facet
    +
    Rn
    +
    randomly perturb computations by a factor of [1-n,1+n]
    +
    + +
    +
    + +
    + +

    »A-n - cosine of maximum +angle for pre-merging.

    + +

    Pre-merging occurs while Qhull constructs the hull. It is +indicated by 'C-n', 'A-n', or 'Qx'.

    + +

    If the angle between a pair of facet normals is greater than n, +Qhull merges one of the facets into a neighbor. It selects the +facet that is closest to a neighboring facet.

    + +

    For example, option 'A-0.99' merges facets during the +construction of the hull. If the cosine of the angle between +facets is greater than 0.99, one or the other facet is merged. +Qhull accounts for the maximum roundoff error.

    + +

    If 'A-n' is set without 'C-n', then 'C-0' is automatically set.

    + +

    In 5-d and higher, you should set 'Qx' +along with 'A-n'. It skips merges of coplanar facets until after +the hull is constructed and before 'An' and 'Cn' are checked.

    + +

    »An - cosine of maximum angle for +post-merging.

    + +

    Post merging occurs after the hull is constructed. For +example, option 'A0.99' merges a facet if the cosine of the angle +between facets is greater than 0.99. Qhull accounts for the +maximum roundoff error.

    + +

    If 'An' is set without 'Cn', then 'C0' is automatically set.

    + +

    »C-0 - handle all precision +errors

    + +

    Qhull handles precision errors by merging facets. The 'C-0' +option handles all precision errors in 2-d, 3-d, and 4-d. It is +set by default. It may be used in higher dimensions, but +sometimes the facet width grows rapidly. It is usually better to +use 'Qx' in 5-d and higher. +Use 'QJ' to joggle the input +instead of merging facets. +Use 'Q0' to turn both options off.

    + +

    Qhull optimizes 'C-0' ("_zero-centrum") by testing +vertices instead of centrums for adjacent simplices. This may be +slower in higher dimensions if merges decrease the number of +processed points. The optimization may be turned off by setting a +small value such as 'C-1e-30'. See How +Qhull handles imprecision.

    + +

    »C-n - centrum radius for +pre-merging

    + +

    Pre-merging occurs while Qhull constructs the hull. It is +indicated by 'C-n', 'A-n', or 'Qx'.

    + +

    The centrum of a facet is a point on the facet for +testing facet convexity. It is the average of the vertices +projected to the facet's hyperplane. Two adjacent facets are +convex if each centrum is clearly below the other facet.

    + +

    If adjacent facets are non-convex, one of the facets is merged +into a neighboring facet. Qhull merges the facet that is closest +to a neighboring facet.

    + +

    For option 'C-n', n is the centrum radius. For example, +'C-0.001' merges facets whenever the centrum is less than 0.001 +from a neighboring hyperplane. Qhull accounts for roundoff error +when testing the centrum.

    + +

    In 5-d and higher, you should set 'Qx' +along with 'C-n'. It skips merges of coplanar facets until after +the hull is constructed and before 'An' and 'Cn' are checked.

    + +

    »Cn - centrum radius for +post-merging

    + +

    Post-merging occurs after Qhull constructs the hull. It is +indicated by 'Cn' or 'An'.

    + +

    For option 'Cn', n is the centrum +radius. For example, 'C0.001' merges facets when the centrum is +less than 0.001 from a neighboring hyperplane. Qhull accounts for +roundoff error when testing the centrum.

    + +

    Both pre-merging and post-merging may be defined. If only +post-merging is used ('Q0' with +'Cn'), Qhull may fail to produce a hull due to precision errors +during the hull's construction.

    + +

    »En - max roundoff error +for distance computations

    + +

    This allows the user to change the maximum roundoff error +computed by Qhull. The value computed by Qhull may be overly +pessimistic. If 'En' is set too small, then the output may not be +convex. The statistic "max. distance of a new vertex to a +facet" (from option 'Ts') is a +reasonable upper bound for the actual roundoff error.

    + +

    »Rn - randomly perturb +computations

    + +

    This option perturbs every distance, hyperplane, and angle +computation by up to (+/- n * max_coord). It simulates the +effect of roundoff errors. Unless 'En' is +explicitly set, it is adjusted for 'Rn'. The command 'qhull Rn' +will generate a convex hull despite the perturbations. See the Examples section for an example.

    + +

    Options 'Rn C-n' have the effect of 'W2n' +and 'C-2n'. To use time as the random number +seed, use option 'QR-1'.

    + +

    »Un - max distance for a +new, coplanar point

    + +

    This allows the user to set coplanarity. When pre-merging ('C-n ', 'A-n' or 'Qx'), Qhull merges a new point into any +coplanar facets. The default value for 'Un' is 'Vn'.

    + +

    »Vn - min distance for a +visible facet

    + +

    This allows the user to set facet visibility. When adding a +point to the convex hull, Qhull determines all facets that are +visible from the point. A facet is visible if the distance from +the point to the facet is greater than 'Vn'.

    + +

    Without merging, the default value for 'Vn' is the roundoff +error ('En'). With merging, the default value +is the pre-merge centrum ('C-n') in 2-d or 3-d, +or three times that in other dimensions. If the outside width is +specified with option 'Wn ', the maximum, +default value for 'Vn' is 'Wn'.

    + +

    Qhull warns if 'Vn' is greater than 'Wn' and +furthest outside ('Qf') is not +selected; this combination usually results in flipped facets +(i.e., reversed normals).

    + +

    »Wn - min distance above +plane for outside points

    + +

    Points are added to the convex hull only if they are clearly +outside of a facet. A point is outside of a facet if its distance +to the facet is greater than 'Wn'. Without pre-merging, the +default value for 'Wn' is 'En '. If the user +specifies pre-merging and does not set 'Wn', than 'Wn' is set to +the maximum of 'C-n' and maxcoord*(1 - A-n).

    + +

    This option is good for approximating +a convex hull.

    + +

    Options 'Qc' and 'Qi' use the minimum vertex to +distinguish coplanar points from interior points.

    + +
    + +

    Up: Home page for Qhull
    +Up: Qhull manual: Table of Contents
    +To: Programs +• Options +• Output +• Formats +• Geomview +• Print +• Qhull +• Precision +• Trace +• Functions

    + +
    + +

    The Geometry Center +Home Page

    + +

    Comments to: qhull@qhull.org +
    +Created: Sept. 25, 1995 --- Last modified: see top

    + + diff --git a/xs/src/qhull/html/qh-optf.htm b/xs/src/qhull/html/qh-optf.htm new file mode 100644 index 000000000..3c7a2b1db --- /dev/null +++ b/xs/src/qhull/html/qh-optf.htm @@ -0,0 +1,736 @@ + + + + +Qhull format options (F) + + + +

    Up: Home page for Qhull
    +Up: Qhull manual: Table of Contents
    +To: Programs +• Options +• Output +• Formats +• Geomview +• Print +• Qhull +• Precision +• Trace +• Functions

    +
    + +

    [delaunay] Qhull format options (F)

    + +

    This section lists the format options for Qhull. These options +are indicated by 'F' followed by a letter. See Output, Print, +and Geomview for other output +options.

    + +

    Copyright © 1995-2015 C.B. Barber

    + +
    + +

    » Programs + Options +• Output +• Formats +• Geomview +• Print +• Qhull +• Precision +• Trace +• Functions

    + +

    Additional input & output formats

    + +

    These options allow for automatic processing of Qhull output. +Options 'i', 'o', +'n', and 'p' +may also be used.

    + +
    +
    +
    Summary and control +
    FA +
    compute total area and volume for option 's' + +
    FV +
    print average vertex (interior point for 'qhalf') +
    FQ +
    print command for qhull and input +
    FO +
    print options to stderr or stdout +
    FS +
    print sizes: total area and volume +
    Fs +
    print summary: dim, #points, total vertices and + facets, #vertices, #facets, max outer and inner plane +
    Fd +
    use format for input (offset first) +
    FD +
    use cdd format for normals (offset first) +
    FM +
    print Maple output (2-d and 3-d) +
    +
    +
    Facets, points, and vertices +
    Fa +
    print area for each facet +
    FC +
    print centrum for each facet +
    Fc +
    print coplanar points for each facet +
    Fx +
    print extreme points (i.e., vertices) of convex hull. + +
    FF +
    print facets w/o ridges +
    FI +
    print ID for each facet +
    Fi +
    print inner planes for each facet +
    Fm +
    print merge count for each facet (511 max) +
    FP +
    print nearest vertex for coplanar points +
    Fn +
    print neighboring facets for each facet +
    FN +
    print neighboring facets for each point +
    Fo +
    print outer planes for each facet +
    Ft +
    print triangulation with added points +
    Fv +
    print vertices for each facet +
    +
    +
    Delaunay, Voronoi, and halfspace +
    Fx +
    print extreme input sites of Delaunay triangulation + or Voronoi diagram. +
    Fp +
    print points at halfspace intersections +
    Fi +
    print separating hyperplanes for inner, bounded + Voronoi regions +
    Fo +
    print separating hyperplanes for outer, unbounded + Voronoi regions +
    Fv +
    print Voronoi diagram as ridges for each input pair +
    FC +
    print Voronoi vertex ("center") for each facet
    +
    + +
    + +

    »Fa - print area for each +facet

    + +

    The first line is the number of facets. The remaining lines +are the area for each facet, one facet per line. See 'FA' and 'FS' for computing the total area and volume.

    + +

    Use 'PAn' for printing the n +largest facets. Use option 'PFn' +for printing facets larger than n.

    + +

    For Delaunay triangulations, the area is the area of each +Delaunay triangle. For Voronoi vertices, the area is the area of +the dual facet to each vertex.

    + +

    Qhull uses the centrum and ridges to triangulate +non-simplicial facets. The area for non-simplicial facets is the +sum of the areas for each triangle. It is an approximation of the +actual area. The ridge's vertices are projected to the facet's +hyperplane. If a vertex is far below a facet (qh_WIDEcoplanar in user.h), +the corresponding triangles are ignored.

    + +

    For non-simplicial facets, vertices are often below the +facet's hyperplane. If so, the approximation is less than the +actual value and it may be significantly less.

    + +

    »FA - compute total area +and volume for option 's'

    + +

    With option 'FA', Qhull includes the total area and volume in +the summary ('s'). Option 'FS' also includes the total area and volume. +If facets are +merged, the area and volume are approximations. Option 'FA' is +automatically set for options 'Fa', 'PAn', and 'PFn'. +

    + +

    With 'qdelaunay s FA', Qhull computes the total area of +the Delaunay triangulation. This equals the volume of the convex +hull of the data points. With options 'qdelaunay Qu +s FA', Qhull computes the +total area of the furthest-site Delaunay triangulation. This +equals of the total area of the Delaunay triangulation.

    + +

    See 'Fa' for further details. Option 'FS' also computes the total area and volume.

    + +

    »Fc - print coplanar +points for each facet

    + +

    The output starts with the number of facets. Then each facet +is printed one per line. Each line is the number of coplanar +points followed by the point ids.

    + +

    By default, option 'Fc' reports coplanar points +('Qc'). You may also use +option 'Qi'. Options 'Qi Fc' prints +interior points while 'Qci Fc' prints both coplanar and interior +points. + +

    Each coplanar point or interior point is assigned to the +facet it is furthest above (resp., least below).

    + +

    Use 'Qc p' to print vertex and +coplanar point coordinates. Use 'Fv' +to print vertices.

    + +

    »FC - print centrum or +Voronoi vertex for each facet

    + +

    The output starts with the dimension followed by the number of +facets. Then each facet centrum is printed, one per line. For +qvoronoi, Voronoi vertices are +printed instead.

    + +

    »Fd - use cdd format for +input

    + +

    The input starts with comments. The first comment is reported +in the summary. Data starts after a "begin" line. The +next line is the number of points followed by the dimension plus +one and "real" or "integer". Then the points +are listed with a leading "1" or "1.0". The +data ends with an "end" line.

    + +

    For halfspaces ('qhalf Fd'), +the input format is the same. Each halfspace starts with its +offset. The signs of the offset and coefficients are the +opposite of Qhull's +convention. The first two lines of the input may be an interior +point in 'FV' format.

    + +

    »FD - use cdd format for +normals

    + +

    Option 'FD' prints normals ('n', 'Fo', 'Fi') or points ('p') in cdd format. The first line is the +command line that invoked Qhull. Data starts with a +"begin" line. The next line is the number of normals or +points followed by the dimension plus one and "real". +Then the normals or points are listed with the offset before the +coefficients. The offset for points is 1.0. For normals, +the offset and coefficients use the opposite sign from Qhull. +The data ends with an "end" line.

    + +

    »FF - print facets w/o +ridges

    + +

    Option 'FF' prints all fields of all facets (as in 'f') without printing the ridges. This is +useful in higher dimensions where a facet may have many ridges. +For simplicial facets, options 'FF' and 'f +' are equivalent.

    + +

    »Fi - print inner planes +for each facet

    + +

    The first line is the dimension plus one. The second line is +the number of facets. The remainder is one inner plane per line. +The format is the same as option 'n'.

    + +

    The inner plane is a plane that is below the facet's vertices. +It is an offset from the facet's hyperplane. It includes a +roundoff error for computing the vertex distance.

    + +

    Note that the inner planes for Geomview output ('Gi') include an additional offset for +vertex visualization and roundoff error.

    + +

    »Fi - print separating +hyperplanes for inner, bounded Voronoi regions

    + +

    With qvoronoi, 'Fi' prints the +separating hyperplanes for inner, bounded regions of the Voronoi +diagram. The first line is the number of ridges. Then each +hyperplane is printed, one per line. A line starts with the +number of indices and floats. The first pair of indices indicates +an adjacent pair of input sites. The next d floats are the +normalized coefficients for the hyperplane, and the last float is +the offset. The hyperplane is oriented toward 'QVn' (if defined), or the first input +site of the pair.

    + +

    Use 'Fo' for unbounded regions, +and 'Fv' for the corresponding +Voronoi vertices.

    + +

    Use 'Tv' to verify that the +hyperplanes are perpendicular bisectors. It will list relevant +statistics to stderr. The hyperplane is a perpendicular bisector +if the midpoint of the input sites lies on the plane, all Voronoi +vertices in the ridge lie on the plane, and the angle between the +input sites and the plane is ninety degrees. This is true if all +statistics are zero. Roundoff and computation errors make these +non-zero. The deviations appear to be largest when the +corresponding Delaunay triangles are large and thin; for example, +the Voronoi diagram of nearly cospherical points.

    + +

    »FI - print ID for each +facet

    + +

    Print facet identifiers. These are used internally and listed +with options 'f' and 'FF'. +Options 'Fn ' and 'FN' use +facet identifiers for negative indices.

    + +

    »Fm - print merge count +for each facet

    + +

    The first line is the number of facets. The remainder is the +number of merges for each facet, one per line. At most 511 merges +are reported for a facet. See 'PMn' +for printing the facets with the most merges.

    + +

    »FM - print Maple +output

    + +

    Qhull writes a Maple file for 2-d and 3-d convex hulls, +2-d and 3-d halfspace intersections, +and 2-d Delaunay triangulations. Qhull produces a 2-d +or 3-d plot. + +

    Warning: This option has not been tested in Maple. + +

    [From T. K. Abraham with help from M. R. Feinberg and N. Platinova.] +The following steps apply while working within the +Maple worksheet environment : +

      +
    1. Generate the data and store it as an array . For example, in 3-d, data generated +in Maple is of the form : x[i],y[i],z[i] +

      +

    2. Create a single variable and assign the entire array of data points to this variable. +Use the "seq" command within square brackets as shown in the following example. +(The square brackets are essential for the rest of the steps to work.) +

      +>data:=[seq([x[i],y[i],z[i]],i=1..n)]:# here n is the number of data points + +

    3. Next we need to write the data to a file to be read by qhull. Before +writing the data to a file, make sure that the qhull executable files and +the data file lie in the same subdirectory. If the executable files are +stored in the "C:\qhull3.1\" subdirectory, then save the file in the same +subdirectory, say "C:\qhull3.1\datafile.txt". For the sake of integrity of +the data file , it is best to first ensure that the data file does not +exist before writing into the data file. This can be done by running a +delete command first . To write the data to the file, use the "writedata" +and the "writedata[APPEND]" commands as illustrated in the following example : +

      +>system("del c:\\qhull3.1\\datafile.txt");#To erase any previous versions of the file +
      >writedata("c:\\qhull3.1\\datafile.txt ",[3, nops(data)]);#writing in qhull format +
      >writedata[APPEND]("c:\\ qhull3.1\\datafile.txt ", data);#writing the data points +

    4. +Use the 'FM' option to produce Maple output. Store the output as a ".mpl" file. +For example, using the file we created above, we type the following (in DOS environment) +

      +qconvex s FM <datafile.txt >dataplot.mpl + +

    5. +To read 3-d output in Maple, we use the 'read' command followed by +a 'display3d' command. For example (in Maple environment): +

      +>with (plots): +
      >read `c:\\qhull3.1\\dataplot.mpl`:#IMPORTANT - Note that the punctuation mark used is ' and NOT '. The correct punctuation mark is the one next to the key for "1" (not the punctuation mark near the enter key) +
      > qhullplot:=%: +
      > display3d(qhullplot); +

    + +

    For Delaunay triangulation orthogonal projection is better. + +

    For halfspace intersections, Qhull produces the dual +convex hull. + +

    See Is Qhull available for Maple? +for other URLs. + +

    »Fn - print neighboring +facets for each facet

    + +

    The output starts with the number of facets. Then each facet +is printed one per line. Each line is the number of neighbors +followed by an index for each neighbor. The indices match the +other facet output formats.

    + +

    For simplicial facets, each neighbor is opposite +the corresponding vertex (option 'Fv'). +Do not compare to option 'i'. Option 'i' +orients facets by reversing the order of two vertices. For non-simplicial facets, +the neighbors are unordered. + +

    A negative index indicates an unprinted facet due to printing +only good facets ('Pg', qdelaunay, +qvoronoi). It +is the negation of the facet's ID (option 'FI'). +For example, negative indices are used for facets "at +infinity" in the Delaunay triangulation.

    + +

    »FN - print neighboring +facets for each point

    + +

    The first line is the number of points. Then each point is +printed, one per line. For unassigned points (either interior or +coplanar), the line is "0". For assigned coplanar +points ('Qc'), the line is +"1" followed by the index of the facet that is furthest +below the point. For assigned interior points ('Qi'), the line is "1" +followed by the index of the facet that is least above the point. +For vertices that do not belong to good facet, the line is +"0"

    + +

    For vertices of good facets, the line is the number of +neighboring facets followed by the facet indices. The indices +correspond to the other 'F' formats. In 4-d +and higher, the facets are sorted by index. In 3-d, the facets +are in adjacency order (not oriented).

    + +

    A negative index indicates an unprinted facet due to printing +only good facets (qdelaunay, +qvoronoi, 'Pdk', +'Pg'). It is the negation of the +facet's ID (' FI'). For example, negative +indices are used for facets "at infinity" in the +Delaunay triangulation.

    + +

    For Voronoi vertices, option 'FN' lists the vertices of the +Voronoi region for each input site. Option 'FN' lists the regions +in site ID order. Option 'FN' corresponds to the second half of +option 'o'. To convert from 'FN' to 'o', replace negative indices with zero +and increment non-negative indices by one.

    + +

    If you are using the Qhull +library or C++ interface, option 'FN' has the side effect of reordering the +neighbors for a vertex

    + +

    »Fo - print outer planes +for each facet

    + +

    The first line is the dimension plus one. The second line is +the number of facets. The remainder is one outer plane per line. +The format is the same as option 'n'.

    + +

    The outer plane is a plane that is above all points. It is an +offset from the facet's hyperplane. It includes a roundoff error +for computing the point distance. When testing the outer plane +(e.g., 'Tv'), another roundoff error +should be added for the tested point.

    + +

    If outer planes are not checked ('Q5') +or not computed (!qh_MAXoutside), the maximum, computed outside +distance is used instead. This can be much larger than the actual +outer planes.

    + +

    Note that the outer planes for Geomview output ('G') include an additional offset for +vertex/point visualization, 'lines closer,' and roundoff error.

    + +

    »Fo - print separating +hyperplanes for outer, unbounded Voronoi regions

    + +

    With qvoronoi, 'Fo' prints the +separating hyperplanes for outer, unbounded regions of the +Voronoi diagram. The first line is the number of ridges. Then +each hyperplane is printed, one per line. A line starts with the +number of indices and floats. The first pair of indices indicates +an adjacent pair of input sites. The next d floats are the +normalized coefficients for the hyperplane, and the last float is +the offset. The hyperplane is oriented toward 'QVn' (if defined), or the first input +site of the pair.

    + +

    Option 'Fo' gives the hyperplanes for the unbounded rays of +the unbounded regions of the Voronoi diagram. Each hyperplane +goes through the midpoint of the corresponding input sites. The +rays are directed away from the input sites.

    + +

    Use 'Fi' for bounded regions, +and 'Fv' for the corresponding +Voronoi vertices. Use 'Tv' to verify +that the corresponding Voronoi vertices lie on the hyperplane.

    + +

    »FO - print list of +selected options

    + +

    Lists selected options and default values to stderr. +Additional 'FO's are printed to stdout.

    + +

    »Fp - print points at +halfspace intersections

    + +

    The first line is the number of intersection points. The +remainder is one intersection point per line. A intersection +point is the intersection of d or more halfspaces from +'qhalf'. It corresponds to a +facet of the dual polytope. The "infinity" point +[-10.101,-10.101,...] indicates an unbounded intersection.

    + +

    If [x,y,z] are the dual facet's normal coefficients and b<0 +is its offset, the halfspace intersection occurs at +[x/-b,y/-b,z/-b] plus the interior point. If b>=0, the +halfspace intersection is unbounded.

    + +

    »FP - print nearest +vertex for coplanar points

    + +

    The output starts with the number of coplanar points. Then +each coplanar point is printed one per line. Each line is the +point ID of the closest vertex, the point ID of the coplanar +point, the corresponding facet ID, and the distance. Sort the +lines to list the coplanar points nearest to each vertex.

    + +

    Use options 'Qc' and/or 'Qi' with 'FP'. Options 'Qc FP' prints +coplanar points while 'Qci FP' prints coplanar and interior +points. Option 'Qc' is automatically selected if 'Qi' is not +selected. + +

    For Delaunay triangulations (qdelaunay +or qvoronoi), a coplanar point is nearly +incident to a vertex. The distance is the distance in the +original point set.

    + +

    If imprecision problems are severe, Qhull will delete input +sites when constructing the Delaunay triangulation. Option 'FP' will +list these points along with coincident points.

    + +

    If there are many coplanar or coincident points and non-simplicial +facets are triangulated ('Qt'), option +'FP' may be inefficient. It redetermines the original vertex set +for each coplanar point.

    + +

    »FQ - print command for +qhull and input

    + +

    Prints qhull and input command, e.g., "rbox 10 s | qhull +FQ". Option 'FQ' may be repeated multiple times.

    + +

    »Fs - print summary

    + +

    The first line consists of number of integers ("10") +followed by the: +

      +
    • dimension +
    • number of points +
    • number of vertices +
    • number of facets +
    • number of vertices selected for output +
    • number of facets selected for output +
    • number of coplanar points for selected facets +
    • number of nonsimplicial or merged facets selected for + output +
    • number of deleted vertices
    • +
    • number of triangulated facets ('Qt')
    • +
    + +

    The second line consists of the number of reals +("2") followed by the: +

      +
    • maximum offset to an outer plane +
    • minimum offset to an inner plane.
    • +
    +Roundoff and joggle are included. +

    + +

    For Delaunay triangulations and Voronoi diagrams, the +number of deleted vertices should be zero. If greater than zero, then the +input is highly degenerate and coplanar points are not necessarily coincident +points. For example, 'RBOX 1000 s W1e-13 t995138628 | QHULL d Qbb' reports +deleted vertices; the input is nearly cospherical.

    + +

    Later versions of Qhull may produce additional integers or reals.

    + +

    »FS - print sizes

    + +

    The first line consists of the number of integers +("0"). The second line consists of the number of reals +("2"), followed by the total facet area, and the total +volume. Later versions of Qhull may produce additional integers +or reals.

    + +

    The total volume measures the volume of the intersection of +the halfspaces defined by each facet. It is computed from the +facet area. Both area and volume are approximations for +non-simplicial facets. See option 'Fa ' for +further notes. Option 'FA ' also computes the total area and volume.

    + +

    »Ft - print triangulation

    + +

    Prints a triangulation with added points for non-simplicial +facets. The output is

    + +
      +
    • The first line is the dimension +
    • The second line is the number of points, the number + of facets, and the number of ridges. +
    • All of the input points follow, one per line. +
    • The centrums follow, one per non-simplicial facet +
    • Then the facets follow as a list of point indices + preceded by the number of points. The simplices are + oriented.
    • +
    + +

    For convex hulls with simplicial facets, the output is the +same as option 'o'.

    + +

    The added points are the centrums of the non-simplicial +facets. Except for large facets, the centrum is the average +vertex coordinate projected to the facet's hyperplane. Large +facets may use an old centrum to avoid recomputing the centrum +after each merge. In either case, the centrum is clearly below +neighboring facets. See Precision issues. +

    + +

    The new simplices will not be clearly convex with their +neighbors and they will not satisfy the Delaunay property. They +may even have a flipped orientation. Use triangulated input ('Qt') for Delaunay triangulations. + +

    For Delaunay triangulations with simplicial facets, the output is the +same as option 'o' without the lifted +coordinate. Since 'Ft' is invalid for merged Delaunay facets, option +'Ft' is not available for qdelaunay or qvoronoi. It may be used with +joggled input ('QJ') or triangulated output ('Qt'), for example, rbox 10 c G 0.01 | qhull d QJ Ft

    + +

    If you add a point-at-infinity with 'Qz', +it is printed after the input sites and before any centrums. It +will not be used in a Delaunay facet.

    + +

    »Fv - print vertices for +each facet

    + +

    The first line is the number of facets. Then each facet is +printed, one per line. Each line is the number of vertices +followed by the corresponding point ids. Vertices are listed in +the order they were added to the hull (the last one added is the +first listed). +

    +

    Option 'i' also lists the vertices, +but it orients facets by reversing the order of two +vertices. Option 'i' triangulates non-simplicial, 4-d and higher facets by +adding vertices for the centrums. +

    + +

    »Fv - print Voronoi +diagram

    + +

    With qvoronoi, 'Fv' prints the +Voronoi diagram or furthest-site Voronoi diagram. The first line +is the number of ridges. Then each ridge is printed, one per +line. The first number is the count of indices. The second pair +of indices indicates a pair of input sites. The remaining indices +list the corresponding ridge of Voronoi vertices. Vertex 0 is the +vertex-at-infinity. It indicates an unbounded ray.

    + +

    All vertices of a ridge are coplanar. If the ridge is +unbounded, add the midpoint of the pair of input sites. The +unbounded ray is directed from the Voronoi vertices to infinity.

    + +

    Use 'Fo' for separating +hyperplanes of outer, unbounded regions. Use 'Fi' for separating hyperplanes of +inner, bounded regions.

    + +

    Option 'Fv' does not list ridges that require more than one +midpoint. For example, the Voronoi diagram of cospherical points +lists zero ridges (e.g., 'rbox 10 s | qvoronoi Fv Qz'). +Other examples are the Voronoi diagrams of a rectangular mesh +(e.g., 'rbox 27 M1,0 | qvoronoi Fv') or a point set with +a rectangular corner (e.g., +'rbox P4,4,4 P4,2,4 P2,4,4 P4,4,2 10 | qvoronoi Fv'). +Both cases miss unbounded rays at the corners. +To determine these ridges, surround the points with a +large cube (e.g., 'rbox 10 s c G2.0 | qvoronoi Fv Qz'). +The cube needs to be large enough to bound all Voronoi regions of the original point set. +Please report any other cases that are missed. If you +can formally describe these cases or +write code to handle them, please send email to qhull@qhull.org.

    + +

    »FV - print average +vertex

    + +

    The average vertex is the average of all vertex coordinates. +It is an interior point for halfspace intersection. The first +line is the dimension and "1"; the second line is the +coordinates. For example,

    + +
    +

    qconvex FV n | qhalf Fp

    +
    + +

    prints the extreme points of the original point set (roundoff +included).

    + +

    »Fx - print extreme +points (vertices) of convex hulls and Delaunay triangulations

    + +

    The first line is the number of points. The following lines +give the index of the corresponding points. The first point is +'0'.

    + +

    In 2-d, the extreme points (vertices) are listed in +counterclockwise order (by qh_ORIENTclock in user.h).

    + +

    In 3-d and higher convex hulls, the extreme points (vertices) +are sorted by index. This is the same order as option 'p' when it doesn't include coplanar or +interior points.

    + +

    For Delaunay triangulations, 'Fx' lists the extreme +points of the input sites (i.e., the vertices of their convex hull). The points +are unordered.

    + +
    + +

    Up: Home page for Qhull
    +Up: Qhull manual: Table of Contents
    +To: Programs +• Options +• Output +• Formats +• Geomview +• Print +• Qhull +• Precision +• Trace +• Functions

    +
    + +

    The Geometry Center +Home Page

    + +

    Comments to: qhull@qhull.org +
    +Created: +Sept. 25, 1995 --- Last modified: see top +

    + + diff --git a/xs/src/qhull/html/qh-optg.htm b/xs/src/qhull/html/qh-optg.htm new file mode 100644 index 000000000..a56e29df1 --- /dev/null +++ b/xs/src/qhull/html/qh-optg.htm @@ -0,0 +1,274 @@ + + + +Qhull Geomview options (G) + + + + + +

    Up: Home page for Qhull
    +Up: Qhull manual: Table of Contents
    +To: Programs +• Options +• Output +• Formats +• Geomview +• Print +• Qhull +• Precision +• Trace +• Functions

    + +
    + + +

    [delaunay] Qhull Geomview options (G)

    + +This section lists the Geomview options for Qhull. These options are +indicated by 'G' followed by a letter. See +Output, Print, +and Format for other output options. + + +

    Copyright © 1995-2015 C.B. Barber

    + +
    + +

    » Programs + Options +• Output +• Formats +• Geomview +• Print +• Qhull +• Precision +• Trace +• Functions

    + +

    Geomview output options

    + +

    Geomview is the graphical +viewer for visualizing Qhull output in 2-d, 3-d and 4-d.

    + +

    Geomview displays each facet of the convex hull. The color of +a facet is determined by the coefficients of the facet's normal +equation. For imprecise hulls, Geomview displays the inner and +outer hull. Geomview can also display points, ridges, vertices, +coplanar points, and facet intersections.

    + +

    For 2-d Delaunay triangulations, Geomview displays the +corresponding paraboloid. Geomview displays the 2-d Voronoi +diagram. For halfspace intersections, it displays the +dual convex hull.

    + +
    +
     
    +
    General
    +
    G
    +
    display Geomview output
    +
    Gt
    +
    display transparent 3-d Delaunay triangulation
    +
    GDn
    +
    drop dimension n in 3-d and 4-d output
    + +
     
    +
     
    +
    Specific
    +
    Ga
    +
    display all points as dots
    +
    Gc
    +
    display centrums (2-d, 3-d)
    +
    Gp
    +
    display coplanar points and vertices as radii
    +
    Gh
    +
    display hyperplane intersections
    +
    Gi
    +
    display inner planes only (2-d, 3-d)
    +
    Go
    +
    display outer planes only (2-d, 3-d)
    +
    Gr
    +
    display ridges (3-d)
    +
    Gv
    +
    display vertices as spheres
    +
    Gn
    +
    do not display planes
    + +
    + +
    + +

    »G - produce output for +viewing with Geomview

    + +

    By default, option 'G' displays edges in 2-d, outer planes in +3-d, and ridges in 4-d.

    + +

    A ridge can be explicit or implicit. An explicit ridge is a (d-1)-dimensional +simplex between two facets. In 4-d, the explicit ridges are +triangles. An implicit ridge is the topological intersection of +two neighboring facets. It is the union of explicit ridges.

    + +

    For non-simplicial 4-d facets, the explicit ridges can be +quite complex. When displaying a ridge in 4-d, Qhull projects the +ridge's vertices to one of its facets' hyperplanes. Use 'Gh' to project ridges to the intersection of both +hyperplanes. This usually results in a cleaner display.

    + +

    For 2-d Delaunay triangulations, Geomview displays the +corresponding paraboloid. Geomview displays the 2-d Voronoi +diagram. For halfspace intersections, it displays the +dual convex hull. + +

    »Ga - display all +points as dots

    + +

    Each input point is displayed as a green dot.

    + +

    »Gc - display centrums +(3-d)

    + +

    The centrum is defined by a green radius sitting on a blue +plane. The plane corresponds to the facet's hyperplane. If you +sight along a facet's hyperplane, you will see that all +neighboring centrums are below the facet. The radius is defined +by 'C-n' or 'Cn'.

    + +

    »GDn - drop dimension +n in 3-d and 4-d output

    + +

    The result is a 2-d or 3-d object. In 4-d, this corresponds to +viewing the 4-d object from the nth axis without perspective. +It's best to view 4-d objects in pieces. Use the 'Pdk' 'Pg' +'PG' 'QGn' +and 'QVn' options to select a few +facets. If one of the facets is perpendicular to an axis, then +projecting along that axis will show the facet exactly as it is +in 4-d. If you generate many facets, use Geomview's ginsu +module to view the interior

    + +

    To view multiple 4-d dimensions at once, output the object +without 'GDn' and read it with Geomview's ndview. As you +rotate the object in one set of dimensions, you can see how it +changes in other sets of dimensions.

    + +

    For additional control over 4-d objects, output the object +without 'GDn' and read it with Geomview's 4dview. You +can slice the object along any 4-d plane. You can also flip the +halfspace that's deleted when slicing. By combining these +features, you can get some interesting cross sections.

    + +

    »Gh - display +hyperplane intersections (3-d, 4-d)

    + +

    In 3-d, the intersection is a black line. It lies on two +neighboring hyperplanes, c.f., the blue squares associated with +centrums ('Gc '). In 4-d, the ridges are +projected to the intersection of both hyperplanes. If you turn on +edges (Geomview's 'appearances' menu), each triangle corresponds +to one ridge. The ridges may overlap each other.

    + +

    »Gi - display inner +planes only (2-d, 3-d)

    + +

    The inner plane of a facet is below all of its vertices. It is +parallel to the facet's hyperplane. The inner plane's color is +the opposite of the outer plane's color, i.e., [1-r,1-g,1-b] . +Its edges are determined by the vertices.

    + +

    »Gn - do not display +planes

    + +

    By default, Geomview displays the precise plane (no merging) +or both inner and output planes (if merging). If merging, +Geomview does not display the inner plane if the the difference +between inner and outer is too small.

    + +

    »Go - display outer +planes only (2-d, 3-d)

    + +

    The outer plane of a facet is above all input points. It is +parallel to the facet's hyperplane. Its color is determined by +the facet's normal, and its edges are determined by the vertices.

    + +

    »Gp - display coplanar +points and vertices as radii

    + +

    Coplanar points ('Qc'), interior +points ('Qi'), outside points ('TCn' or 'TVn'), +and vertices are displayed as red and yellow radii. The radii are +perpendicular to the corresponding facet. Vertices are aligned +with an interior point. The radii define a ball which corresponds +to the imprecision of the point. The imprecision is the maximum +of the roundoff error, the centrum radius, and maxcoord * (1 - +A-n). It is at +least 1/20'th of the maximum coordinate, and ignores post merging +if pre-merging is done.

    + +

    If 'Gv' (print vertices as +spheres) is also selected, option 'Gp' displays coplanar +points as radii. Select options Qc' +and/or 'Qi'. Options 'Qc Gpv' displays +coplanar points while 'Qci Gpv' displays coplanar and interior +points. Option 'Qc' is automatically selected if 'Qi' is not +selected with options 'Gpv'. + +

    »Gr - display ridges +(3-d)

    + +

    A ridge connects the two vertices that are shared by +neighboring facets. It is displayed in green. A ridge is the +topological edge between two facets while the hyperplane +intersection is the geometric edge between two facets. Ridges are +always displayed in 4-d.

    + +

    »Gt - transparent 3-d +Delaunay

    + +

    A 3-d Delaunay triangulation looks like a convex hull with +interior facets. Option 'Gt' removes the outside ridges to reveal +the outermost facets. It automatically sets options 'Gr' and 'GDn'. See example eg.17f.delaunay.3.

    + +

    »Gv - display vertices +as spheres (2-d, 3-d)

    + +

    The radius of the sphere corresponds to the imprecision of the +data. See 'Gp' for determining the radius.

    + + + +
    + +

    Up: Home page for Qhull
    +Up: Qhull manual: Table of Contents
    +To: Programs +• Options +• Output +• Formats +• Geomview +• Print +• Qhull +• Precision +• Trace +• Functions

    + + +
    + +

    The Geometry Center +Home Page

    + +

    Comments to: qhull@qhull.org +
    +Created: Sept. 25, 1995 --- Last modified: see top

    + + diff --git a/xs/src/qhull/html/qh-opto.htm b/xs/src/qhull/html/qh-opto.htm new file mode 100644 index 000000000..e7b21745c --- /dev/null +++ b/xs/src/qhull/html/qh-opto.htm @@ -0,0 +1,353 @@ + + + + +Qhull output options + + + + +

    Up: Home page for Qhull
    +Up: Qhull manual: Table of Contents
    +To: Programs +• Options +• Output +• Formats +• Geomview +• Print +• Qhull +• Precision +• Trace +• Functions

    + +
    + +

    [delaunay] Qhull output options

    + +

    This section lists the output options for Qhull. These options +are indicated by lower case characters. See Formats, Print, and Geomview for other output +options.

    + +

    Copyright © 1995-2015 C.B. Barber

    + +
    + +

    » Programs + Options +• Output +• Formats +• Geomview +• Print +• Qhull +• Precision +• Trace +• Functions

    + +

    Output options

    + +

    Qhull prints its output to standard out. All output is printed +text. The default output is a summary (option 's'). +Other outputs may be specified as follows.

    + +
    +
    f
    +
    print all fields of all facets
    +
    n
    +
    print hyperplane normals with offsets
    +
    m
    +
    print Mathematica output (2-d and 3-d)
    +
    o
    +
    print OFF file format (dim, points and facets)
    +
    s
    +
    print summary to stderr
    +
    p
    +
    print vertex and point coordinates
    +
    i
    +
    print vertices incident to each facet
    +
     
    +
     
    +
    Related options
    +
    F
    +
    additional input/output formats
    +
    G
    +
    Geomview output
    +
    P
    +
    Print options
    +
    Ft
    +
    print triangulation with added points
    +
     
    +
    + +
    + +

    »f - print all fields of +all facets

    + +

    Print all fields of all facets. +The facet is the primary data structure for +Qhull. + +

    Option 'f' is for +debugging. Most of the fields are available via the 'F' options. If you need specialized +information from Qhull, you can use the Qhull library or C++ interface.

    + +

    Use the 'FF' option to print the +facets but not the ridges.

    + +

    »i - print vertices +incident to each facet

    + +

    The first line is the number of facets. The remaining lines +list the vertices for each facet, one facet per line. The indices +are 0-relative indices of the corresponding input points. The +facets are oriented. Option 'Fv' +displays an unoriented list of vertices with a vertex count per +line. Options 'o' and 'Ft' displays coordinates for each +vertex prior to the vertices for each facet.

    + +

    Simplicial facets (e.g., triangles in 3-d) consist of d +vertices. Non-simplicial facets in 3-d consist of 4 or more +vertices. For example, a facet of a cube consists of 4 vertices. +Use option 'Qt' to triangulate non-simplicial facets.

    + +

    For 4-d and higher convex hulls and 3-d and higher Delaunay +triangulations, d vertices are listed for all facets. A +non-simplicial facet is triangulated with its centrum and each +ridge. The index of the centrum is higher than any input point. +Use option 'Fv' to list the vertices +of non-simplicial facets as is. Use option 'Ft' to print the coordinates of the +centrums as well as those of the input points.

    + +

    »m - print Mathematica +output

    + +

    Qhull writes a Mathematica file for 2-d and 3-d convex hulls, +2-d and 3-d halfspace intersections, +and 2-d Delaunay triangulations. Qhull produces a list of +objects that you can assign to a variable in Mathematica, for +example: "list= << <outputfilename> ". +If the object is 2-d, it can be visualized by "Show[Graphics[list]] +". For 3-d objects the command is "Show[Graphics3D[list]] +". Now the object can be manipulated by commands of the +form "Show[%, <parametername> -> +<newvalue>]".

    + +

    For Delaunay triangulation orthogonal projection is better. +This can be specified, for example, by "BoxRatios: +Show[%, BoxRatios -> {1, 1, 1e-8}]". To see the +meaningful side of the 3-d object used to visualize 2-d Delaunay, +you need to change the viewpoint: "Show[%, ViewPoint +-> {0, 0, -1}]". By specifying different viewpoints +you can slowly rotate objects.

    + +

    For halfspace intersections, Qhull produces the dual +convex hull. + +

    See Is Qhull available for Mathematica? +for URLs. + +

    »n - print hyperplane +normals with offsets

    + +

    The first line is the dimension plus one. The second line is +the number of facets. The remaining lines are the normals for +each facet, one normal per line. The facet's offset follows its +normal coefficients.

    + +

    The normals point outward, i.e., the convex hull satisfies Ax +<= -b where A is the matrix of coefficients and b +is the vector of offsets.

    + +

    A point is inside or below a hyperplane if its distance +to the hyperplane is negative. A point is outside or above a hyperplane +if its distance to the hyperplane is positive. Otherwise a point is on or +coplanar to the hyperplane. + +

    If cdd output is specified ('FD'), +Qhull prints the command line, the keyword "begin", the +number of facets, the dimension (plus one), the keyword +"real", and the normals for each facet. The facet's +negative offset precedes its normal coefficients (i.e., if the +origin is an interior point, the offset is positive). Qhull ends +the output with the keyword "end".

    + +

    »o - print OFF file format +

    + +

    The output is:

    + +
      +
    • The first line is the dimension
    • +
    • The second line is the number of points, the number of + facets, and the number of ridges.
    • +
    • All of the input points follow, one per line.
    • +
    • Then Qhull prints the vertices for each facet. Each facet + is on a separate line. The first number is the number of + vertices. The remainder is the indices of the + corresponding points. The vertices are oriented in 2-d, + 3-d, and in simplicial facets.
    • +
    + +

    Option 'Ft' prints the same +information with added points for non-simplicial facets.

    + +

    Option 'i' displays vertices +without the point coordinates. Option 'p' +displays the point coordinates without vertex and facet information.

    + +

    In 3-d, Geomview can load the file directly if you delete the +first line (e.g., by piping through 'tail +2').

    + +

    For Voronoi diagrams (qvoronoi), option +'o' prints Voronoi vertices and Voronoi regions instead of input +points and facets. The first vertex is the infinity vertex +[-10.101, -10.101, ...]. Then, option 'o' lists the vertices in +the Voronoi region for each input site. The regions appear in +site ID order. In 2-d, the vertices of a Voronoi region are +sorted by adjacency (non-oriented). In 3-d and higher, the +Voronoi vertices are sorted by index. See the 'FN' option for listing Voronoi regions +without listing Voronoi vertices.

    + +

    If you are using the Qhull library, options 'v o' have the +side effect of reordering the neighbors for a vertex.

    + +

    »p - print vertex and +point coordinates

    + +

    The first line is the dimension. The second line is the number +of vertices. The remaining lines are the vertices, one vertex per +line. A vertex consists of its point coordinates

    + +

    With the 'Gc' and 'Gi' options, option 'p' also prints +coplanar and interior points respectively.

    + +

    For qvoronoi, it prints the +coordinates of each Voronoi vertex.

    + +

    For qdelaunay, it prints the +input sites as lifted to a paraboloid. For qhalf +it prints the dual points. For both, option 'p' is the same as the first +section of option 'o'.

    + +

    Use 'Fx' to list the point ids of +the extreme points (i.e., vertices).

    + +

    If a subset of the facets is selected ('Pdk', 'PDk', +'Pg' options), option 'p' only +prints vertices and points associated with those facets.

    + +

    If cdd-output format is selected ('FD'), +the first line is "begin". The second line is the +number of vertices, the dimension plus one, and "real". +The vertices follow with a leading "1". Output ends +with "end".

    + +

    »s - print summary to +stderr

    + +

    The default output of Qhull is a summary to stderr. Options 'FS' and 'Fs' +produce the same information for programs. Note: Windows 95 and 98 +treats stderr the same as stdout. Use option 'TO file' to separate +stderr and stdout.

    + +

    The summary lists the number of input points, the dimension, +the number of vertices in the convex hull, and the number of +facets in the convex hull. It lists the number of selected +("good") facets for options 'Pg', +'Pdk', qdelaunay, +or qvoronoi (Delaunay triangulations only +use the lower half of a convex hull). It lists the number of +coplanar points. For Delaunay triangulations without 'Qc', it lists the total number of +coplanar points. It lists the number of simplicial facets in +the output.

    + +

    The terminology depends on the output structure.

    + +

    The summary lists these statistics:

    + +
      +
    • number of points processed by Qhull
    • +
    • number of hyperplanes created
    • +
    • number of distance tests (not counting statistics, + summary, and checking)
    • +
    • number of merged facets (if any)
    • +
    • number of distance tests for merging (if any)
    • +
    • CPU seconds to compute the hull
    • +
    • the maximum joggle for 'QJ'
      + or, the probability of precision errors for 'QJ TRn' +
    • +
    • total area and volume (if computed, see 'FS' 'FA' + 'Fa' 'PAn')
    • +
    • max. distance of a point above a facet (if non-zero)
    • +
    • max. distance of a vertex below a facet (if non-zero)
    • +
    + +

    The statistics include intermediate hulls. For example 'rbox d +D4 | qhull' reports merged facets even though the final hull is +simplicial.

    + +

    Qhull starts counting CPU seconds after it has read and +projected the input points. It stops counting before producing +output. In the code, CPU seconds measures the execution time of +function qhull() in libqhull.c. If the number of CPU +seconds is clearly wrong, check qh_SECticks in user.h.

    + +

    The last two figures measure the maximum distance from a point +or vertex to a facet. They are not printed if less than roundoff +or if not merging. They account for roundoff error in computing +the distance (c.f., option 'Rn'). +Use 'Fs' to report the maximum outer +and inner plane.

    + +

    A number may appear in parentheses after the maximum distance +(e.g., 2.1x). It is the ratio between the maximum distance and +the worst-case distance due to merging two simplicial facets. It +should be small for 2-d, 3-d, and 4-d, and for higher dimensions +with 'Qx'. It is not printed if less +than 0.05.

    + +
    + +

    Up: Home page for Qhull
    +Up: Qhull manual: Table of Contents
    +To: Programs +• Options +• Output +• Formats +• Geomview +• Print +• Qhull +• Precision +• Trace +• Functions

    + +
    + +

    The Geometry Center +Home Page

    + +

    Comments to: qhull@qhull.org +
    +Created: Sept. 25, 1995 --- Last modified: see top

    + + diff --git a/xs/src/qhull/html/qh-optp.htm b/xs/src/qhull/html/qh-optp.htm new file mode 100644 index 000000000..9c6df90f5 --- /dev/null +++ b/xs/src/qhull/html/qh-optp.htm @@ -0,0 +1,253 @@ + + + + +Qhull print options (P) + + + + +

    Up: Home page for Qhull
    +Up: Qhull manual: Table of Contents
    +To: Programs +• Options +• Output +• Formats +• Geomview +• Print +• Qhull +• Precision +• Trace +• Functions

    + +
    + +

    [delaunay] Qhull print options (P)

    + +This section lists the print options for Qhull. These options are +indicated by 'P' followed by a letter. See +Output, Geomview, +and Format for other output options. + + +

    Copyright © 1995-2015 C.B. Barber

    + +
    + +

    » Programs + Options +• Output +• Formats +• Geomview +• Print +• Qhull +• Precision +• Trace +• Functions

    + +

    Print options

    +
    +
    +
     
    +
    General
    +
    Pp
    +
    do not report precision problems
    +
    Po
    +
    force output despite precision problems
    +
    Po
    +
    if error, output neighborhood of facet
    +
     
    +
     
    +
    Select
    +
    Pdk:n
    +
    print facets with normal[k] >= n (default 0.0)
    +
    PDk:n
    +
    print facets with normal[k] <= n
    +
    PFn
    +
    print facets whose area is at least n
    +
    Pg
    +
    print good facets only (needs 'QGn' + or 'QVn ')
    +
    PMn
    +
    print n facets with most merges
    +
    PAn
    +
    print n largest facets by area
    +
    PG
    +
    print neighbors of good facets
    +
    +
    +
    + +

    »PAn - keep n largest +facets by area

    + +

    The n largest facets are marked good for printing. This +may be useful for approximating +a hull. Unless 'PG' is set, 'Pg' +is automatically set.

    + +

    »Pdk:n - print facet if +normal[k] >= n

    + +

    For a given output, print only those facets with normal[k] >= n +and drop the others. For example, 'Pd0:0.5' prints facets with normal[0] +>= 0.5 . The default value of n is zero. For +example in 3-d, 'Pd0d1d2' prints facets in the positive octant. +

    +If no facets match, the closest facet is returned.

    +

    +On Windows 95, do not combine multiple options. A 'd' is considered +part of a number. For example, use 'Pd0:0.5 Pd1:0.5' instead of +'Pd0:0.5d1:0.5'. + +

    »PDk:n - print facet if +normal[k] <= n

    + +

    For a given output, print only those facets with normal[k] <= n +and drop the others. +For example, 'PD0:0.5' prints facets with normal[0] +<= 0.5 . The default value of n is zero. For +example in 3-d, 'PD0D1D2' displays facets in the negative octant. +

    +If no facets match, the closest facet is returned.

    + +

    In 2-d, 'd G PD2' displays the Delaunay triangulation instead +of the corresponding paraboloid.

    + +

    Be careful of placing 'Dk' or 'dk' immediately after a real +number. Some compilers treat the 'D' as a double precision +exponent.

    + +

    »PFn - keep facets whose +area is at least n

    + +

    The facets with area at least n are marked good for +printing. This may be useful for approximating a hull. Unless +'PG' is set, 'Pg' is +automatically set.

    + +

    »Pg - print good facets

    + +

    Qhull can mark facets as "good". This is used to

    + +
      +
    • mark the lower convex hull for Delaunay triangulations + and Voronoi diagrams
    • +
    • mark the facets that are visible from a point (the 'QGn ' option)
    • +
    • mark the facets that contain a point (the 'QVn' option).
    • +
    • indicate facets with a large enough area (options 'PAn' and 'PFn')
    • +
    + +

    Option 'Pg' only prints good facets that +also meet 'Pdk' and 'PDk' +options. It is automatically set for options 'PAn', +'PFn ', 'QGn', +and 'QVn'.

    + +

    »PG - print neighbors of +good facets

    + +

    Option 'PG' can be used with or without option 'Pg' +to print the neighbors of good facets. For example, options 'QGn' and 'QVn' +print the horizon facets for point n.

    + +

    »PMn - keep n facets with +most merges

    + +

    The n facets with the most merges are marked good for +printing. This may be useful for approximating a hull. Unless +'PG' is set, 'Pg' is +automatically set.

    + +

    Use option 'Fm' to print merges +per facet. + +

    »Po - force output despite +precision problems

    + +

    Use options 'Po' and 'Q0' if you +can not merge facets, triangulate the output ('Qt'), +or joggle the input (QJ). + +

    Option 'Po' can not force output when +duplicate ridges or duplicate facets occur. It may produce +erroneous results. For these reasons, merged facets, joggled input, or exact arithmetic are better.

    + +

    If you need a simplicial Delaunay triangulation, use +joggled input 'QJ' or triangulated +output 'Ft'. + +

    Option 'Po' may be used without 'Q0' +to remove some steps from Qhull or to output the neighborhood of +an error.

    + +

    Option 'Po' may be used with option 'Q5') +to skip qh_check_maxout (i.e., do not determine the maximum outside distance). +This can save a significant amount of time. + +

    If option 'Po' is used,

    + +
      +
    • most precision errors allow Qhull to continue.
    • +
    • verify ('Tv') does not check + coplanar points.
    • +
    • points are not partitioned into flipped facets and a + flipped facet is always visible to a point. This may + delete flipped facets from the output.
    • +
    + +

    »Po - if error, output +neighborhood of facet

    + +

    If an error occurs before the completion of Qhull and tracing +is not active, 'Po' outputs a neighborhood of the erroneous +facets (if any). It uses the current output options.

    + +

    See 'Po' - force output despite +precision problems. + +

    »Pp - do not report +precision problems

    + +

    With option 'Pp', Qhull does not print statistics about +precision problems, and it removes some of the warnings. It +removes the narrow hull warning.

    + + +
    + +

    Up: Home page for Qhull
    +Up: Qhull manual: Table of Contents
    +To: Programs +• Options +• Output +• Formats +• Geomview +• Print +• Qhull +• Precision +• Trace +• Functions

    + +
    + +

    The Geometry Center +Home Page

    + +

    Comments to: qhull@qhull.org +
    +Created: Sept. 25, 1995 --- Last modified: see top

    + + diff --git a/xs/src/qhull/html/qh-optq.htm b/xs/src/qhull/html/qh-optq.htm new file mode 100644 index 000000000..2edbb1fd4 --- /dev/null +++ b/xs/src/qhull/html/qh-optq.htm @@ -0,0 +1,731 @@ + + + + +Qhull control options (Q) + + + + +

    Up: Home page for Qhull
    +Up: Qhull manual: Table of Contents
    +To: Programs +• Options +• Output +• Formats +• Geomview +• Print +• Qhull +• Precision +• Trace +• Functions

    + +
    + +

    [delaunay] Qhull control options (Q)

    + +

    This section lists the control options for Qhull. These +options are indicated by 'Q' followed by a letter.

    + +

    Copyright © 1995-2015 C.B. Barber

    + +
    + +

    » Programs + Options +• Output +• Formats +• Geomview +• Print +• Qhull +• Precision +• Trace +• Functions

    + +

    Qhull control options

    + +
    +
     
    +
    General
    +
    Qu
    +
    compute upper hull for furthest-site Delaunay + triangulation
    +
    Qc
    +
    keep coplanar points with nearest facet
    +
    Qi
    +
    keep interior points with nearest facet
    +
    QJ
    +
    joggled input to avoid precision problems
    +
    Qt
    +
    triangulated output
    +
     
    +
     
    +
    Precision handling
    +
    Qz
    +
    add a point-at-infinity for Delaunay triangulations
    +
    Qx
    +
    exact pre-merges (allows coplanar facets)
    +
    Qs
    +
    search all points for the initial simplex
    +
    Qbb
    +
    scale last coordinate to [0,m] for Delaunay
    +
    Qv
    +
    test vertex neighbors for convexity
    +
     
    +
     
    +
    Transform input
    +
    Qbk:0Bk:0
    +
    drop dimension k from input
    +
    QRn
    +
    random rotation (n=seed, n=0 time, n=-1 time/no rotate)
    +
    Qbk:n
    +
    scale coord[k] to low bound of n (default -0.5)
    +
    QBk:n
    +
    scale coord[k] to upper bound of n (default 0.5)
    +
    QbB
    +
    scale input to fit the unit cube
    +
     
    +
     
    +
    Select facets
    +
    QVn
    +
    good facet if it includes point n, -n if not
    +
    QGn
    +
    good facet if visible from point n, -n for not visible
    +
    Qg
    +
    only build good facets (needs 'QGn', 'QVn ', or 'Pdk')
    +
     
    +
     
    +
    Experimental
    +
    Q4
    +
    avoid merging old facets into new facets
    +
    Q5
    +
    do not correct outer planes at end of qhull
    +
    Q3
    +
    do not merge redundant vertices
    +
    Q6
    +
    do not pre-merge concave or coplanar facets
    +
    Q0
    +
    do not pre-merge facets with 'C-0' or 'Qx'
    +
    Q8
    +
    ignore near-interior points
    +
    Q2
    +
    merge all non-convex at once instead of independent sets
    +
    Qf
    +
    partition point to furthest outside facet
    +
    Q7
    +
    process facets depth-first instead of breadth-first
    +
    Q9
    +
    process furthest of furthest points
    +
    Q10
    +
    no special processing for narrow distributions
    +
    Q11
    +
    copy normals and recompute centrums for tricoplanar facets
    +
    Q12
    +
    do not error on wide merge due to duplicate ridge and nearly coincident points
    +
    Qm
    +
    process points only if they would increase the max. outer + plane
    +
    Qr
    +
    process random outside points instead of furthest one
    +
    Q1
    +
    sort merges by type instead of angle
    +
    + +
    + +

    »Qbb - scale the last +coordinate to [0,m] for Delaunay

    + +

    After scaling with option 'Qbb', the lower bound of the last +coordinate will be 0 and the upper bound will be the maximum +width of the other coordinates. Scaling happens after projecting +the points to a paraboloid and scaling other coordinates.

    + +

    Option 'Qbb' is automatically set for qdelaunay +and qvoronoi. Option 'Qbb' is automatically set for joggled input 'QJ'.

    + +

    Option 'Qbb' should be used for Delaunay triangulations with +integer coordinates. Since the last coordinate is the sum of +squares, it may be much larger than the other coordinates. For +example, rbox 10000 D2 B1e8 | qhull d has precision +problems while rbox 10000 D2 B1e8 | qhull d Qbb is OK.

    + +

    »QbB - scale the input to +fit the unit cube

    + +

    After scaling with option 'QbB', the lower bound will be -0.5 +and the upper bound +0.5 in all dimensions. For different bounds +change qh_DEFAULTbox in user.h (0.5 is best for Geomview).

    + +

    For Delaunay and Voronoi diagrams, scaling happens after +projection to the paraboloid. Under precise arithmetic, scaling +does not change the topology of the convex hull. Scaling may +reduce precision errors if coordinate values vary widely.

    + +

    »Qbk:n - scale coord[k] +to low bound

    + +

    After scaling, the lower bound for dimension k of the input +points will be n. 'Qbk' scales coord[k] to -0.5.

    + +

    »QBk:n - scale coord[k] +to upper bound

    + +

    After scaling, the upper bound for dimension k of the input +points will be n. 'QBk' scales coord[k] to 0.5.

    + +

    »Qbk:0Bk:0 - drop +dimension k from the input points

    + +

    Drop dimension k from the input points. For example, +'Qb1:0B1:0' deletes the y-coordinate from all input points. This +allows the user to take convex hulls of sub-dimensional objects. +It happens before the Delaunay and Voronoi transformation. +It happens after the halfspace transformation for both the data +and the feasible point.

    + +

    »Qc - keep coplanar points +with nearest facet

    + +

    During construction of the hull, a point is coplanar if it is +between 'Wn' above and 'Un' below a facet's hyperplane. A +different definition is used for output from Qhull.

    + +

    For output, a coplanar point is above the minimum vertex +(i.e., above the inner plane). With joggle ('QJ'), a coplanar point includes points +within one joggle of the inner plane.

    + +

    With option 'Qc', output formats 'p ', +'f', 'Gp', +'Fc', 'FN', +and 'FP' will print the coplanar +points. With options 'Qc Qi' these outputs +include the interior points.

    + +

    For Delaunay triangulations (qdelaunay +or qvoronoi), a coplanar point is a point +that is nearly incident to a vertex. All input points are either +vertices of the triangulation or coplanar.

    + +

    Qhull stores coplanar points with a facet. While constructing +the hull, it retains all points within qh_RATIOnearInside +(user.h) of a facet. In qh_check_maxout(), it uses these points +to determine the outer plane for each facet. With option 'Qc', +qh_check_maxout() retains points above the minimum vertex for the +hull. Other points are removed. If qh_RATIOnearInside is wrong or +if options 'Q5 Q8' are set, a +coplanar point may be missed in the output (see Qhull limitations).

    + +

    »Qf - partition point to +furthest outside facet

    + +

    After adding a new point to the convex hull, Qhull partitions +the outside points and coplanar points of the old, visible +facets. Without the 'f ' option and +merging, it assigns a point to the first facet that it is outside +('Wn'). When merging, it assigns a +point to the first facet that is more than several times outside +(see qh_DISToutside in user.h).

    + +

    If option 'Qf' is selected, Qhull performs a directed search +(no merging) or an exhaustive search (merging) of new facets. +Option 'Qf' may reduce precision errors if pre-merging does not +occur.

    + +

    Option 'Q9' processes the furthest of all +furthest points.

    + +

    »Qg - only build good +facets (needs 'QGn' 'QVn' or 'Pdk')

    + +

    Qhull has several options for defining and printing good +facets. With the 'Qg' option, Qhull will only +build those facets that it needs to determine the good facets in +the output. This may speed up Qhull in 2-d and 3-d. It is +useful for furthest-site Delaunay +triangulations (qdelaunay Qu, +invoke with 'qhull d Qbb Qu Qg'). +It is not effective in higher +dimensions because many facets see a given point and contain a +given vertex. It is not guaranteed to work for all combinations.

    + +

    See 'QGn', 'QVn', and 'Pdk' for defining good facets, and 'Pg' and 'PG' +for printing good facets and their neighbors. If pre-merging ('C-n') is not used and there are +coplanar facets, then 'Qg Pg' may produce a different result than +'Pg'.

    + +

    »QGn - good facet if +visible from point n, -n for not visible

    + +

    With option 'QGn', a facet is good (see 'Qg' +and 'Pg') if it is visible from +point n. If n < 0, a facet is good if it is not visible +from point n. Point n is not added to the hull (unless 'TCn' or 'TPn').

    + +

    With rbox, use the 'Pn,m,r' option +to define your point; it will be point 0 ('QG0').

    + +

    »Qi - keep interior points +with nearest facet

    + +

    Normally Qhull ignores points that are clearly interior to the +convex hull. With option 'Qi', Qhull treats interior points the +same as coplanar points. Option 'Qi' does not retain coplanar +points. You will probably want 'Qc ' as well.

    + +

    Option 'Qi' is automatically set for 'qdelaunay +Qc' and 'qvoronoi +Qc'. If you use +'qdelaunay Qi' or 'qvoronoi +Qi', option 's' reports all nearly +incident points while option 'Fs' +reports the number of interior points (should always be zero).

    + +

    With option 'Qi', output formats 'p', +'f','Gp', +'Fc', 'FN', +and 'FP' include interior points.

    + +

    »QJ or QJn - joggled +input to avoid precision errors

    + +

    Option 'QJ' or 'QJn' joggles each input coordinate by adding a +random number in the range [-n,n]. If a precision error occurs, +It tries again. If precision errors still occur, Qhull increases n +ten-fold and tries again. The maximum value for increasing n +is 0.01 times the maximum width of the input. Option 'QJ' selects +a default value for n. User.h +defines these parameters and a maximum number of retries. See Merged facets or joggled input.

    + +

    Users of joggled input should consider converting to +triangulated output ('Qt'). Triangulated output is +approximately 1000 times more accurate than joggled input. + +

    Option 'QJ' also sets 'Qbb' for +Delaunay triangulations and Voronoi diagrams. It does not set +'Qbb' if 'Qbk:n' or 'QBk:n' are set.

    + +

    If 'QJn' is set, Qhull does not merge facets unless requested +to. All facets are simplicial (triangular in 2-d). This may be +important for your application. You may also use triangulated output +('Qt') or Option 'Ft'. + +

    Qhull adjusts the outer and inner planes for 'QJn' ('Fs'). They are increased by sqrt(d)*n +to account for the maximum distance between a joggled point and +the corresponding input point. Coplanar points ('Qc') require an additional sqrt(d)*n +since vertices and coplanar points may be joggled in opposite +directions.

    + +

    For Delaunay triangulations (qdelaunay), joggle +happens before lifting the input sites to a paraboloid. Instead of +'QJ', you may use triangulated output ('Qt')

    + +

    This option is deprecated for Voronoi diagrams (qvoronoi). +It triangulates cospherical points, leading to duplicated Voronoi vertices.

    + +

    By default, 'QJn' uses a fixed random number seed. To use time +as the random number seed, select 'QR-1'. +The summary ('s') will show the +selected seed as 'QR-n'. + +

    With 'QJn', Qhull does not error on degenerate hyperplane +computations. Except for Delaunay and Voronoi computations, Qhull +does not error on coplanar points.

    + +

    Use option 'FO' to display the +selected options. Option 'FO' displays the joggle and the joggle +seed. If Qhull restarts, it will redisplay the options.

    + +

    Use option 'TRn' to estimate the +probability that Qhull will fail for a given 'QJn'. + +

    »Qm - only process points +that increase the maximum outer plane

    + +

    Qhull reports the maximum outer plane in its summary ('s'). With option 'Qm', Qhull does not +process points that are below the current, maximum outer plane. +This is equivalent to always adjusting 'Wn +' to the maximum distance of a coplanar point to a facet. It +is ignored for points above the upper convex hull of a Delaunay +triangulation. Option 'Qm' is no longer important for merging.

    + +

    »Qr - process random +outside points instead of furthest ones

    + +

    Normally, Qhull processes the furthest point of a facet's +outside points. Option 'Qr' instead selects a random outside +point for processing. This makes Qhull equivalent to the +randomized incremental algorithms.

    + +

    The original randomization algorithm by Clarkson and Shor ['89] used a conflict list which +is equivalent to Qhull's outside set. Later randomized algorithms +retained the previously constructed facets.

    + +

    To compare Qhull to the randomized algorithms with option +'Qr', compare "hyperplanes constructed" and +"distance tests". Qhull does not report CPU time +because the randomization is inefficient.

    + +

    »QRn - random rotation

    + +

    Option 'QRn' randomly rotates the input. For Delaunay +triangulations (qdelaunay or qvoronoi), +it rotates the lifted points about +the last axis.

    + +

    If n=0, use time as the random number seed. If n>0, +use n as the random number seed. If n=-1, don't rotate +but use time as the random number seed. If n<-1, +don't rotate but use n as the random number seed.

    + +

    »Qs - search all points +for the initial simplex

    + +

    Qhull constructs an initial simplex from d+1 points. It +selects points with the maximum and minimum coordinates and +non-zero determinants. If this fails, it searches all other +points. In 8-d and higher, Qhull selects points with the minimum +x or maximum coordinate (see qh_initialvertices in poly2.c ). +It rejects points with nearly zero determinants. This should work +for almost all input sets.

    + +

    If Qhull can not construct an initial simplex, it reports a +descriptive message. Usually, the point set is degenerate and one +or more dimensions should be removed ('Qbk:0Bk:0'). +If not, use option 'Qs'. It performs an exhaustive search for the +best initial simplex. This is expensive is high dimensions.

    + +

    »Qt - triangulated output

    + +

    By default, qhull merges facets to handle precision errors. This +produces non-simplicial facets (e.g., the convex hull of a cube has 6 square +facets). Each facet is non-simplicial because it has four vertices. + +

    Use option 'Qt' to triangulate all non-simplicial facets before generating +results. Alternatively, use joggled input ('QJ') to +prevent non-simplical facets. Unless 'Pp' is set, +qhull produces a warning if 'QJ' and 'Qt' are used together. + +

    For Delaunay triangulations (qdelaunay), triangulation +occurs after lifting the input sites to a paraboloid and computing the convex hull. +

    + +

    Option 'Qt' is deprecated for Voronoi diagrams (qvoronoi). +It triangulates cospherical points, leading to duplicated Voronoi vertices.

    + +

    Option 'Qt' may produce degenerate facets with zero area.

    + +

    Facet area and hull volumes may differ with and without +'Qt'. The triangulations are different and different triangles +may be ignored due to precision errors. + +

    With sufficient merging, the ridges of a non-simplicial facet may share more than two neighboring facets. If so, their triangulation ('Qt') will fail since two facets have the same vertex set.

    + +

    »Qu - compute upper hull +for furthest-site Delaunay triangulation

    + +

    When computing a Delaunay triangulation (qdelaunay +or qvoronoi), +Qhull computes both the the convex hull of points on a +paraboloid. It normally prints facets of the lower hull. These +correspond to the Delaunay triangulation. With option 'Qu', Qhull +prints facets of the upper hull. These correspond to the furthest-site Delaunay triangulation +and the furthest-site Voronoi diagram.

    + +

    Option 'qhull d Qbb Qu Qg' may improve the speed of option +'Qu'. If you use the Qhull library, a faster method is 1) use +Qhull to compute the convex hull of the input sites; 2) take the +extreme points (vertices) of the convex hull; 3) add one interior +point (e.g., +'FV', the average of d extreme points); 4) run +'qhull d Qbb Qu' or 'qhull v Qbb Qu' on these points.

    + +

    »Qv - test vertex +neighbors for convexity

    + +

    Normally, Qhull tests all facet neighbors for convexity. +Non-neighboring facets which share a vertex may not satisfy the +convexity constraint. This occurs when a facet undercuts the +centrum of another facet. They should still be convex. Option +'Qv' extends Qhull's convexity testing to all neighboring facets +of each vertex. The extra testing occurs after the hull is +constructed..

    + +

    »QVn - good facet if it +includes point n, -n if not

    + +

    With option 'QVn', a facet is good ('Qg', +'Pg') if one of its vertices is +point n. If n<0, a good facet does not include point n. + +

    If options 'PG' +and 'Qg' are not set, option 'Pg' +(print only good) +is automatically set. +

    + +

    Option 'QVn' behaves oddly with options 'Fx' +and 'qvoronoi Fv'. + +

    If used with option 'Qg' (only process good facets), point n is +either in the initial simplex or it is the first +point added to the hull. Options 'QVn Qg' require either 'QJ' or +'Q0' (no merging).

    + +

    »Qx - exact pre-merges +(allows coplanar facets)

    + +

    Option 'Qx' performs exact merges while building the hull. +Option 'Qx' is set by default in 5-d and higher. Use option 'Q0' to not use 'Qx' by default. Unless otherwise +specified, option 'Qx' sets option 'C-0'. +

    + +

    The "exact" merges are merging a point into a +coplanar facet (defined by 'Vn ', 'Un', and 'C-n'), +merging concave facets, merging duplicate ridges, and merging +flipped facets. Coplanar merges and angle coplanar merges ('A-n') are not performed. Concavity +testing is delayed until a merge occurs.

    + +

    After the hull is built, all coplanar merges are performed +(defined by 'C-n' and 'A-n'), then post-merges are performed +(defined by 'Cn' and 'An'). If facet progress is logged ('TFn'), Qhull reports each phase and +prints intermediate summaries and statistics ('Ts').

    + +

    Without 'Qx' in 5-d and higher, options 'C-n' and 'A-n' +may merge too many facets. Since redundant vertices are not +removed effectively, facets become increasingly wide.

    + +

    Option 'Qx' may report a wide facet. With 'Qx', coplanar +facets are not merged. This can produce a "dent" in an +intermediate hull. If a point is partitioned into a dent and it +is below the surrounding facets but above other facets, one or +more wide facets will occur. In practice, this is unlikely. To +observe this effect, run Qhull with option 'Q6' +which doesn't pre-merge concave facets. A concave facet makes a +large dent in the intermediate hull.

    + +

    Option 'Qx' may set an outer plane below one of the input +points. A coplanar point may be assigned to the wrong facet +because of a "dent" in an intermediate hull. After +constructing the hull, Qhull double checks all outer planes with +qh_check_maxout in poly2.c . If a coplanar point is +assigned to the wrong facet, qh_check_maxout may reach a local +maximum instead of locating all coplanar facets. This appears to +be unlikely.

    + +

    »Qz - add a +point-at-infinity for Delaunay triangulations

    + +

    Option 'Qz' adds a point above the paraboloid of lifted sites +for a Delaunay triangulation. It allows the Delaunay +triangulation of cospherical sites. It reduces precision errors +for nearly cospherical sites.

    + +

    »Q0 - no merging with C-0 +and Qx

    + +

    Turn off default merge options 'C-0' +and 'Qx'.

    + +

    With 'Q0' and without other pre-merge options, Qhull ignores +precision issues while constructing the convex hull. This may +lead to precision errors. If so, a descriptive warning is +generated. See Precision issues.

    + +

    »Q1 - sort merges by type +instead of angle

    + +

    Qhull sorts the coplanar facets before picking a subset of the +facets to merge. It merges concave and flipped facets first. Then +it merges facets that meet at a steep angle. With 'Q1', Qhull +sorts merges by type (coplanar, angle coplanar, concave) instead +of by angle. This may make the facets wider.

    + +

    »Q2 - merge all non-convex +at once instead of independent sets

    + +

    With 'Q2', Qhull merges all facets at once instead of +performing merges in independent sets. This may make the facets +wider.

    + +

    »Q3 - do not merge +redundant vertices

    + +

    With 'Q3', Qhull does not remove redundant vertices. In 6-d +and higher, Qhull never removes redundant vertices (since +vertices are highly interconnected). Option 'Q3' may be faster, +but it may result in wider facets. Its effect is easiest to see +in 3-d and 4-d.

    + +

    »Q4 - avoid merging old +facets into new facets

    + +

    With 'Q4', Qhull avoids merges of an old facet into a new +facet. This sometimes improves facet width and sometimes makes it +worse.

    + +

    »Q5 - do not correct outer +planes at end of qhull

    + +

    When merging facets or approximating a hull, Qhull tests +coplanar points and outer planes after constructing the hull. It +does this by performing a directed search (qh_findbest in geom.c). +It includes points that are just inside the hull.

    + +

    With options 'Q5' or 'Po', Qhull +does not test outer planes. The maximum outer plane is used +instead. Coplanar points ('Qc') are defined by +'Un'. An input point may be outside +of the maximum outer plane (this appears to be unlikely). An +interior point may be above 'Un' +from a hyperplane.

    + +

    Option 'Q5' may be used if outer planes are not needed. Outer +planes are needed for options 's', 'G', 'Go ', +'Fs', 'Fo', +'FF', and 'f'.

    + +

    »Q6 - do not pre-merge +concave or coplanar facets

    + +

    With 'Q6', Qhull does not pre-merge concave or coplanar +facets. This demonstrates the effect of "dents" when +using 'Qx'.

    + +

    »Q7 - depth-first +processing instead of breadth-first

    + +

    With 'Q7', Qhull processes facets in depth-first order instead +of breadth-first order. This may increase the locality of +reference in low dimensions. If so, Qhull may be able to use +virtual memory effectively.

    + +

    In 5-d and higher, many facets are visible from each +unprocessed point. So each iteration may access a large +proportion of allocated memory. This makes virtual memory +ineffectual. Once real memory is used up, Qhull will spend most +of its time waiting for I/O.

    + +

    Under 'Q7', Qhull runs slower and the facets may be wider.

    + +

    »Q8 - ignore near-interior +points

    + +

    With 'Q8' and merging, Qhull does not process interior points +that are near to a facet (as defined by qh_RATIOnearInside in +user.h). This avoids partitioning steps. It may miss a coplanar +point when adjusting outer hulls in qh_check_maxout(). The best +value for qh_RATIOnearInside is not known. Options 'Q8 Qc' may be sufficient.

    + +

    »Q9 - process furthest of +furthest points

    + +

    With 'Q9', Qhull processes the furthest point of all outside +sets. This may reduce precision problems. The furthest point of +all outside sets is not necessarily the furthest point from the +convex hull.

    + +

    »Q10 - no special processing +for narrow distributions

    + +

    With 'Q10', Qhull does not special-case narrow distributions. +See Limitations of merged facets for +more information. + +

    »Q11 - copy normals and recompute +centrums for +tricoplanar facets

    + +Option 'Qt' triangulates non-simplicial facets +into "tricoplanar" facets. +Normally tricoplanar facets share the same normal, centrum, and +Voronoi vertex. They can not be merged or replaced. With +option 'Q11', Qhull duplicates the normal and Voronoi vertex. +It recomputes the centrum. + +

    Use 'Q11' if you use the Qhull library to add points +incrementally and call qh_triangulate() after each point. +Otherwise, Qhull will report an error when it tries to +merge and replace a tricoplanar facet. + +

    With sufficient merging and new points, option 'Q11' may +lead to precision problems such +as duplicate ridges and concave facets. For example, if qh_triangulate() +is added to qh_addpoint(), RBOX 1000 s W1e-12 t1001813667 P0 | QHULL d Q11 Tv, +reports an error due to a duplicate ridge. + +

    »Q12 - do not error +on wide merge due to duplicate ridge and nearly coincident points

    + +

    In 3-d and higher Delaunay Triangulations or 4-d and higher convex hulls, multiple, +nearly coincident points may lead to very wide facets. An error is reported if a +merge across a duplicate ridge would increase the facet width by 100x or more. + +

    Use option 'Q12' to log a warning instead of throwing an error. + +

    For Delaunay triangulations, a bounding box may alleviate this error (e.g., rbox 500 C1,1E-13 t c G1 | qhull d). +This avoids the ill-defined edge between upper and lower convex hulls. +The problem will be fixed in a future release of Qhull. + +

    To demonstrate the problem, use rbox option 'Cn,r,m' to generate nearly coincident points. +For more information, see "Nearly coincident points on an edge" +in Nearly coincident points on an edge. + + +


    + +

    Up: Home page for Qhull
    +Up: Qhull manual: Table of Contents
    +To: Programs +• Options +• Output +• Formats +• Geomview +• Print +• Qhull +• Precision +• Trace +• Functions

    + +
    + +

    The Geometry Center +Home Page

    + +

    Comments to: qhull@qhull.org +
    +Created: Sept. 25, 1995 --- Last modified: see top

    + + diff --git a/xs/src/qhull/html/qh-optt.htm b/xs/src/qhull/html/qh-optt.htm new file mode 100644 index 000000000..0709f58c6 --- /dev/null +++ b/xs/src/qhull/html/qh-optt.htm @@ -0,0 +1,278 @@ + + + + +Qhull trace options (T) + + + + +

    Up: Home page for Qhull
    +Up: Qhull manual: Table of Contents
    +To: Programs +• Options +• Output +• Formats +• Geomview +• Print +• Qhull +• Precision +• Trace +• Functions

    + +
    + +

    [delaunay] Qhull trace options (T)

    + +This section lists the trace options for Qhull. These options are +indicated by 'T' followed by a letter. + +

    Copyright © 1995-2015 C.B. Barber

    + +
    + +

    » Programs + Options +• Output +• Formats +• Geomview +• Print +• Qhull +• Precision +• Trace +• Functions

    + +

    Trace options

    + +
    +
     
    +
    General
    +
    Tz
    +
    output error information to stdout instead of stderr
    +
    TI file
    +
    input data from a file
    +
    TO file
    +
    output results to a file
    +
    Ts
    +
    print statistics
    +
    TFn
    +
    report progress whenever n or more facets created
    +
    TRn
    +
    rerun qhull n times
    +
    Tv
    +
    verify result: structure, convexity, and point inclusion
    + +
     
    +
     
    +
    Debugging
    +
    Tc
    +
    check frequently during execution
    +
    TVn
    +
    stop qhull after adding point n
    +
    TCn
    +
    stop qhull after building cone for point n
    +
    TV-n
    +
    stop qhull before adding point n
    +
    T4
    +
    trace at level n, 4=all, 5=mem/gauss, -1= events
    +
    TWn
    +
    trace merge facets when width > n
    +
    TMn
    +
    turn on tracing at merge n
    +
    TPn
    +
    turn on tracing when point n added to hull
    +
    + +
    + +

    »Tc - check frequently +during execution

    + +

    Qhull includes frequent checks of its data structures. Option +'Tc' will catch most inconsistency errors. It is slow and should +not be used for production runs. Option 'Tv' +performs the same checks after the hull is constructed.

    + +

    »TCn - stop qhull after +building cone for point n

    + +

    Qhull builds a cone from the point to its horizon facets. +Option 'TCn' stops Qhull just after building the cone. The output +for 'f' includes the cone and the old +hull.'.

    + +

    »TFn - report summary +whenever n or more facets created

    + +

    Option 'TFn' reports progress whenever more than n facets are +created. The test occurs just before adding a new point to the +hull. During post-merging, 'TFn' reports progress after more than +n/2 merges.

    + +

    »TI file - input data from file

    + +

    Input data from 'file' instead of stdin. The filename may not +contain spaces or use single quotes. +You may use I/O redirection +instead (e.g., 'rbox 10 | qdelaunay >results').

    + +

    »TMn - turn on tracing at +merge n

    + +

    Turn on tracing at n'th merge.

    + +

    »Tn - trace at level n

    + +

    Qhull includes full execution tracing. 'T-1' traces events. +'T1' traces the overall execution of the program. 'T2' and 'T3' +trace overall execution and geometric and topological events. +'T4' traces the algorithm. 'T5' includes information about memory +allocation and Gaussian elimination. 'T1' is useful for logging +progress of Qhull in high dimensions.

    + +

    Option 'Tn' can produce large amounts of output. Use options 'TPn', 'TWn', and 'TMn' to selectively +turn on tracing. Since all errors report the last processed +point, option 'TPn' is particularly useful.

    + +

    Different executions of the same program may produce different +traces and different results. The reason is that Qhull uses hashing +to match ridges of non-simplicial facets. For performance reasons, +the hash computation uses +memory addresses which may change across executions. + +

    »TO file - output results to file

    + +

    Redirect stdout to 'file'. The filename may be enclosed in +single quotes. Unix and Windows NT users may use I/O redirection +instead (e.g., 'rbox 10 | qdelaunay >results').

    +

    +Windows95 users should always use 'TO file'. If they use I/O redirection, +error output is not sent to the console. Qhull uses single quotes instead +of double quotes because a missing double quote can +freeze Windows95 (e.g., do not run, rbox 10 | qhull TO "x)

    +

    + +

    »TPn - turn on tracing +when point n added to hull

    + +

    Option 'TPn' turns on tracing when point n is added to +the hull. It also traces partitions of point n. This option +reduces the output size when tracing. It is the normal +method to determine the cause of a Qhull error. All Qhull errors +report the last point added. + +

    Use options 'TPn TVn' to +trace the addition of point n to the convex hull and stop when done.

    + +

    If used with option 'TWn', +'TPn' turns off tracing after adding point n to the hull. +Use options 'TPn TWn' to +trace the addition of point n to the convex hull, partitions +of point n, and wide merges.

    + +

    »TRn - rerun qhull n times

    + +

    Option 'TRn' reruns Qhull n times. It is usually used +with 'QJn' to determine the probability +that a given joggle will fail. The summary +('s') lists the failure +rate and the precision errors that occurred. +Option 'Ts' will report statistics for +all of the runs. Trace and output options only apply to the last +run. An event trace, 'T-1' reports events for all runs. + +

    Tracing applies to the last run of Qhull. If an error +is reported, the options list the run number as "_run". +To trace this run, set 'TRn' to the same value.

    + +

    »Ts - print statistics

    + +

    Option 'Ts' collects statistics and prints them to stderr. For +Delaunay triangulations, the angle statistics are restricted to +the lower or upper envelope.

    + +

    »Tv - verify result: +structure, convexity, and point inclusion

    + +

    Option 'Tv' checks the topological structure, convexity, and +point inclusion. If precision problems occurred, facet convexity +is tested whether or not 'Tv' is selected. Option 'Tv' does not +check point inclusion if forcing output with 'Po', or if 'Q5' +is set.

    + +

    The convex hull of a set of points is the smallest polytope +that includes the points. Option 'Tv' tests point inclusion. +Qhull verifies that all points are below all outer planes +(facet->maxoutside). Point inclusion is exhaustive if merging +or if the facet-point product is small enough; otherwise Qhull +verifies each point with a directed search (qh_findbest). To +force an exhaustive test when using option 'C-0' (default), use 'C-1e-30' instead.

    + +

    Point inclusion testing occurs after producing output. It +prints a message to stderr unless option 'Pp' is used. This allows the user to +interrupt Qhull without changing the output.

    + +

    With 'qvoronoi Fi' +and 'qvoronoi Fo', +option 'Tv' collects statistics that verify all Voronoi vertices lie +on the separating hyperplane, and for bounded regions, all +separating hyperplanes are perpendicular bisectors. + +

    »TV-n - stop qhull before +adding point n

    + +

    Qhull adds one point at a time to the convex hull. See how Qhull adds a point. Option 'TV-n' +stops Qhull just before adding a new point. Output shows the hull +at this time.

    + +

    »TVn - stop qhull after +adding point n

    + +

    Option 'TVn' stops Qhull after it has added point n. Output +shows the hull at this time.

    + +

    »TWn - trace merge facets +when width > n

    + +

    Along with TMn, this option allows the user to determine the +cause of a wide merge.

    +

    »Tz - send all output to +stdout

    + +

    Redirect stderr to stdout.

    + + +
    + +

    Up: Home page for Qhull
    +Up: Qhull manual: Table of Contents
    +To: Programs +• Options +• Output +• Formats +• Geomview +• Print +• Qhull +• Precision +• Trace +• Functions

    + +
    + +

    The Geometry Center +Home Page

    + +

    Comments to: qhull@qhull.org +
    +Created: Sept. 25, 1995 --- Last modified: see top

    + + diff --git a/xs/src/qhull/html/qh-quick.htm b/xs/src/qhull/html/qh-quick.htm new file mode 100644 index 000000000..9d52e7d75 --- /dev/null +++ b/xs/src/qhull/html/qh-quick.htm @@ -0,0 +1,495 @@ + + + + +Qhull quick reference + + + + +

    Up: Home +page for Qhull
    +Up: Qhull manual
    +To: Programs +• Options +• Output +• Formats +• Geomview +• Print +• Qhull +• Precision +• Trace +• Functions
    +To: Qhull internals
    +To: Qhull functions, macros, and data structures
    +To: Qhull files
    +To: GeomGlobal +• IoMem +• MergePoly +• QhullSet +• StatUser +

    + +
    + +

    [cone] Qhull quick reference

    + +This section lists all programs and options in Qhull. + +

    Copyright © 1995-2015 C.B. Barber

    + +

    +  +


    +Qhull programs +

    » Programs +• Options +• Output +• Formats +• Geomview +• Print +• Qhull +• Precision +• Trace +• Functions

    + +
    +
    qconvex -- convex hull
    +
    synopsis • input • outputs • controls • graphics • notes • conventions • options
    +
     
    +
    qdelaunay -- Delaunay triangulation
    +
    synopsis • input • outputs • controls • graphics • notes • conventions • options
    +
     
    +
    qdelaunay Qu -- furthest-site Delaunay triangulation
    +
    synopsis • input • outputs • controls • graphics • notes • conventions • options
    +
     
    +
    qhalf -- halfspace intersection about a point
    +
    synopsis • input • outputs • controls • graphics • notes • conventions • options
    +
     
    +
    qvoronoi -- Voronoi diagram
    +
    synopsis • input • outputs • + controls • graphics • notes • conventions • options
    +
     
    +
    qvoronoi Qu -- furthest-site Voronoi diagram
    +
    synopsis • input • outputs • controls • graphics • notes • conventions • options
    +
     
    +
    rbox -- generate point distributions for qhull
    +
    synopsis • outputs • examples • notes • options
    +
     
    +
    qhull -- convex hull and related structures
    +
    synopsis • input • outputs • controls • options
    +
    +  +
    +Qhull options + +

    » Programs +• Options +• Output +• Formats +• Geomview +• Print +• Qhull +• Precision +• Trace +• Functions

    + +

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    'Fa' +Farea +'FA' +FArea-total +'Fc' +Fcoplanars +'FC' +FCentrums + +
    'Fd' +Fd-cdd-in +'FD' +FD-cdd-out +'FF' +FF-dump-xridge +'Fi' +Finner + +
    'Fi' +Finner_bounded +'FI' +FIDs +'Fm' +Fmerges +'FM' +FMaple + +
    'Fn' +Fneighbors +'FN' +FNeigh-vertex +'Fo' +Fouter +'Fo' +Fouter_unbounded + +
    'FO' +FOptions +'Fp' +Fpoint-intersect +'FP' +FPoint_near +'FQ' +FQhull + +
    'Fs' +Fsummary +'FS' +FSize +'Ft' +Ftriangles +'Fv' +Fvertices + +
    'Fv' +Fvoronoi +'FV' +FVertex-ave +'Fx' +Fxtremes + +Merged facets or joggled input + +
     
    'PAn' +PArea-keep +'Pdk:n' +Pdrop_low +'PDk:n' +Pdrop_high +'Pg' +Pgood + +
    'PFn' +PFacet_area_keep +'PG' +PGood_neighbors +'PMn' +PMerge-keep +'Po' +Poutput_forced + +
    'Po' +Poutput_error +'Pp' +Pprecision_not + +
     
    'd' +delaunay +'v' +voronoi +'G' +Geomview +'H' +Halfspace + +
    'f' +facet_dump +'i' +incidences +'m' +mathematica +'n' +normals + +
    'o' +OFF_format +'p' +points +'s' +summary + +
     
    'Gv' +Gvertices +'Gp' +Gpoints +'Ga' +Gall_points +'Gn' +Gno_planes + +
    'Gi' +Ginner +'Gc' +Gcentrums +'Gh' +Ghyperplanes +'Gr' +Gridges + +
    'Go' +Gouter +'GDn' +GDrop_dim +'Gt' +Gtransparent + +
     
    'T4' +T4_trace +'Tc' +Tcheck_often +'Ts' +Tstatistics +'Tv' +Tverify + +
    'Tz' +Tz_stdout +'TFn' +TFacet_log +'TI file' +TInput_file +'TPn' +TPoint_trace + +
    'TMn' +TMerge_trace +'TO file' +TOutput_file +'TRn' +TRerun +'TWn' +TWide_trace + +
    'TV-n' +TVertex_stop_before +
    'TVn' +TVertex_stop_after +'TCn' +TCone_stop_after + +
     
    'A-n' +Angle_max_pre +'An' +Angle_max_post +'C-0' +Centrum_roundoff +'C-n' +Centrum_size_pre + +
    'Cn' +Centrum_size_post +'En' +Error_round +'Rn' +Random_dist +'Vn' +Visible_min + +
    'Un' +Ucoplanar_max +'Wn' +Wide_outside + +
     
    'Qbk:n' +Qbound_low +'QBk:n' +QBound_high +'Qbk:0Bk:0' +Qbound_drop +'QbB' +QbB-scale-box + +
    'Qbb' +Qbb-scale-last +'Qc' +Qcoplanar +'Qf' +Qfurthest +'Qg' +Qgood_only + +
    'QGn' +QGood_point +'Qi' +Qinterior +'Qm' +Qmax_out +'QJn' +QJoggle + +
    'Qr' +Qrandom +'QRn' +QRotate +'Qs' +Qsearch_1st +'Qt' +Qtriangulate + +
    'Qu' +QupperDelaunay +'QVn' +QVertex_good +'Qv' +Qvneighbors +'Qx' +Qxact_merge + +
    'Qz' +Qzinfinite + +
     
    'Q0' +Q0_no_premerge +'Q1' +Q1_no_angle +'Q2' +Q2_no_independ +'Q3' +Q3_no_redundant + +
    'Q4' +Q4_no_old +'Q5' +Q5_no_check_out +'Q6' +Q6_no_concave +'Q7' +Q7_depth_first + +
    'Q8' +Q8_no_near_in +'Q9' +Q9_pick_furthest +'Q10' +Q10_no_narrow +'Q11' +Q11_trinormals +
    + + +


    + +

    Up: Home +page for Qhull
    +Up: Qhull manual
    +To: Programs +• Options +• Output +• Formats +• Geomview +• Print +• Qhull +• Precision +• Trace +• Functions
    +To: Qhull internals
    +To: Qhull functions, macros, and data structures
    +To: Qhull files
    +To: GeomGlobal +• IoMem +• MergePoly +• QhullSet +• StatUser
    + +


    + +

    The Geometry Center +Home Page

    + +

    Comments to: qhull@qhull.org +
    +Created: Sept. 25, 1995 --- Last modified: see top

    + + diff --git a/xs/src/qhull/html/qhalf.htm b/xs/src/qhull/html/qhalf.htm new file mode 100644 index 000000000..c87fe719e --- /dev/null +++ b/xs/src/qhull/html/qhalf.htm @@ -0,0 +1,626 @@ + + + + +qhalf -- halfspace intersection about a point + + + + +

    Up: Home page for Qhull
    +Up: Qhull manual: Table of Contents
    +To: Programs +• Options +• Output +• Formats +• Geomview +• Print +• Qhull +• Precision +• Trace +• Functions
    +To: synopsis +• input • outputs +• controls • graphics +• notes • conventions +• options + +


    + +

    [halfspace]qhalf -- halfspace intersection about a point

    + +

    The intersection of a set of halfspaces is a polytope. The +polytope may be unbounded. See Preparata & Shamos ['85] for a discussion. In low +dimensions, halfspace intersection may be used for linear +programming. + +

    +
    +

    Example: rbox c | qconvex FQ FV + n | qhalf Fp

    +
    Print the intersection of the facets of a cube. rbox c + generates the vertices of a cube. qconvex FV n returns of average + of the cube's vertices (in this case, the origin) and the halfspaces + that define the cube. qhalf Fp computes the intersection of + the halfspaces about the origin. The intersection is the vertices + of the original cube.
    + +

    Example: rbox c d G0.55 | qconvex FQ FV + n | qhalf Fp

    +
    Print the intersection of the facets of a cube and a diamond. There + are 24 facets and 14 intersection points. Four facets define each diamond + vertex. Six facets define each cube vertex. +
    + +

    Example: rbox c d G0.55 | qconvex FQ FV + n | qhalf Fp + Qt

    +
    Same as above except triangulate before computing + the intersection points. Three facets define each intersection + point. There are two duplicates of the diamond and four duplicates of the cube. +
    + +

    Example: rbox 10 s t10 | qconvex FQ FV + n | qhalf Fp Fn

    +
    Print the intersection of the facets of the convex hull of 10 cospherical points. + Include the intersection points and the neighboring intersections. + As in the previous examples, the intersection points are nearly the same as the + original input points. +
    +
    +
    + +

    In Qhull, a halfspace is defined by the points on or below a hyperplane. +The distance of each point to the hyperplane is less than or equal to zero. + +

    Qhull computes a halfspace intersection by the geometric +duality between points and halfspaces. +See halfspace examples, +qhalf notes, and +option 'p' of qhalf outputs.

    + +

    Qhalf's outputs are the intersection +points (Fp) and +the neighboring intersection points (Fn). +For random inputs, halfspace +intersections are usually defined by more than d halfspaces. See the sphere example. + +

    You can try triangulated output ('Qt') and joggled input ('QJ'). +It demonstrates that triangulated output is more accurate than joggled input. + +

    If you use 'Qt' (triangulated output), all +halfspace intersections are simplicial (e.g., three halfspaces per +intersection in 3-d). In 3-d, if more than three halfspaces intersect +at the same point, triangulated output will produce +duplicate intersections, one for each additional halfspace. See the third example, or +add 'Qt' to the sphere example.

    + +

    If you use 'QJ' (joggled input), all halfspace +intersections are simplicial. This may lead to nearly identical +intersections. For example, either replace 'Qt' with 'QJ' above, or add +'QJ' to the sphere example. +See Merged facets or joggled input.

    + +

    The 'qhalf' program is equivalent to +'qhull H' in 2-d to 4-d, and +'qhull H Qx' +in 5-d and higher. It disables the following Qhull +options: d n v Qbb QbB Qf Qg Qm +Qr QR Qv Qx Qz TR E V Fa FA FC FD FS Ft FV Gt Q0,etc. + + +

    Copyright © 1995-2015 C.B. Barber

    +
    + +

    »qhalf synopsis

    +
    +qhalf- halfspace intersection about a point.
    +    input (stdin): [dim, 1, interior point]
    +                   dim+1, n
    +                   halfspace coefficients + offset
    +    comments start with a non-numeric character
    +
    +options (qhalf.htm):
    +    Hn,n - specify coordinates of interior point
    +    Qt   - triangulated output
    +    QJ   - joggle input instead of merging facets
    +    Tv   - verify result: structure, convexity, and redundancy
    +    .    - concise list of all options
    +    -    - one-line description of all options
    +
    +output options (subset):
    +    s    - summary of results (default)
    +    Fp   - intersection coordinates
    +    Fv   - non-redundant halfspaces incident to each intersection
    +    Fx   - non-redundant halfspaces
    +    o    - OFF file format (dual convex hull)
    +    G    - Geomview output (dual convex hull)
    +    m    - Mathematica output (dual convex hull)
    +    QVn  - print intersections for halfspace n, -n if not
    +    TO file - output results to file, may be enclosed in single quotes
    +
    +examples:
    +    rbox d | qconvex n | qhalf s H0,0,0 Fp
    +    rbox c | qconvex FV n | qhalf s i
    +    rbox c | qconvex FV n | qhalf s o
    +
    + +

    »qhalf input

    + +
    +

    The input data on stdin consists of:

    +
      +
    • [optional] interior point +
        +
      • dimension +
      • 1 +
      • coordinates of interior point +
      +
    • dimension + 1 +
    • number of halfspaces
    • +
    • halfspace coefficients followed by offset
    • +
    + +

    Use I/O redirection (e.g., qhalf < data.txt), a pipe (e.g., rbox c | qconvex FV n | qhalf), +or the 'TI' option (e.g., qhalf TI data.txt). + +

    Qhull needs an interior point to compute the halfspace +intersection. An interior point is clearly inside all of the halfspaces. +A point is inside a halfspace if its distance to the corresponding hyperplane is negative. + +

    The interior point may be listed at the beginning of the input (as shown above). +If not, option +'Hn,n' defines the interior point as +[n,n,0,...] where 0 is the default coordinate (e.g., +'H0' is the origin). Use linear programming if you do not know +the interior point (see halfspace notes),

    + +

    The input to qhalf is a set of halfspaces that are defined by their hyperplanes. +Each halfspace is defined by +d coefficients followed by a signed offset. This defines +a linear inequality. The coefficients define a vector that is +normal to the halfspace. +The vector may have any length. If it +has length one, the offset is the distance from the origin to the +halfspace's boundary. Points in the halfspace have a negative distance to the hyperplane. +The distance from the interior point to each +halfspace is likewise negative.

    + +

    The halfspace format is the same as Qhull's output options 'n', 'Fo', +and 'Fi'. Use option 'Fd' to use cdd format for the +halfspaces.

    + +

    For example, here is the input for computing the intersection +of halfplanes that form a cube.

    + +
    +

    rbox c | qconvex FQ FV n TO data

    +
    +RBOX c | QCONVEX FQ FV n
    +3 1
    +     0      0      0
    +4
    +6
    +     0      0     -1   -0.5
    +     0     -1      0   -0.5
    +     1      0      0   -0.5
    +    -1      0      0   -0.5
    +     0      1      0   -0.5
    +     0      0      1   -0.5
    +
    +

    qhalf s Fp < data

    +
    +
    +Halfspace intersection by the convex hull of 6 points in 3-d:
    +
    +  Number of halfspaces: 6
    +  Number of non-redundant halfspaces: 6
    +  Number of intersection points: 8
    +
    +Statistics for: RBOX c | QCONVEX FQ FV n | QHALF s Fp
    +
    +  Number of points processed: 6
    +  Number of hyperplanes created: 11
    +  Number of distance tests for qhull: 11
    +  Number of merged facets: 1
    +  Number of distance tests for merging: 45
    +  CPU seconds to compute hull (after input):  0
    +
    +3
    +3
    +8
    +  -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.5   -0.5    0.5
    +  -0.5   -0.5    0.5
    +  -0.5   -0.5   -0.5
    +   0.5   -0.5   -0.5
    +
    +
    + +
    +

    »qhalf outputs

    +
    + +

    The following options control the output for halfspace +intersection.

    +
    +
    +
     
    +
    Intersections
    +
    FN
    +
    list intersection points for each non-redundant + halfspace. The first line + is the number of non-redundant halfspaces. Each remaining + lines starts with the number of intersection points. For the cube + example, each halfspace has four intersection points.
    +
    Fn
    +
    list neighboring intersections for each intersection point. The first line + is the number of intersection points. Each remaining line + starts with the number of neighboring intersections. For the cube + example, each intersection point has three neighboring intersections. +

    + In 3-d, a non-simplicial intersection has more than three neighboring + intersections. For random data (e.g., the sphere example), non-simplicial intersections are the norm. + Option 'Qt' produces three + neighboring intersections per intersection by duplicating the intersection + points. Option QJ' produces three + neighboring intersections per intersection by joggling the hyperplanes and + hence their intersections. +

    +
    Fp
    +
    print intersection coordinates. The first line is the dimension and the + second line is the number of intersection points. The following lines are the + coordinates of each intersection.
    +
    FI
    +
    list intersection IDs. The first line is the number of + intersections. The IDs follow, one per line.
    +
     
    +
     
    +
    Halfspaces
    +
    Fx
    +
    list non-redundant halfspaces. The first line is the number of + non-redundant halfspaces. The other lines list one halfspace per line. + A halfspace is non-redundant if it + defines a facet of the intersection. Redundant halfspaces are ignored. For + the cube example, all of the halfspaces are non-redundant. +
    +
    Fv
    +
    list non-redundant halfspaces incident to each intersection point. + The first line is the number of + non-redundant halfspaces. Each remaining line starts with the number + of non-redundant halfspaces. For the + cube example, each intersection is incident to three halfspaces.
    +
    i
    +
    list non-redundant halfspaces incident to each intersection point. The first + line is the number of intersection points. Each remaining line + lists the incident, non-redundant halfspaces. For the + cube example, each intersection is incident to three halfspaces. +
    +
    Fc
    +
    list coplanar halfspaces for each intersection point. The first line is + the number of intersection points. Each remaining line starts with + the number of coplanar halfspaces. A coplanar halfspace is listed for + one intersection point even though it is coplanar to multiple intersection + points.
    +
    Qi Fc
    +
    list redundant halfspaces for each intersection point. The first line is + the number of intersection points. Each remaining line starts with + the number of redundant halfspaces. Use options 'Qc Qi Fc' to list + coplanar and redundant halfspaces.
    + +
     
    +
     
    +
    General
    +
    s
    +
    print summary for the halfspace intersection. Use 'Fs' if you need numeric data.
    +
    o
    +
    print vertices and facets of the dual convex hull. The + first line is the dimension. The second line is the number of + vertices, facets, and ridges. The vertex + coordinates are next, followed by the facets, one per line.
    +
    p
    +
    print vertex coordinates of the dual convex hull. Each vertex corresponds + to a non-redundant halfspace. Its coordinates are the negative of the hyperplane's coefficients + divided by the offset plus the inner product of the coefficients and + the interior point (-c/(b+a.p). + Options 'p Qc' includes coplanar halfspaces. + Options 'p Qi' includes redundant halfspaces.
    +
    m
    +
    Mathematica output for the dual convex hull in 2-d or 3-d.
    +
    FM
    +
    Maple output for the dual convex hull in 2-d or 3-d.
    +
    G
    +
    Geomview output for the dual convex hull in 2-d, 3-d, or 4-d.
    +
    +
    + +
    +

    »qhalf controls

    +
    + +

    These options provide additional control:

    + +
    +
    +
    Qt
    +
    triangulated output. If a 3-d intersection is defined by more than + three hyperplanes, Qhull produces duplicate intersections -- one for + each extra hyperplane.
    +
    QJ
    +
    joggle the input instead of merging facets. In 3-d, this guarantees that + each intersection is defined by three hyperplanes.
    +
    f
    +
    facet dump. Print the data structure for each intersection (i.e., + facet)
    +
    TFn
    +
    report summary after constructing n + intersections
    +
    QVn
    +
    select intersection points for halfspace n + (marked 'good')
    +
    QGn
    +
    select intersection points that are visible to halfspace n + (marked 'good'). Use -n for the remainder.
    +
    Qbk:0Bk:0
    +
    remove the k-th coordinate from the input. This computes the + halfspace intersection in one lower dimension.
    +
    Tv
    +
    verify result
    +
    TI file
    +
    input data from file. The filename may not use spaces or quotes.
    +
    TO file
    +
    output results to file. Use single quotes if the filename + contains spaces (e.g., TO 'file with spaces.txt'
    +
    Qs
    +
    search all points for the initial simplex. If Qhull can + not construct an initial simplex, it reports a +descriptive message. Usually, the point set is degenerate and one +or more dimensions should be removed ('Qbk:0Bk:0'). +If not, use option 'Qs'. It performs an exhaustive search for the +best initial simplex. This is expensive is high dimensions.
    +
    +
    + + +
    +

    »qhalf graphics

    +
    + +

    To view the results with Geomview, compute the convex hull of +the intersection points ('qhull FQ H0 Fp | qhull G'). See Halfspace examples.

    + +
    +

    »qhalf notes

    +
    + +

    See halfspace intersection for precision issues related to qhalf.

    + +

    If you do not know an interior point for the halfspaces, use +linear programming to find one. Assume, n halfspaces +defined by: aj*x1+bj*x2+cj*x3+dj<=0, j=1..n. Perform +the following linear program:

    + +
    +

    max(x5) aj*x1+bj*x2+cj*x3+dj*x4+x5<=0, j=1..n

    +
    + +

    Then, if [x1,x2,x3,x4,x5] is an optimal solution with +x4>0 and x5>0 we get:

    + +
    +

    aj*(x1/x4)+bj*(x2/x4)+cj*(x3/x4)+dj<=(-x5/x4) j=1..n and (-x5/x4)<0, +

    +
    + +

    and conclude that the point [x1/x4,x2/x4,x3/x4] is in +the interior of all the halfspaces. Since x5 is +optimal, this point is "way in" the interior (good +for precision errors).

    + +

    After finding an interior point, the rest of the intersection +algorithm is from Preparata & Shamos ['85, p. 316, "A simple case +..."]. Translate the halfspaces so that the interior point +is the origin. Calculate the dual polytope. The dual polytope is +the convex hull of the vertices dual to the original faces in +regard to the unit sphere (i.e., halfspaces at distance d +from the origin are dual to vertices at distance 1/d). +Then calculate the resulting polytope, which is the dual of the +dual polytope, and translate the origin back to the interior +point [S. Spitz, S. Teller, D. Strawn].

    + + +
    +

    »qhalf +conventions

    +
    + +

    The following terminology is used for halfspace intersection +in Qhull. This is the hardest structure to understand. The +underlying structure is a convex hull with one vertex per +non-redundant halfspace. See convex hull +conventions and Qhull's data structures.

    + +
      +
    • interior point - a point in the intersection of + the halfspaces. Qhull needs an interior point to compute + the intersection. See halfspace input.
    • +
    • halfspace - d coordinates for the + normal and a signed offset. The distance to an interior + point is negative.
    • +
    • non-redundant halfspace - a halfspace that + defines an output facet
    • +
    • vertex - a dual vertex in the convex hull + corresponding to a non-redundant halfspace
    • +
    • coplanar point - the dual point corresponding to + a similar halfspace
    • +
    • interior point - the dual point corresponding to + a redundant halfspace
    • +
    • intersection point- the intersection of d + or more non-redundant halfspaces
    • +
    • facet - a dual facet in the convex hull + corresponding to an intersection point
    • +
    • non-simplicial facet - more than d + halfspaces intersect at a point
    • +
    • good facet - an intersection point that + satisfies restriction 'QVn', + etc.
    • +
    + +
    +

    »qhalf options

    + +
    +qhalf- compute the intersection of halfspaces about a point
    +    http://www.qhull.org
    +
    +input (stdin):
    +    optional interior point: dimension, 1, coordinates
    +    first lines: dimension+1 and number of halfspaces
    +    other lines: halfspace coefficients followed by offset
    +    comments:    start with a non-numeric character
    +
    +options:
    +    Hn,n - specify coordinates of interior point
    +    Qt   - triangulated ouput
    +    QJ   - joggle input instead of merging facets
    +    Qc   - keep coplanar halfspaces
    +    Qi   - keep other redundant halfspaces
    +
    +Qhull control options:
    +    QJn  - randomly joggle input in range [-n,n]
    +    Qbk:0Bk:0 - remove k-th coordinate from input
    +    Qs   - search all halfspaces for the initial simplex
    +    QGn  - print intersection if redundant to halfspace n, -n for not
    +    QVn  - print intersections for halfspace n, -n if not
    +
    +Trace options:
    +    T4   - trace at level n, 4=all, 5=mem/gauss, -1= events
    +    Tc   - check frequently during execution
    +    Ts   - print statistics
    +    Tv   - verify result: structure, convexity, and redundancy
    +    Tz   - send all output to stdout
    +    TFn  - report summary when n or more facets created
    +    TI file - input data from file, no spaces or single quotes
    +    TO file - output results to file, may be enclosed in single quotes
    +    TPn  - turn on tracing when halfspace n added to intersection
    +    TMn  - turn on tracing at merge n
    +    TWn  - trace merge facets when width > n
    +    TVn  - stop qhull after adding halfspace n, -n for before (see TCn)
    +    TCn  - stop qhull after building cone for halfspace n (see TVn)
    +
    +Precision options:
    +    Cn   - radius of centrum (roundoff added).  Merge facets if non-convex
    +     An  - cosine of maximum angle.  Merge facets if cosine > n or non-convex
    +           C-0 roundoff, A-0.99/C-0.01 pre-merge, A0.99/C0.01 post-merge
    +    Rn   - randomly perturb computations by a factor of [1-n,1+n]
    +    Un   - max distance below plane for a new, coplanar halfspace
    +    Wn   - min facet width for outside halfspace (before roundoff)
    +
    +Output formats (may be combined; if none, produces a summary to stdout):
    +    f    - facet dump
    +    G    - Geomview output (dual convex hull)
    +    i    - non-redundant halfspaces incident to each intersection
    +    m    - Mathematica output (dual convex hull)
    +    o    - OFF format (dual convex hull: dimension, points, and facets)
    +    p    - vertex coordinates of dual convex hull (coplanars if 'Qc' or 'Qi')
    +    s    - summary (stderr)
    +
    +More formats:
    +    Fc   - count plus redundant halfspaces for each intersection
    +         -   Qc (default) for coplanar and Qi for other redundant
    +    Fd   - use cdd format for input (homogeneous with offset first)
    +    FF   - facet dump without ridges
    +    FI   - ID of each intersection
    +    Fm   - merge count for each intersection (511 max)
    +    FM   - Maple output (dual convex hull)
    +    Fn   - count plus neighboring intersections for each intersection
    +    FN   - count plus intersections for each non-redundant halfspace
    +    FO   - options and precision constants
    +    Fp   - dim, count, and intersection coordinates
    +    FP   - nearest halfspace and distance for each redundant halfspace
    +    FQ   - command used for qhalf
    +    Fs   - summary: #int (8), dim, #halfspaces, #non-redundant, #intersections
    +                      for output: #non-redundant, #intersections, #coplanar
    +                                  halfspaces, #non-simplicial intersections
    +                    #real (2), max outer plane, min vertex
    +    Fv   - count plus non-redundant halfspaces for each intersection
    +    Fx   - non-redundant halfspaces
    +
    +Geomview output (2-d, 3-d and 4-d; dual convex hull)
    +    Ga   - all points (i.e., transformed halfspaces) as dots
    +     Gp  -  coplanar points and vertices as radii
    +     Gv  -  vertices (i.e., non-redundant halfspaces) as spheres
    +    Gi   - inner planes (i.e., halfspace intersections) only
    +     Gn  -  no planes
    +     Go  -  outer planes only
    +    Gc   - centrums
    +    Gh   - hyperplane intersections
    +    Gr   - ridges
    +    GDn  - drop dimension n in 3-d and 4-d output
    +
    +Print options:
    +    PAn  - keep n largest facets (i.e., intersections) by area
    +    Pdk:n- drop facet if normal[k] <= n (default 0.0)
    +    PDk:n- drop facet if normal[k] >= n
    +    Pg   - print good facets (needs 'QGn' or 'QVn')
    +    PFn  - keep facets whose area is at least n
    +    PG   - print neighbors of good facets
    +    PMn  - keep n facets with most merges
    +    Po   - force output.  If error, output neighborhood of facet
    +    Pp   - do not report precision problems
    +
    +    .    - list of all options
    +    -    - one line descriptions of all options
    +
    + + +
    + +

    Up: Home page for Qhull
    +Up: Qhull manual: Table of Contents
    +To: Programs +• Options +• Output +• Formats +• Geomview +• Print +• Qhull +• Precision +• Trace +• Functions
    +To: synopsis +• input • outputs +• controls • graphics +• notes • conventions +• options + +


    + +

    The Geometry Center +Home Page

    + +

    Comments to: qhull@qhull.org +
    +Created: Sept. 25, 1995 --- Last modified: see top

    + + + diff --git a/xs/src/qhull/html/qhull-cpp.xml b/xs/src/qhull/html/qhull-cpp.xml new file mode 100644 index 000000000..ae755e826 --- /dev/null +++ b/xs/src/qhull/html/qhull-cpp.xml @@ -0,0 +1,214 @@ + + + + +

    Qhull C++ -- C++ interface to Qhull

    + + Copyright (c) 2009-2015, C.B. Barber + + +
    +

    This draft + document records some of the design decisions for Qhull C++. Convert it to HTML by road-faq.xsl from road-faq. + + Please send comments and suggestions to bradb@shore.net +

    +
    +
    +
    + Help +
    • +
    • +
    • +
    +
    +
    +
    • +
    • +
    +
    +
    +
    + . +
    + +
    + + + + Qhull's collection APIs are modeled on Qt's collection API (QList, QVector, QHash) w/o QT_STRICT_ITERATORS. They support STL and Qt programming. + +

    Some of Qhull's collection classes derive from STL classes. If so, + please avoid additional STL functions and operators added by inheritance. + These collection classes may be rewritten to derive from Qt classes instead. + See Road's . +

    + + + Qhull's collection API (where applicable). For documentation, see Qt's QList, QMap, QListIterator, QMapIterator, QMutableListIterator, and QMutableMapIterator +
    • + STL types [list, qlinkedlist, qlist, qvector, vector] -- const_iterator, iterator +
    • + STL types describing iterators [list, qlinkedlist, qlist, qvector, vector] -- const_pointer, const_reference, difference_type, + pointer, reference, size_type, value_type. + Pointer and reference types not defined if unavailable (not needed for <algorithm>) +
    • + const_iterator, iterator types -- difference_type, iterator_category, pointer, reference, value_type +
    • + Qt types [qlinkedlist, qlist, qvector] -- ConstIterator, Iterator, QhullclassIterator, MutableQhullclassIterator. + Qt's foreach requires const_iterator. +
    • + Types for sets/maps [hash_map, QHash] -- key_compare, key_type, mapped_type +
    • + Constructor -- default constructor, copy constructor, assignment operator, destructor +
    • + Conversion -- to/from/as corresponding C, STL, and Qt constructs. Include toQList and toStdVector (may be filtered, e.g., QhullFacetSet). + Do not define fromStdList and fromQList if container is not reference counted (i.e., acts like a value) +
    • + Get/set -- configuration options for class +
    • + STL-style iterator - begin, constBegin, constEnd, end, key, value, =, *, [], ->, ++, --, +, -, ==, !=, <, + <=, >, >=, const_iterator(iterator), iterator COMPARE const_iterator. + An iterator is an abstraction of a pointer. It is not aware of its container. +
    • + Java-style iterator [qiterator.h] - countRemaining, findNext, findPrevious, hasNext, hasPrevious, next, peekNext, peekPrevious, previous, toBack, toFront, = Coordinates +
    • + Mutable Java-style iterator adds - insert, remove, setValue, value +
    • + Element access -- back, first, front, last +
    • + Element access w/ index -- [], at (const& only), constData, data, mid, value +
    • + Read-only - (int)count, empty, isEmpty, (size_t)size. Count() and size() may be filtered. If so, they may be zero when !empty(). +
    • + Read-only for sets/maps - capacity, key, keys, reserve, resize, values +
    • + Operator - ==, !=, +, +=, << +
    • + Read-write -- append, clear, erase, insert, move, prepend, pop_back, pop_front, push_back, push_front, removeAll, removeAt, removeFirst, removeLast, replace, + swap, takeAt, takeFirst, takeLast +
    • + Read-write for sets/maps -- insertMulti, squeeze, take, unite +
    • + Search -- contains(const T &), count(const T &), indexOf, lastIndexOf +
    • + Search for sets/maps -- constFind, lowerBound, upperBound +
    • + Stream I/O -- stream << +
    + + STL list and vector -- For unfiltered access to each element. +
    • + Apache: Creating your own containers -- requirements for STL containers. Iterators should define the types from 'iterator_traits'. +
    • + STL types -- allocator_type, const_iterator, const_pointer, const_reference, const_reverse_iterator, difference_type, iterator, iterator_category, pointer, reference, reverse_iterator, size_type, value_type +
    • + STL constructors -- MyType(), MyType(count), MyType(count, value), MyType(first, last), + MyType(MyType&), +
    • + STL getter/setters -- at (random_access only), back, begin, capacity, end, front, rbegin, rend, size, max_size +
    • + STL predicates -- empty +
    • + STL iterator types -- const_pointer, const_reference, difference_type, iterator_category, pointer, reference, value_type +
    • + STL iterator operators -- *, -<, ++, --, +=, -=, +, -, [], ==, !=, <, >, >=, <= +
    • + STL operators -- =, [] (random_access only), ==, !=, <, >, <=, >= +
    • + STL modifiers -- assign, clear, erase, insert, pop_back, push_back, reserve, resize, swap +
    • +
    + + Qt Qlist -- For unfiltered access to each element +
    • +
    • + Additional Qt types -- ConstIterator, Iterator, QListIterator, QMutableListIterator +
    • + Additional Qt get/set -- constBegin, constEnd, count, first, last, value (random_access only) +
    • + Additional Qt predicates -- isEmpty +
    • + Additional Qt -- mid (random_access only) +
    • + Additional Qt search -- contains, count(T&), indexOf (random_access only), lastIndeOf (random_access only) +
    • + Additional Qt modifiers -- append, insert(index,value) (random_access only), move (random_access only), pop_front, prepend, push_front, removeAll, removeAt (random_access only), removeFirst, removeLast, replace, swap by index, takeAt, takeFirst, takeLast +
    • + Additional Qt operators -- +, <<, +=, + stream << and >> +
    • + Unsupported types by Qt -- allocator_type, const_reverse_iterator, reverse_iterator +
    • + Unsupported accessors by Qt -- max_size, rbegin, rend +
    • + Unsupported constructors by Qt -- multi-value constructors +
    • + unsupported modifiers by Qt -- assign, muli-value inserts, STL's swaps +
    • +
    + + STL map and Qt QMap. These use nearly the same API as list and vector classes. They add the following. +
    • + STL types -- key_compare, key_type, mapped_type +
    • + STL search -- equal_range, find, lower_bound, upper_bound +
    • + Qt removes -- equal_range, key_compare +
    • + Qt renames -- lowerBound, upperBound +
    • + Qt adds -- constFind, insertMulti, key, keys, take, uniqueKeys, unite, values +
    • + Not applicable to map and QMap -- at, back, pop_back, pop_front, push_back, push_front, swap +
    • + Not applicable to QMap -- append, first, last, lastIndexOf, mid, move, prepend, removeAll, removeAt, removeFirst, removeLast, replace, squeeze, takeAt, takeFirst, takeLast +
    • + Not applicable to map -- assign +
    + + Qt QHash. STL extensions provide similar classes, e.g., Microsoft's stdext::hash_set. THey are nearly the same as QMap +
    • +
    • +
    • + Not applicable to Qhash -- lowerBound, unite, upperBound, +
    • + Qt adds -- squeeze +
    +
    + +
    • + check... -- Throw error on failure +
    • + try... -- Return false on failure. Do not throw errors. +
    • + ...Temporarily -- lifetime depends on source. e.g., toByteArrayTemporarily +
    • + ...p -- indicates pointer-to. +
    • + end... -- points to one beyond the last available +
    • + private functions -- No syntactic indication. They may become public later on. +
    • + Error messages -- Preceed error messages with the name of the class throwing the error (e.g. "ClassName: ..."). If this is an internal error, use "ClassName inconsistent: ..." +
    • + parameter order -- qhRunId, dimension, coordinates, count. +
    • + toClass -- Convert into a Class object (makes a deep copy) +
    • + qRunId -- Requires Qh installed. Some routines allow 0 for limited info (e.g., operator<<) +
    • + Disable methods in derived classes -- If the default constructor, copy constructor, or copy assignment is disabled, it should be also disabled in derived classes (better error messages). +
    • + Constructor order -- default constructor, other constructors, copy constructor, copy assignment, destructor +
    +
    +
    +
    diff --git a/xs/src/qhull/html/qhull.htm b/xs/src/qhull/html/qhull.htm new file mode 100644 index 000000000..0a2aa75e0 --- /dev/null +++ b/xs/src/qhull/html/qhull.htm @@ -0,0 +1,473 @@ + + + + +qhull -- convex hull and related structures + + + + +

    Up: Home page for Qhull
    +Up: Qhull manual: Table of Contents
    +To: Programs +• Options +• Output +• Formats +• Geomview +• Print +• Qhull +• Precision +• Trace +• Functions
    +To: synopsis • input +• outputs • controls +• options +


    + +

    [cone]qhull -- convex hull and related structures

    + +

    The convex hull of a set of points is the smallest convex set +containing the points. The Delaunay triangulation and furthest-site +Delaunay triangulation are equivalent to a convex hull in one +higher dimension. Halfspace intersection about a point is +equivalent to a convex hull by polar duality. + +

    The qhull program provides options to build these +structures and to experiment with the process. Use the +qconvex, +qdelaunay, qhalf, +and qvoronoi programs +to build specific structures. You may use qhull instead. +It takes the same options and uses the same code. +

    +
    +
    Example: rbox 1000 D3 | qhull + C-1e-4 + FO + Ts +
    +
    Compute the 3-d convex hull of 1000 random + points. + Centrums must be 10^-4 below neighboring + hyperplanes. Print the options and precision constants. + When done, print statistics. These options may be + used with any of the Qhull programs.
    +
     
    +
    Example: rbox 1000 D3 | qhull d + Qbb + R1e-4 + Q0
    +
    Compute the 3-d Delaunay triangulation of 1000 random + points. Randomly perturb all calculations by + [0.9999,1.0001]. Do not correct precision problems. + This leads to serious precision errors.
    +
    +
    +

    Use the following equivalences when calling qhull in 2-d to 4-d (a 3-d +Delaunay triangulation is a 4-d convex hull): +

    + +
    + +

    Use the following equivalences when calling qhull in 5-d and higher (a 4-d +Delaunay triangulation is a 5-d convex hull): +

    + +
    + + +

    By default, Qhull merges coplanar facets. For example, the convex +hull of a cube's vertices has six facets. + +

    If you use 'Qt' (triangulated output), +all facets will be simplicial (e.g., triangles in 2-d). For the cube +example, it will have 12 facets. Some facets may be +degenerate and have zero area. + +

    If you use 'QJ' (joggled input), +all facets will be simplicial. The corresponding vertices will be +slightly perturbed. Joggled input is less accurate that triangulated +output.See Merged facets or joggled input.

    + +

    The output for 4-d convex hulls may be confusing if the convex +hull contains non-simplicial facets (e.g., a hypercube). See +Why +are there extra points in a 4-d or higher convex hull?
    +

    + +

    Copyright © 1995-2015 C.B. Barber

    + +
    + +

    »qhull synopsis

    +
    +qhull- compute convex hulls and related structures.
    +    input (stdin): dimension, n, point coordinates
    +    comments start with a non-numeric character
    +    halfspace: use dim+1 and put offsets after coefficients
    +
    +options (qh-quick.htm):
    +    d    - Delaunay triangulation by lifting points to a paraboloid
    +    d Qu - furthest-site Delaunay triangulation (upper convex hull)
    +    v    - Voronoi diagram as the dual of the Delaunay triangulation
    +    v Qu - furthest-site Voronoi diagram
    +    H1,1 - Halfspace intersection about [1,1,0,...] via polar duality
    +    Qt   - triangulated output
    +    QJ   - joggle input instead of merging facets
    +    Tv   - verify result: structure, convexity, and point inclusion
    +    .    - concise list of all options
    +    -    - one-line description of all options
    +
    +Output options (subset):
    +    s    - summary of results (default)
    +    i    - vertices incident to each facet
    +    n    - normals with offsets
    +    p    - vertex coordinates (if 'Qc', includes coplanar points)
    +           if 'v', Voronoi vertices
    +    Fp   - halfspace intersections
    +    Fx   - extreme points (convex hull vertices)
    +    FA   - compute total area and volume
    +    o    - OFF format (if 'v', outputs Voronoi regions)
    +    G    - Geomview output (2-d, 3-d and 4-d)
    +    m    - Mathematica output (2-d and 3-d)
    +    QVn  - print facets that include point n, -n if not
    +    TO file- output results to file, may be enclosed in single quotes
    +
    +examples:
    +    rbox c d D2 | qhull Qc s f Fx | more      rbox 1000 s | qhull Tv s FA
    +    rbox 10 D2 | qhull d QJ s i TO result     rbox 10 D2 | qhull v Qbb Qt p
    +    rbox 10 D2 | qhull d Qu QJ m              rbox 10 D2 | qhull v Qu QJ o
    +    rbox c | qhull n                          rbox c | qhull FV n | qhull H Fp
    +    rbox d D12 | qhull QR0 FA                 rbox c D7 | qhull FA TF1000
    +    rbox y 1000 W0 | qhull                    rbox 10 | qhull v QJ o Fv
    +
    + +

    »qhull input

    +
    + +

    The input data on stdin consists of:

    +
      +
    • dimension +
    • number of points
    • +
    • point coordinates
    • +
    + +

    Use I/O redirection (e.g., qhull < data.txt), a pipe (e.g., rbox 10 | qhull), +or the 'TI' option (e.g., qhull TI data.txt). + +

    Comments start with a non-numeric character. Error reporting is +simpler if there is one point per line. Dimension +and number of points may be reversed. For halfspace intersection, +an interior point may be prepended (see qhalf input). + +

    Here is the input for computing the convex +hull of the unit cube. The output is the normals, one +per facet.

    + +
    +

    rbox c > data

    +
    +3 RBOX c
    +8
    +  -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.5   -0.5   -0.5
    +   0.5   -0.5    0.5
    +   0.5    0.5   -0.5
    +   0.5    0.5    0.5
    +
    +

    qhull s n < data

    +
    +
    +Convex hull of 8 points in 3-d:
    +
    +  Number of vertices: 8
    +  Number of facets: 6
    +  Number of non-simplicial facets: 6
    +
    +Statistics for: RBOX c | QHULL s n
    +
    +  Number of points processed: 8
    +  Number of hyperplanes created: 11
    +  Number of distance tests for qhull: 35
    +  Number of merged facets: 6
    +  Number of distance tests for merging: 84
    +  CPU seconds to compute hull (after input): 0.081
    +
    +4
    +6
    +     0      0     -1   -0.5
    +     0     -1      0   -0.5
    +     1      0      0   -0.5
    +    -1      0      0   -0.5
    +     0      1      0   -0.5
    +     0      0      1   -0.5
    +
    +
    + +
    +

    »qhull outputs

    +
    + +

    These options control the output of qhull. They may be used +individually or together.

    +
    +
    +
     
    +
    General
    +
    qhull
    +
    compute the convex hull of the input points. + See qconvex.
    +
    qhull d Qbb
    +
    compute the Delaunay triangulation by lifting the points + to a paraboloid. Use option 'Qbb' + to scale the paraboloid and improve numeric precision. + See qdelaunay.
    +
    qhull v Qbb
    +
    compute the Voronoi diagram by computing the Delaunay + triangulation. Use option 'Qbb' + to scale the paraboloid and improve numeric precision. + See qvoronoi.
    +
    qhull H
    +
    compute the halfspace intersection about a point via polar + duality. The point is below the hyperplane that defines the halfspace. + See qhalf.
    +
    +
    + +

    For a full list of output options see +

    + +
    + +
    +

    »qhull controls

    +
    + +

    For a full list of control options see +

    + +
    + +
    +

    »qhull options

    + +
    +qhull- compute convex hulls and related structures.
    +    http://www.qhull.org
    +
    +input (stdin):
    +    first lines: dimension and number of points (or vice-versa).
    +    other lines: point coordinates, best if one point per line
    +    comments:    start with a non-numeric character
    +    halfspaces:  use dim plus one and put offset after coefficients.
    +                 May be preceded by a single interior point ('H').
    +
    +options:
    +    d    - Delaunay triangulation by lifting points to a paraboloid
    +    d Qu - furthest-site Delaunay triangulation (upper convex hull)
    +    v    - Voronoi diagram (dual of the Delaunay triangulation)
    +    v Qu - furthest-site Voronoi diagram
    +    Hn,n,... - halfspace intersection about point [n,n,0,...]
    +    Qt   - triangulated output
    +    QJ   - joggle input instead of merging facets
    +    Qc   - keep coplanar points with nearest facet
    +    Qi   - keep interior points with nearest facet
    +
    +Qhull control options:
    +    Qbk:n   - scale coord k so that low bound is n
    +      QBk:n - scale coord k so that upper bound is n (QBk is 0.5)
    +    QbB  - scale input to unit cube centered at the origin
    +    Qbb  - scale last coordinate to [0,m] for Delaunay triangulations
    +    Qbk:0Bk:0 - remove k-th coordinate from input
    +    QJn  - randomly joggle input in range [-n,n]
    +    QRn  - random rotation (n=seed, n=0 time, n=-1 time/no rotate)
    +    Qf   - partition point to furthest outside facet
    +    Qg   - only build good facets (needs 'QGn', 'QVn', or 'PdD')
    +    Qm   - only process points that would increase max_outside
    +    Qr   - process random outside points instead of furthest ones
    +    Qs   - search all points for the initial simplex
    +    Qu   - for 'd' or 'v', compute upper hull without point at-infinity
    +              returns furthest-site Delaunay triangulation
    +    Qv   - test vertex neighbors for convexity
    +    Qx   - exact pre-merges (skips coplanar and anglomaniacs facets)
    +    Qz   - add point-at-infinity to Delaunay triangulation
    +    QGn  - good facet if visible from point n, -n for not visible
    +    QVn  - good facet if it includes point n, -n if not
    +    Q0   - turn off default p remerge with 'C-0'/'Qx'
    +    Q1     - sort merges by type instead of angle
    +    Q2   - merge all non-convex at once instead of independent sets
    +    Q3   - do not merge redundant vertices
    +    Q4   - avoid old>new merges
    +    Q5   - do not correct outer planes at end of qhull
    +    Q6   - do not pre-merge concave or coplanar facets
    +    Q7   - depth-first processing instead of breadth-first
    +    Q8   - do not process near-inside points
    +    Q9   - process furthest of furthest points
    +    Q10  - no special processing for narrow distributions
    +    Q11  - copy normals and recompute centrums for tricoplanar facets
    +    Q12  - do not error on wide merge due to duplicate ridge and nearly coincident points
    +
    +Towpaths Trace options:
    +    T4   - trace at level n, 4=all, 5=mem/gauss, -1= events
    +    Tc   - check frequently during execution
    +    Ts   - print statistics
    +    Tv   - verify result: structure, convexity, and point inclusion
    +    Tz   - send all output to stdout
    +    TFn  - report summary when n or more facets created
    +    TI file - input data from file, no spaces or single quotes
    +    TO file - output results to file, may be enclosed in single quotes
    +    TPn  - turn on tracing when point n added to hull
    +     TMn - turn on tracing at merge n
    +     TWn - trace merge facets when width > n
    +    TRn  - rerun qhull n times.  Use with 'QJn'
    +    TVn  - stop qhull after adding point n, -n for before (see TCn)
    +     TCn - stop qhull after building cone for point n (see TVn)
    +
    +Precision options:
    +    Cn   - radius of centrum (roundoff added).  Merge facets if non-convex
    +     An  - cosine of maximum angle.  Merge facets if cosine > n or non-convex
    +           C-0 roundoff, A-0.99/C-0.01 pre-merge, A0.99/C0.01 post-merge
    +    En   - max roundoff error for distance computation
    +    Rn   - randomly perturb computations by a factor of [1-n,1+n]
    +    Vn   - min distance above plane for a visible facet (default 3C-n or En)
    +    Un   - max distance below plane for a new, coplanar point (default Vn)
    +    Wn   - min facet width for outside point (before roundoff, default 2Vn)
    +
    +Output formats (may be combined; if none, produces a summary to stdout):
    +    f    - facet dump
    +    G    - Geomview output (see below)
    +    i    - vertices incident to each facet
    +    m    - Mathematica output (2-d and 3-d)
    +    o    - OFF format (dim, points and facets; Voronoi regions)
    +    n    - normals with offsets
    +    p    - vertex coordinates or Voronoi vertices (coplanar points if 'Qc')
    +    s    - summary (stderr)
    +
    +More formats:
    +    Fa   - area for each facet
    +    FA   - compute total area and volume for option 's'
    +    Fc   - count plus coplanar points for each facet
    +           use 'Qc' (default) for coplanar and 'Qi' for interior
    +    FC   - centrum or Voronoi center for each facet
    +    Fd   - use cdd format for input (homogeneous with offset first)
    +    FD   - use cdd format for numeric output (offset first)
    +    FF   - facet dump without ridges
    +    Fi   - inner plane for each facet
    +           for 'v', separating hyperplanes for bounded Voronoi regions
    +    FI   - ID of each facet
    +    Fm   - merge count for each facet (511 max)
    +    FM   - Maple output (2-d and 3-d)
    +    Fn   - count plus neighboring facets for each facet
    +    FN   - count plus neighboring facets for each point
    +    Fo   - outer plane (or max_outside) for each facet
    +           for 'v', separating hyperplanes for unbounded Voronoi regions
    +    FO   - options and precision constants
    +    Fp   - dim, count, and intersection coordinates (halfspace only)
    +    FP   - nearest vertex and distance for each coplanar point
    +    FQ   - command used for qhull
    +    Fs   - summary: #int (8), dimension, #points, tot vertices, tot facets,
    +                      output: #vertices, #facets, #coplanars, #nonsimplicial
    +                    #real (2), max outer plane, min vertex
    +    FS   - sizes:   #int (0)
    +                    #real(2) tot area, tot volume
    +    Ft   - triangulation with centrums for non-simplicial facets (OFF format)
    +    Fv   - count plus vertices for each facet
    +           for 'v', Voronoi diagram as Voronoi vertices for pairs of sites
    +    FV   - average of vertices (a feasible point for 'H')
    +    Fx   - extreme points (in order for 2-d)
    +
    +Geomview options (2-d, 3-d, and 4-d; 2-d Voronoi)
    +    Ga   - all points as dots
    +     Gp  -  coplanar points and vertices as radii
    +     Gv  -  vertices as spheres
    +    Gi   - inner planes only
    +     Gn  -  no planes
    +     Go  -  outer planes only
    +    Gc   - centrums
    +    Gh   - hyperplane intersections
    +    Gr   - ridges
    +    GDn  - drop dimension n in 3-d and 4-d output
    +    Gt   - for 3-d 'd', transparent outer ridges
    +
    +Print options:
    +    PAn  - keep n largest facets by area
    +    Pdk:n - drop facet if normal[k] <= n (default 0.0)
    +    PDk:n - drop facet if normal[k] >= n
    +    Pg   - print good facets (needs 'QGn' or 'QVn')
    +    PFn  - keep facets whose area is at least n
    +    PG   - print neighbors of good facets
    +    PMn  - keep n facets with most merges
    +    Po   - force output.  If error, output neighborhood of facet
    +    Pp   - do not report precision problems
    +
    +    .    - list of all options
    +    -    - one line descriptions of all options
    +
    + + +
    + +

    Up: Home page for Qhull
    +Up: Qhull manual: Table of Contents
    +To: Programs +• Options +• Output +• Formats +• Geomview +• Print +• Qhull +• Precision +• Trace +• Functions
    +To: synopsis • input +• outputs • controls +• options + +


    + +

    The Geometry Center +Home Page

    + +

    Comments to: qhull@qhull.org +
    +Created: Sept. 25, 1995 --- Last modified: see top

    + + diff --git a/xs/src/qhull/html/qhull.man b/xs/src/qhull/html/qhull.man new file mode 100644 index 000000000..8d1dc08ac --- /dev/null +++ b/xs/src/qhull/html/qhull.man @@ -0,0 +1,1008 @@ +.\" This is the Unix manual page for qhull, written in nroff, the standard +.\" manual formatter for Unix systems. To format it, type +.\" +.\" nroff -man qhull.man +.\" +.\" This will print a formatted copy to standard output. If you want +.\" to ensure that the output is plain ASCII, free of any control +.\" characters that nroff uses for underlining etc, pipe the output +.\" through "col -b": +.\" +.\" nroff -man qhull.man | col -b +.\" +.\" Warning: a leading quote "'" or dot "." will not format correctly +.\" +.TH qhull 1 "2003/12/30" "Geometry Center" +.SH NAME +qhull \- convex hull, Delaunay triangulation, Voronoi diagram, +halfspace intersection about a point, hull volume, facet area +.SH SYNOPSIS +.nf +qhull- compute convex hulls and related structures + input (stdin): dimension, #points, point coordinates + first comment (non-numeric) is listed in the summary + halfspace: use dim plus one with offsets after coefficients + +options (qh-quick.htm): + d - Delaunay triangulation by lifting points to a paraboloid + v - Voronoi diagram via the Delaunay triangulation + H1,1 - Halfspace intersection about [1,1,0,...] + d Qu - Furthest-site Delaunay triangulation (upper convex hull) + v Qu - Furthest-site Voronoi diagram + Qt - triangulated output + QJ - Joggle the input to avoid precision problems + . - concise list of all options + - - one-line description of all options + +Output options (subset): + FA - compute total area and volume + Fx - extreme points (convex hull vertices) + G - Geomview output (2-d, 3-d and 4-d) + Fp - halfspace intersection coordinates + m - Mathematica output (2-d and 3-d) + n - normals with offsets + o - OFF file format (if Voronoi, outputs regions) + TO file- output results to file, may be enclosed in single quotes + f - print all fields of all facets + s - summary of results (default) + Tv - verify result: structure, convexity, and point inclusion + p - vertex coordinates (centers for Voronoi) + i - vertices incident to each facet + +example: + rbox 1000 s | qhull Tv s FA +.fi + + - html manual: index.htm + - installation: README.txt + - see also: COPYING.txt, REGISTER.txt, Changes.txt + - WWW: + - GIT: + - mirror: + - news: + - Geomview: + - news group: + - FAQ: + - email: qhull@qhull.org + - bug reports: qhull_bug@qhull.org + +The sections are: + - INTRODUCTION + - DESCRIPTION, a description of Qhull + - IMPRECISION, how Qhull handles imprecision + - OPTIONS + - Input and output options + - Additional input/output formats + - Precision options + - Geomview options + - Print options + - Qhull options + - Trace options + - BUGS + - E-MAIL + - SEE ALSO + - AUTHORS + - ACKNOWLEGEMENTS + +This man page briefly describes all Qhull options. Please report +any mismatches with Qhull's html manual (index.htm). + +.PP +.SH INTRODUCTION +Qhull is a general dimension code for computing convex hulls, Delaunay +triangulations, Voronoi diagram, furthest\[hy]site Voronoi diagram, +furthest\[hy]site Delaunay triangulations, and +halfspace intersections about a point. It implements the Quickhull algorithm for +computing the convex hull. Qhull handles round\[hy]off errors from floating +point arithmetic. It can approximate a convex hull. + +The program includes options for hull volume, facet area, partial hulls, +input transformations, randomization, tracing, multiple output formats, and +execution statistics. The program can be called from within your application. +You can view the results in 2\[hy]d, 3\[hy]d and 4\[hy]d with Geomview. +.PP +.SH DESCRIPTION +.PP +The format of input is the following: first line contains the dimension, +second line contains the number of input points, and point coordinates follow. +The dimension and number of points can be reversed. +Comments and line breaks are ignored. A comment starts with a +non\[hy]numeric character and continues to the end of line. The first comment +is reported in summaries and statistics. +Error reporting is +better if there is one point per line. +.PP +The default printout option is a short summary. There are many +other output formats. +.PP +Qhull implements the Quickhull algorithm for convex hull. This algorithm combines +the 2\[hy]d Quickhull algorithm with the n\[hy]d beneath\[hy]beyond algorithm +[c.f., Preparata & Shamos '85]. +It is similar to the randomized algorithms of Clarkson and +others [Clarkson et al. '93]. The main +advantages of Quickhull are output sensitive performance, reduced +space requirements, and automatic handling of precision problems. +.PP +The data structure produced by Qhull consists of vertices, ridges, and facets. +A vertex is a point of the input set. A ridge is a set of d vertices +and two neighboring facets. For example in 3\[hy]d, a ridge is an edge of the +polyhedron. A facet is a set of ridges, a set of neighboring facets, a set +of incident vertices, and a hyperplane equation. For simplicial facets, the +ridges are defined by the vertices and neighboring facets. When Qhull +merges two facets, it produces a non\[hy]simplicial +facet. A non\[hy]simplicial facet has more than d neighbors and may share more than +one ridge with a neighbor. +.PP +.SH IMPRECISION +.PP +Since Qhull uses floating point arithmetic, roundoff error may occur for each +calculation. This causes problems +for most geometric algorithms. +.PP +Qhull automatically sets option 'C\-0' in 2\[hy]d, 3\[hy]d, and 4\[hy]d, or +option 'Qx' in 5\[hy]d and higher. These options handle precision problems +by merging facets. Alternatively, use option 'QJ' to joggle the +input. +.PP +With 'C\-0', Qhull merges non\[hy]convex +facets while constructing the hull. The remaining facets are +clearly convex. With 'Qx', Qhull merges +coplanar horizon facets, flipped facets, concave facets and +duplicated ridges. It merges coplanar facets after constructing +the hull. +With 'Qx', coplanar points may be missed, but it +appears to be unlikely. +.PP +To guarantee triangular output, joggle the input with option 'QJ'. Facet +merging will not occur. +.SH OPTIONS +.PP +To get a list of the most important options, execute 'qhull' by itself. +To get a complete list of options, +execute 'qhull \-'. +To get a complete, concise list of options, execute 'qhull .'. + +Options can be in any order. +Capitalized options take an argument (except 'PG' and 'F' options). +Single letters are used for output formats and precision constants. The +other options are grouped into menus for other output formats ('F'), +Geomview output ('G'), +printing ('P'), Qhull control ('Q'), and tracing ('T'). +.TP +Main options: +.TP +default +Compute the convex hull of the input points. Report a summary of +the result. +.TP +d +Compute the Delaunay triangulation by lifting the input points to a +paraboloid. The 'o' option prints the input points and facets. +The 'QJ' option guarantees triangular output. The 'Ft' +option prints a triangulation. It adds points (the centrums) to non\[hy]simplicial +facets. +.TP +v +Compute the Voronoi diagram from the Delaunay triangulation. +The 'p' option prints the Voronoi vertices. +The 'o' option prints the Voronoi vertices and the +vertices in each Voronoi region. It lists regions in +site ID order. +The 'Fv' option prints each ridge of the Voronoi diagram. +The first or zero'th vertex +indicates the infinity vertex. Its coordinates are +qh_INFINITE (\-10.101). It indicates unbounded Voronoi +regions or degenerate Delaunay triangles. +.TP +Hn,n,... +Compute halfspace intersection about [n,n,0,...]. +The input is a set of halfspaces +defined in the same format as 'n', 'Fo', and 'Fi'. +Use 'Fp' to print the intersection points. Use 'Fv' +to list the intersection points for each halfspace. The +other output formats display the dual convex hull. + +The point [n,n,n,...] is a feasible point for the halfspaces, i.e., +a point that is inside all +of the halfspaces (Hx+b <= 0). The default coordinate value is 0. + +The input may start with a feasible point. If so, use 'H' by itself. +The input starts with a feasible point when the first number is the dimension, +the second number is "1", and the coordinates complete a line. The 'FV' +option produces a feasible point for a convex hull. +.TP +d Qu +Compute the furthest\[hy]site Delaunay triangulation from the upper +convex hull. The 'o' option prints the input points and facets. +The 'QJ' option guarantees triangular otuput. You can also use 'Ft' +to triangulate via the centrums of non\[hy]simplicial +facets. +.TP +v Qu +Compute the furthest\[hy]site Voronoi diagram. +The 'p' option prints the Voronoi vertices. +The 'o' option prints the Voronoi vertices and the +vertices in each Voronoi region. +The 'Fv' option prints each ridge of the Voronoi diagram. +The first or zero'th vertex +indicates the infinity vertex at infinity. Its coordinates are +qh_INFINITE (\-10.101). It indicates unbounded Voronoi regions +and degenerate Delaunay triangles. +.PP +.TP +Input/Output options: +.TP +f +Print out all facets and all fields of each facet. +.TP +G +Output the hull in Geomview format. For imprecise hulls, +Geomview displays the inner and outer hull. Geomview can also +display points, ridges, vertices, coplanar points, and +facet intersections. See below for a list of options. + +For Delaunay triangulations, 'G' displays the +corresponding paraboloid. For halfspace intersection, 'G' displays the +dual polytope. +.TP +i +Output the incident vertices for each facet. +Qhull prints the number of facets followed by the +vertices of each facet. One facet is printed per line. The numbers +are the 0\[hy]relative indices of the corresponding input points. +The facets +are oriented. + +In 4d and higher, +Qhull triangulates non\[hy]simplicial facets. Each apex (the first vertex) is +a created point that corresponds to the facet's centrum. Its index is greater +than the indices of the input points. Each base +corresponds to a simplicial ridge between two facets. +To print the vertices without triangulation, use option 'Fv'. +.TP +m +Output the hull in Mathematica format. Qhull writes a Mathematica file for 2\[hy]d and 3\[hy]d +convex hulls and for 2\[hy]d Delaunay triangulations. Qhull produces a list of objects +that you can assign to a variable in Mathematica, for example: +"list= << ". If the object is 2\[hy]d, it can be +visualized by "Show[Graphics[list]] ". For 3\[hy]d objects the command is +"Show[Graphics3D[list]]". +.TP +n +Output the normal equation for each facet. +Qhull prints the dimension (plus one), the number of facets, +and the normals for each facet. The facet's offset follows its +normal coefficients. +.TP +o +Output the facets in OFF file format. +Qhull prints the dimension, number of points, number +of facets, and number of ridges. Then it prints the coordinates of +the input points and the vertices for each facet. Each facet is on +a separate line. The first number is the number of vertices. The +remainder are the indices of the corresponding points. The vertices are +oriented in 2\[hy]d, 3\[hy]d, and in simplicial facets. + +For 2\[hy]d Voronoi diagrams, +the vertices are sorted by adjacency, but not oriented. In 3\[hy]d and higher, +the Voronoi vertices are sorted by index. +See the 'v' option for more information. +.TP +p +Output the coordinates of each vertex point. +Qhull prints the dimension, the number of points, +and the coordinates for each vertex. +With the 'Gc' and 'Gi' options, it also prints coplanar +and interior points. For Voronoi diagrams, it prints the coordinates +of each Voronoi vertex. +.TP +s +Print a summary to stderr. If no output options +are specified at all, a summary goes to stdout. The summary lists +the number of input points, the dimension, the number of vertices +in the convex hull, the number of facets in the convex hull, the +number of good facets (if 'Pg'), and statistics. + +The last two statistics (if needed) measure the maximum distance +from a point or vertex to a +facet. The number in parenthesis (e.g., 2.1x) is the ratio between the +maximum distance and the worst\[hy]case distance due to merging +two simplicial facets. +.PP +.TP +Precision options +.TP +An +Maximum angle given as a cosine. If the angle between a pair of facet +normals +is greater than n, Qhull merges one of the facets into a neighbor. +If 'n' is negative, Qhull tests angles after adding +each point to the hull (pre\[hy]merging). +If 'n' is positive, Qhull tests angles after +constructing the hull (post\[hy]merging). +Both pre\[hy] and post\[hy]merging can be defined. + +Option 'C0' or 'C\-0' is set if the corresponding 'Cn' or 'C\-n' +is not set. If 'Qx' +is set, then 'A\-n' and 'C\-n' are checked after the hull is constructed +and before 'An' and 'Cn' are checked. +.TP +Cn +Centrum radius. +If a centrum is less than n below a neighboring facet, Qhull merges one +of the facets. +If 'n' is negative or '\-0', Qhull tests and merges facets after adding +each point to the hull. This is called "pre\[hy]merging". If 'n' is positive, +Qhull tests for convexity after constructing the hull ("post\[hy]merging"). +Both pre\[hy] and post\[hy]merging can be defined. + +For 5\[hy]d and higher, 'Qx' should be used +instead of 'C\-n'. Otherwise, most or all facets may be merged +together. +.TP +En +Maximum roundoff error for distance computations. +.TP +Rn +Randomly perturb distance computations up to +/\- n * max_coord. +This option perturbs every distance, hyperplane, and angle computation. +To use time as the random number seed, use option 'QR\-1'. +.TP +Vn +Minimum distance for a facet to be visible. +A facet is visible if the distance from the point to the +facet is greater than 'Vn'. + +Without merging, the default value for 'Vn' is the round\[hy]off error ('En'). +With merging, the default value is the pre\[hy]merge centrum ('C\-n') in 2\[hy]d or +3\[hy]d, or three times that in other dimensions. If the outside width +is specified ('Wn'), the maximum, default value for 'Vn' is 'Wn'. +.TP +Un +Maximum distance below a facet for a point to be coplanar to the facet. The +default value is 'Vn'. +.TP +Wn +Minimum outside width of the hull. Points are added to the convex hull +only if they are clearly outside of a facet. A point is outside of a +facet if its distance to the facet is greater than 'Wn'. The normal +value for 'Wn' is 'En'. If the user specifies pre\[hy]merging and +does not set 'Wn', than 'Wn' is set +to the premerge 'Cn' and maxcoord*(1\-An). +.PP +.TP +Additional input/output formats +.TP +Fa +Print area for each facet. +For Delaunay triangulations, the area is the area of the triangle. +For Voronoi diagrams, the area is the area of the dual facet. +Use 'PAn' for printing the n largest facets, and option 'PFn' for +printing facets larger than 'n'. + +The area for non\[hy]simplicial facets is the sum of the +areas for each ridge to the centrum. Vertices far below +the facet's hyperplane are ignored. +The reported area may be significantly less than the actual area. +.TP +FA +Compute the total area and volume for option 's'. It is an approximation +for non\[hy]simplicial facets (see 'Fa'). +.TP +Fc +Print coplanar points for each facet. The output starts with the +number of facets. Then each facet is printed one per line. Each line +is the number of coplanar points followed by the point ids. +Option 'Qi' includes the interior points. Each coplanar point (interior point) is +assigned to the facet it is furthest above (resp., least below). +.TP +FC +Print centrums for each facet. The output starts with the +dimension followed by the number of facets. +Then each facet centrum is printed, one per line. +.TP +Fd +Read input in cdd format with homogeneous points. +The input starts with comments. The first comment is reported in +the summary. +Data starts after a "begin" line. The next line is the number of points +followed by the dimension+1 and "real" or "integer". Then the points +are listed with a leading "1" or "1.0". The data ends with an "end" line. + +For halfspaces ('Fd Hn,n,...'), the input format is the same. Each halfspace +starts with its offset. The sign of the offset is the opposite of Qhull's +convention. +.TP +FD +Print normals ('n', 'Fo', 'Fi') or points ('p') in cdd format. +The first line is the command line that invoked Qhull. +Data starts with a "begin" line. The next line is the number of normals or points +followed by the dimension+1 and "real". Then the normals or points +are listed with the offset before the coefficients. The offset for points is +1.0. The offset for normals has the opposite sign. +The data ends with an "end" line. +.TP +FF +Print facets (as in 'f') without printing the ridges. +.TP +Fi +Print inner planes for each facet. The inner plane is below all vertices. +.TP +Fi +Print separating hyperplanes for bounded, inner regions of the Voronoi +diagram. The first line is the number +of ridges. Then each hyperplane is printed, one per line. A line starts +with the number of indices and floats. The first pair lists +adjacent input +sites, the next d floats are the normalized coefficients for the hyperplane, +and the last float is the offset. The hyperplane is oriented toward 'QVn' +(if defined), or the first input site of the pair. Use 'Tv' to +verify that the hyperplanes are perpendicular bisectors. Use 'Fo' for +unbounded regions, and 'Fv' for the corresponding Voronoi vertices. +.TP +FI +Print facet identifiers. +.TP +Fm +Print number of merges for each facet. At most 511 merges are reported for +a facet. See 'PMn' for printing the facets with the most merges. +.TP +FM +Output the hull in Maple format. Qhull writes a Maple +file for 2\[hy]d and 3\[hy]d +convex hulls and for 2\[hy]d Delaunay triangulations. Qhull produces a '.mpl' +file for displaying with display3d(). +.TP +Fn +Print neighbors for each facet. The output starts with the number of facets. +Then each facet is printed one per line. Each line +is the number of neighbors followed by an index for each neighbor. The indices +match the other facet output formats. + +A negative index indicates an unprinted +facet due to printing only good facets ('Pg'). It is the negation of the facet's +ID (option 'FI'). +For example, negative indices are used for facets +"at infinity" in the Delaunay triangulation. +.TP +FN +Print vertex neighbors or coplanar facet for each point. +The first line is the number +of points. Then each point is printed, one per line. If the +point is coplanar, the line is "1" followed by the facet's ID. +If the point is +not a selected vertex, the line is "0". +Otherwise, each line is the number of +neighbors followed by the corresponding facet indices (see 'Fn'). +.TP +Fo +Print outer planes for each facet in the same format as 'n'. +The outer plane is above all points. +.TP +Fo +Print separating hyperplanes for unbounded, outer regions of the Voronoi +diagram. The first line is the number +of ridges. Then each hyperplane is printed, one per line. A line starts +with the number of indices and floats. The first pair lists +adjacent input +sites, the next d floats are the normalized coefficients for the hyperplane, +and the last float is the offset. The hyperplane is oriented toward 'QVn' +(if defined), or the first input site of the pair. Use 'Tv' to +verify that the hyperplanes are perpendicular bisectors. Use 'Fi' for +bounded regions, and 'Fv' for the corresponding Voronoi vertices. +.TP +FO +List all options to stderr, including the default values. Additional 'FO's +are printed to stdout. +.TP +Fp +Print points for halfspace intersections (option 'Hn,n,...'). Each +intersection corresponds to a facet of the dual polytope. +The "infinity" point [\-10.101,\-10.101,...] +indicates an unbounded intersection. +.TP +FP +For each coplanar point ('Qc') print the point ID of the nearest vertex, +the point ID, the facet ID, and the distance. +.TP +FQ +Print command used for qhull and input. +.TP +Fs +Print a summary. The first line consists of the number of integers ("8"), +followed by the dimension, the number of points, the number of vertices, +the number of facets, the number of vertices selected for output, the +number of facets selected for output, the number of coplanar points selected +for output, number of simplicial, unmerged facets in output + +The second line consists of the number of reals ("2"), +followed by the maxmimum offset to an outer plane and and minimum offset to +an inner plane. Roundoff is included. Later +versions of Qhull may produce additional integers or reals. +.TP +FS +Print the size of the hull. The first line consists of the number of integers ("0"). +The second line consists of the number of reals ("2"), +followed by the total facet area, and the total volume. +Later +versions of Qhull may produce additional integers or reals. + +The total volume measures the volume +of the intersection of the halfspaces defined by each facet. +Both area and volume are +approximations for non\[hy]simplicial facets. See option 'Fa'. +.TP +Ft +Print a triangulation with added points for non\[hy]simplicial +facets. The first line is the dimension and the second line is the +number of points and the number of facets. The points follow, one +per line, then the facets follow as a list of point indices. With option 'Qz', the +points include the point\[hy]at\[hy]infinity. +.TP +Fv +Print vertices for each facet. The first line is the number +of facets. Then each facet is printed, one per line. Each line is +the number of vertices followed by the corresponding point ids. Vertices +are listed in the order they were added to the hull (the last one is first). +.TP +Fv +Print all ridges of a Voronoi diagram. The first line is the number +of ridges. Then each ridge is printed, one per line. A line starts +with the number of indices. The first pair lists adjacent input +sites, the remaining indices list Voronoi vertices. Vertex '0' indicates +the vertex\[hy]at\[hy]infinity (i.e., an unbounded ray). In 3\[hy]d, the vertices +are listed in order. See 'Fi' and 'Fo' for separating hyperplanes. +.TP +FV +Print average vertex. The average vertex is a feasible point +for halfspace intersection. +.TP +Fx +List extreme points (vertices) of the convex hull. The first line +is the number of points. The other lines give the indices of the +corresponding points. The first point is '0'. In 2\[hy]d, the points +occur in counter\[hy]clockwise order; otherwise they occur in input order. +For Delaunay triangulations, 'Fx' lists the extreme points of the +input sites. The points are unordered. +.PP +.TP +Geomview options +.TP +G +Produce a file for viewing with Geomview. Without other options, +Qhull displays edges in 2\[hy]d, outer planes in 3\[hy]d, and ridges in 4\[hy]d. +A ridge can be +explicit or implicit. An explicit ridge is a dim\-1 dimensional simplex +between two facets. +In 4\[hy]d, the explicit ridges are triangles. +When displaying a ridge in 4\[hy]d, Qhull projects the ridge's vertices to +one of its facets' hyperplanes. +Use 'Gh' to +project ridges to the intersection of both hyperplanes. +.TP +Ga +Display all input points as dots. +.TP +Gc +Display the centrum for each facet in 3\[hy]d. The centrum is defined by a +green radius sitting on a blue plane. The plane corresponds to the +facet's hyperplane. +The radius is defined by 'C\-n' or 'Cn'. +.TP +GDn +Drop dimension n in 3\[hy]d or 4\[hy]d. The result is a 2\[hy]d or 3\[hy]d object. +.TP +Gh +Display hyperplane intersections in 3\[hy]d and 4\[hy]d. In 3\[hy]d, the +intersection is a black line. It lies on two neighboring hyperplanes +(c.f., the blue squares associated with centrums ('Gc')). In 4\[hy]d, +the ridges are projected to the intersection of both hyperplanes. +.TP +Gi +Display inner planes in 2\[hy]d and 3\[hy]d. The inner plane of a facet +is below all of its vertices. It is parallel to the facet's hyperplane. +The inner plane's color is the opposite (1\-r,1\-g,1\-b) of the outer +plane. Its edges are determined by the vertices. +.TP +Gn +Do not display inner or outer planes. By default, +Geomview displays the precise plane (no merging) or both +inner and output planes (merging). Under merging, Geomview does +not display the inner plane if the +the difference between inner and outer is too small. +.TP +Go +Display outer planes in 2\[hy]d and 3\[hy]d. The outer plane of a facet +is above all input points. It is parallel to the facet's hyperplane. +Its color is determined by the facet's normal, and its +edges are determined by the vertices. +.TP +Gp +Display coplanar points and vertices as radii. A radius defines a ball +which corresponds to the imprecision of the point. The imprecision is +the maximum of the roundoff error, the centrum radius, and maxcoord * +(1\-An). It is at least 1/20'th of the maximum coordinate, +and ignores post\[hy]merging if pre\[hy]merging is done. +.TP +Gr +Display ridges in 3\[hy]d. A ridge connects the two vertices that are shared +by neighboring facets. Ridges are always displayed in 4\[hy]d. +.TP +Gt +A 3\[hy]d Delaunay triangulation looks like a convex hull with interior +facets. Option 'Gt' removes the outside ridges to reveal the outermost +facets. It automatically sets options 'Gr' and 'GDn'. +.TP +Gv +Display vertices as spheres. The radius of the sphere corresponds to +the imprecision of the data. See 'Gp' for determining the radius. +.PP +.TP +Print options +.TP +PAn +Only the n largest facets are marked good for printing. +Unless 'PG' is set, 'Pg' is automatically set. +.TP +Pdk:n +Drop facet from output if normal[k] <= n. The option 'Pdk' uses the +default value of 0 for n. +.TP +PDk:n +Drop facet from output if normal[k] >= n. The option 'PDk' uses the +default value of 0 for n. +.TP +PFn +Only facets with area at least 'n' are marked good for printing. +Unless 'PG' is set, 'Pg' is automatically set. +.TP +Pg +Print only good facets. A good facet is either visible from a point +(the 'QGn' option) or includes a point (the 'QVn' option). It also meets the +requirements of 'Pdk' and 'PDk' options. Option 'Pg' is automatically +set for options 'PAn' and 'PFn'. +.TP +PG +Print neighbors of good facets. +.TP +PMn +Only the n facets with the most merges are marked good for printing. +Unless 'PG' is set, 'Pg' is automatically set. +.TP +Po +Force output despite precision problems. Verify ('Tv') does not check +coplanar points. +Flipped facets are reported and concave facets are counted. +If 'Po' is used, points are not +partitioned into flipped facets and a flipped facet is always visible +to a point. +Also, if an error occurs before the completion of Qhull and tracing is +not active, 'Po' outputs a neighborhood of the erroneous facets +(if any). +.TP +Pp +Do not report precision problems. +.PP +.TP +Qhull control options +.TP +Qbk:0Bk:0 +Drop dimension k from the input points. This allows the user to +take convex hulls of sub\[hy]dimensional objects. It happens before +the Delaunay and Voronoi transformation. +.TP +QbB +Scale the input points to fit the unit cube. After scaling, the lower +bound will be \-0.5 and the upper bound +0.5 in all dimensions. +For Delaunay and +Voronoi diagrams, scaling happens after projection to the paraboloid. +Under precise +arithmetic, scaling does not change the topology of the convex hull. +.TP +Qbb +Scale the last coordinate to [0, m] where m is the maximum absolute +value of the other coordinates. For Delaunay and +Voronoi diagrams, scaling happens after projection to the paraboloid. +It reduces roundoff error for inputs with integer coordinates. +Under precise +arithmetic, scaling does not change the topology of the convex hull. +.TP +Qbk:n +Scale the k'th coordinate of the input points. After scaling, the lower +bound of the input points will be n. 'Qbk' scales to \-0.5. +.TP +QBk:n +Scale the k'th coordinate of the input points. After scaling, the upper +bound will be n. 'QBk' scales to +0.5. +.TP +Qc +Keep coplanar points with the nearest facet. Output +formats 'p', 'f', 'Gp', 'Fc', 'FN', and 'FP' will print the points. +.TP +Qf +Partition points to the furthest outside facet. +.TP +Qg +Only build good facets. With the 'Qg' option, Qhull will only build +those facets that it needs to determine the good facets in the output. +See 'QGn', 'QVn', and 'PdD' for defining good facets, and 'Pg' and 'PG' +for printing good facets and their neighbors. +.TP +QGn +A facet is good (see 'Qg' and 'Pg') if it is visible from point n. If n < 0, a facet is +good if it is not visible from point n. Point n is not added to the +hull (unless 'TCn' or 'TPn'). +With rbox, use the 'Pn,m,r' option to define your point; it +will be point 0 (QG0). +.TP +Qi +Keep interior points with the nearest facet. +Output formats 'p', 'f', 'Gp', 'FN', 'FP', and 'Fc' will print the points. +.TP +QJn +Joggle each input coordinate by adding a random number in [\-n,n]. If a +precision error occurs, then qhull increases n and tries again. It does +not increase n beyond a certain value, and it stops after a certain number +of attempts [see user.h]. Option 'QJ' +selects a default value for n. The output will be simplicial. For +Delaunay triangulations, 'QJn' sets 'Qbb' to scale the last coordinate +(not if 'Qbk:n' or 'QBk:n' is set). +\'QJn' is deprecated for Voronoi diagrams. See also 'Qt'. +.TP +Qm +Only process points that would otherwise increase max_outside. Other +points are treated as coplanar or interior points. +.TP +Qr +Process random outside points instead of furthest ones. This makes +Qhull equivalent to the randomized incremental algorithms. CPU time +is not reported since the randomization is inefficient. +.TP +QRn +Randomly rotate the input points. If n=0, use time as the random number seed. +If n>0, use n as the random number seed. If n=\-1, don't rotate but use +time as the random number seed. For Delaunay triangulations ('d' and 'v'), +rotate about the last axis. +.TP +Qs +Search all points for the initial simplex. +.TP +Qt +Triangulated output. Triangulate all non\[hy]simplicial facets. +\'Qt' is deprecated for Voronoi diagrams. See also 'Qt'. +.TP +Qv +Test vertex neighbors for convexity after post\[hy]merging. +To use the 'Qv' option, you also need to set a merge option +(e.g., 'Qx' or 'C\-0'). +.TP +QVn +A good facet (see 'Qg' and 'Pg') includes point n. If n<0, then a good facet does not +include point n. The point is either in the initial simplex or it +is the first point added to the hull. Option 'QVn' may not be used with merging. +.TP +Qx +Perform exact merges while building the hull. The "exact" merges +are merging a point into a coplanar facet (defined by 'Vn', 'Un', +and 'C\-n'), merging concave facets, merging duplicate ridges, and +merging flipped facets. Coplanar merges and angle coplanar merges ('A\-n') +are not performed. Concavity testing is delayed until a merge occurs. + +After +the hull is built, all coplanar merges are performed (defined by 'C\-n' +and 'A\-n'), then post\[hy]merges are performed +(defined by 'Cn' and 'An'). +.TP +Qz +Add a point "at infinity" that is above the paraboloid for Delaunay triangulations +and Voronoi diagrams. This reduces precision problems and allows the triangulation +of cospherical points. +.PP +.TP +Qhull experiments and speedups +.TP +Q0 +Turn off pre\[hy]merging as a default option. +With 'Q0'/'Qx' and without explicit pre\[hy]merge options, Qhull +ignores precision issues while constructing the convex hull. This +may lead to precision errors. If so, a descriptive warning is +generated. +.TP +Q1 +With 'Q1', Qhull sorts merges by type (coplanar, angle coplanar, concave) +instead of by angle. +.TP +Q2 +With 'Q2', Qhull merges all facets at once instead of using +independent sets of merges and then retesting. +.TP +Q3 +With 'Q3', Qhull does not remove redundant vertices. +.TP +Q4 +With 'Q4', Qhull avoids merges of an old facet into a new facet. +.TP +Q5 +With 'Q5', Qhull does not correct outer planes at the end. The +maximum outer plane is used instead. +.TP +Q6 +With 'Q6', Qhull does not pre\[hy]merge concave or coplanar facets. +.TP +Q7 +With 'Q7', Qhull processes facets in depth\[hy]first order instead of +breadth\[hy]first order. +.TP +Q8 +With 'Q8' and merging, Qhull does not retain near\[hy]interior points for adjusting +outer planes. 'Qc' will probably retain +all points that adjust outer planes. +.TP +Q9 +With 'Q9', Qhull processes the furthest of all outside sets at each iteration. +.TP +Q10 +With 'Q10', Qhull does not use special processing for narrow distributions. +.TP +Q11 +With 'Q11', Qhull copies normals and recompute centrums for tricoplanar facets. +.TP +Q12 +With 'Q12', Qhull does not report a very wide merge due to a duplicated ridge with nearly coincident vertices +.PP +.TP +Trace options +.TP +Tn +Trace at level n. Qhull includes full execution tracing. 'T\-1' +traces events. 'T1' traces +the overall execution of the program. 'T2' and 'T3' trace overall +execution and geometric and topological events. 'T4' traces the +algorithm. 'T5' includes information about memory allocation and +Gaussian elimination. +.TP +Ta +Annotate output with codes that identify the +corresponding qh_fprintf() statement. +.TP +Tc +Check frequently during execution. This will catch most inconsistency +errors. +.TP +TCn +Stop Qhull after building the cone of new facets for point n. The +output for 'f' includes the cone and the old hull. +See also 'TVn'. +.TP +TFn +Report progress whenever more than n facets are created +During post\[hy]merging, 'TFn' +reports progress after more than n/2 merges. +.TP +TI file +Input data from 'file'. The filename may not include spaces or +quotes. +.TP +TO file +Output results to 'file'. The name may be enclosed in single +quotes. +.TP +TPn +Turn on tracing when point n is added to the hull. Trace +partitions of point n. If used with TWn, turn off +tracing after adding point n to the hull. +.TP +TRn +Rerun qhull n times. Usually used with 'QJn' to determine the +probability that a given joggle will fail. +.TP +Ts +Collect statistics and print to stderr at the end of execution. +.TP +Tv +Verify the convex hull. This checks the topological structure, facet +convexity, and point inclusion. +If precision problems occurred, facet convexity is tested whether or +not 'Tv' is selected. +Option 'Tv' does not check point inclusion if forcing output with 'Po', +or if 'Q5' is set. + +For point inclusion testing, Qhull verifies that all points are below +all outer planes (facet\->maxoutside). Point inclusion is exhaustive +if merging or if the facet\[hy]point product is small enough; +otherwise Qhull verifies each point with a directed +search (qh_findbest). + +Point inclusion testing occurs after producing output. It prints +a message to stderr unless option 'Pp' is used. This +allows the user to interrupt Qhull without changing the output. +.TP +TVn +Stop Qhull after adding point n. If n < 0, stop Qhull before adding +point n. Output shows the hull at this time. See also 'TCn' +.TP +TMn +Turn on tracing at n'th merge. +.TP +TWn +Trace merge facets when the width is greater than n. +.TP +Tz +Redirect stderr to stdout. +.PP +.SH BUGS +Please report bugs to Brad Barber at qhull_bug@qhull.org. + +If Qhull does not compile, it is due to an incompatibility between your +system and ours. The first thing to check is that your compiler is +ANSI standard. If it is, check the man page for the best options, or +find someone to help you. If you locate the cause of your problem, +please send email since it might help others. + +If Qhull compiles but crashes on the test case (rbox D4), there's +still incompatibility between your system and ours. Typically it's +been due to mem.c and memory alignment. You can use qh_NOmem in mem.h +to turn off memory management. Please let us know if you figure out +how to fix these problems. + +If you do find a problem, try to simplify it before reporting the +error. Try different size inputs to locate the smallest one that +causes an error. You're welcome to hunt through the code using the +execution trace as a guide. This is especially true if you're +incorporating Qhull into your own program. + +When you do report an error, please attach a data set to the +end of your message. This allows us to see the error for ourselves. +Qhull is maintained part\[hy]time. +.PP +.SH E\[hy]MAIL +Please send correspondence to qhull@qhull.org and report bugs to +qhull_bug@qhull.org. Let us know how you use Qhull. If you +mention it in a paper, please send the reference and an abstract. + +If you would like to get Qhull announcements (e.g., a new version) +and news (any bugs that get fixed, etc.), let us know and we will add you to +our mailing list. If you would like to communicate with other +Qhull users, we will add you to the qhull_users alias. +For Internet news about geometric algorithms and convex hulls, look at +comp.graphics.algorithms and sci.math.num\-analysis + +.SH SEE ALSO +rbox(1) + +Barber, C. B., D.P. Dobkin, and H.T. Huhdanpaa, +"The Quickhull Algorithm for Convex Hulls," ACM +Trans. on Mathematical Software, 22(4):469\[en]483, Dec. 1996. +http://portal.acm.org/citation.cfm?doid=235815.235821 +http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.117.405 + +Clarkson, K.L., K. Mehlhorn, and R. Seidel, "Four results on randomized +incremental construction," Computational Geometry: Theory and Applications, +vol. 3, p. 185\[en]211, 1993. + +Preparata, F. and M. Shamos, Computational +Geometry, Springer\[hy]Verlag, New York, 1985. + +.PP +.SH AUTHORS +.nf + C. Bradford Barber Hannu Huhdanpaa + bradb@shore.net hannu@qhull.org + + .fi + +.SH ACKNOWLEDGEMENTS + +A special thanks to Albert Marden, Victor Milenkovic, the Geometry Center, +Harvard University, and Endocardial Solutions, Inc. for supporting this work. + +Qhull 1.0 and 2.0 were developed under National Science Foundation +grants NSF/DMS\[hy]8920161 and NSF\[hy]CCR\[hy]91\[hy]15793 750\[hy]7504. David Dobkin +guided the original work at Princeton University. +If you find it useful, please let us know. + +The Geometry Center is supported by grant DMS\[hy]8920161 from the National +Science Foundation, by grant DOE/DE\[hy]FG02\[hy]92ER25137 from the Department +of Energy, by the University of Minnesota, and by Minnesota Technology, Inc. + +Qhull is available from http://www.qhull.org diff --git a/xs/src/qhull/html/qhull.txt b/xs/src/qhull/html/qhull.txt new file mode 100644 index 000000000..03753547e --- /dev/null +++ b/xs/src/qhull/html/qhull.txt @@ -0,0 +1,1263 @@ + + + +qhull(1) qhull(1) + + +NAME + qhull - convex hull, Delaunay triangulation, Voronoi dia- + gram, halfspace intersection about a point, hull volume, facet area + +SYNOPSIS + qhull- compute convex hulls and related structures + input (stdin): dimension, #points, point coordinates + first comment (non-numeric) is listed in the summary + halfspace: use dim plus one with offsets after coefficients + + options (qh-quick.htm): + d - Delaunay triangulation by lifting points to a paraboloid + v - Voronoi diagram via the Delaunay triangulation + H1,1 - Halfspace intersection about [1,1,0,...] + d Qu - Furthest-site Delaunay triangulation (upper convex hull) + v Qu - Furthest-site Voronoi diagram + QJ - Joggle the input to avoid precision problems + . - concise list of all options + - - one-line description of all options + + Output options (subset): + FA - compute total area and volume + Fx - extreme points (convex hull vertices) + G - Geomview output (2-d, 3-d and 4-d) + Fp - halfspace intersection coordinates + m - Mathematica output (2-d and 3-d) + n - normals with offsets + o - OFF file format (if Voronoi, outputs regions) + TO file- output results to file, may be enclosed in single quotes + f - print all fields of all facets + s - summary of results (default) + Tv - verify result: structure, convexity, and point inclusion + p - vertex coordinates + i - vertices incident to each facet + + example: + rbox 1000 s | qhull Tv s FA + + - html manual: index.htm + - installation: README.txt + - see also: COPYING.txt, REGISTER.txt, Changes.txt + - WWW: + - GIT: + - mirror: + - news: + - Geomview: + - news group: + - FAQ: + - email: qhull@qhull.org + - bug reports: qhull_bug@qhull.org + + + + +Geometry Center 2003/12/30 1 + + + + + +qhull(1) qhull(1) + + + The sections are: + - INTRODUCTION + - DESCRIPTION, a description of Qhull + - IMPRECISION, how Qhull handles imprecision + - OPTIONS + - Input and output options + - Additional input/output formats + - Precision options + - Geomview options + - Print options + - Qhull options + - Trace options + - BUGS + - E-MAIL + - SEE ALSO + - AUTHORS + - ACKNOWLEGEMENTS + + This man page briefly describes all Qhull options. Please + report any mismatches with Qhull's html manual (qh- + man.htm). + + + +INTRODUCTION + Qhull is a general dimension code for computing convex + hulls, Delaunay triangulations, Voronoi diagram, furthest- + site Voronoi diagram, furthest-site Delaunay triangula- + tions, and halfspace intersections about a point. It + implements the Quickhull algorithm for computing the con- + vex hull. Qhull handles round-off errors from floating + point arithmetic. It can approximate a convex hull. + + The program includes options for hull volume, facet area, + partial hulls, input transformations, randomization, trac- + ing, multiple output formats, and execution statistics. + The program can be called from within your application. + You can view the results in 2-d, 3-d and 4-d with + Geomview. + + +DESCRIPTION + The format of input is the following: first line contains + the dimension, second line contains the number of input + points, and point coordinates follow. The dimension and + number of points can be reversed. Comments and line + breaks are ignored. A comment starts with a non-numeric + character and continues to the end of line. The first + comment is reported in summaries and statistics. Error + reporting is better if there is one point per line. + + The default printout option is a short summary. There are + many other output formats. + + + + +Geometry Center 2003/12/30 2 + + + + + +qhull(1) qhull(1) + + + Qhull implements the Quickhull algorithm for convex hull. + This algorithm combines the 2-d Quickhull algorithm with + the n-d beneath-beyond algorithm [c.f., Preparata & Shamos + '85]. It is similar to the randomized algorithms of + Clarkson and others [Clarkson et al. '93]. The main + advantages of Quickhull are output sensitive performance, + reduced space requirements, and automatic handling of pre- + cision problems. + + The data structure produced by Qhull consists of vertices, + ridges, and facets. A vertex is a point of the input set. + A ridge is a set of d vertices and two neighboring facets. + For example in 3-d, a ridge is an edge of the polyhedron. + A facet is a set of ridges, a set of neighboring facets, a + set of incident vertices, and a hyperplane equation. For + simplicial facets, the ridges are defined by the vertices + and neighboring facets. When Qhull merges two facets, it + produces a non-simplicial facet. A non-simplicial facet + has more than d neighbors and may share more than one + ridge with a neighbor. + + +IMPRECISION + Since Qhull uses floating point arithmetic, roundoff error + may occur for each calculation. This causes problems for + most geometric algorithms. + + Qhull automatically sets option 'C-0' in 2-d, 3-d, and + 4-d, or option 'Qx' in 5-d and higher. These options han- + dle precision problems by merging facets. Alternatively, + use option 'QJ' to joggle the input. + + With 'C-0', Qhull merges non-convex facets while con- + structing the hull. The remaining facets are clearly con- + vex. With 'Qx', Qhull merges coplanar horizon facets, + flipped facets, concave facets and duplicated ridges. It + merges coplanar facets after constructing the hull. With + 'Qx', coplanar points may be missed, but it appears to be + unlikely. + + To guarantee triangular output, joggle the input with + option 'QJ'. Facet merging will not occur. + +OPTIONS + To get a list of the most important options, execute + 'qhull' by itself. To get a complete list of options, + execute 'qhull -'. To get a complete, concise list of + options, execute 'qhull .'. + + Options can be in any order. Capitalized options take an + argument (except 'PG' and 'F' options). Single letters + are used for output formats and precision constants. The + other options are grouped into menus for other output for- + mats ('F'), Geomview output ('G'), printing ('P'), Qhull + + + +Geometry Center 2003/12/30 3 + + + + + +qhull(1) qhull(1) + + + control ('Q'), and tracing ('T'). + + Main options: + + default + Compute the convex hull of the input points. + Report a summary of the result. + + d Compute the Delaunay triangulation by lifting the + input points to a paraboloid. The 'o' option + prints the input points and facets. The 'QJ' + option guarantees triangular output. The 'Ft' + option prints a triangulation. It adds points (the + centrums) to non-simplicial facets. + + v Compute the Voronoi diagram from the Delaunay tri- + angulation. The 'p' option prints the Voronoi ver- + tices. The 'o' option prints the Voronoi vertices + and the vertices in each Voronoi region. It lists + regions in site id order. The 'Fv' option prints + each ridge of the Voronoi diagram. The first or + zero'th vertex indicates the infinity vertex. Its + coordinates are qh_INFINITE (-10.101). It indi- + cates unbounded Voronoi regions or degenerate + Delaunay triangles. + + Hn,n,... + Compute halfspace intersection about [n,n,0,...]. + The input is a set of halfspaces defined in the + same format as 'n', 'Fo', and 'Fi'. Use 'Fp' to + print the intersection points. Use 'Fv' to list + the intersection points for each halfspace. The + other output formats display the dual convex hull. + + The point [n,n,n,...] is a feasible point for the + halfspaces, i.e., a point that is inside all of the + halfspaces (Hx+b <= 0). The default coordinate + value is 0. + + The input may start with a feasible point. If so, + use 'H' by itself. The input starts with a feasi- + ble point when the first number is the dimension, + the second number is "1", and the coordinates com- + plete a line. The 'FV' option produces a feasible + point for a convex hull. + + d Qu Compute the furthest-site Delaunay triangulation + from the upper convex hull. The 'o' option prints + the input points and facets. The 'QJ' option guar- + antees triangular otuput. You can also use facets. + + v Qu Compute the furthest-site Voronoi diagram. The 'p' + option prints the Voronoi vertices. The 'o' option + prints the Voronoi vertices and the vertices in + + + +Geometry Center 2003/12/30 4 + + + + + +qhull(1) qhull(1) + + + each Voronoi region. The 'Fv' option prints each + ridge of the Voronoi diagram. The first or zero'th + vertex indicates the infinity vertex at infinity. + Its coordinates are qh_INFINITE (-10.101). It + indicates unbounded Voronoi regions and degenerate + Delaunay triangles. + + Qt Triangulated output. + + + Input/Output options: + + f Print out all facets and all fields of each facet. + + G Output the hull in Geomview format. For imprecise + hulls, Geomview displays the inner and outer hull. + Geomview can also display points, ridges, vertices, + coplanar points, and facet intersections. See + below for a list of options. + + For Delaunay triangulations, 'G' displays the cor- + responding paraboloid. For halfspace intersection, + 'G' displays the dual polytope. + + i Output the incident vertices for each facet. Qhull + prints the number of facets followed by the ver- + tices of each facet. One facet is printed per + line. The numbers are the 0-relative indices of + the corresponding input points. The facets are + oriented. + + In 4-d and higher, Qhull triangulates non-simpli- + cial facets. Each apex (the first vertex) is a + created point that corresponds to the facet's cen- + trum. Its index is greater than the indices of the + input points. Each base corresponds to a simpli- + cial ridge between two facets. To print the ver- + tices without triangulation, use option 'Fv'. + + m Output the hull in Mathematica format. Qhull + writes a Mathematica file for 2-d and 3-d convex + hulls and for 2-d Delaunay triangulations. Qhull + produces a list of objects that you can assign to a + variable in Mathematica, for example: "list= << + ". If the object is 2-d, it can be + visualized by "Show[Graphics[list]] ". For 3-d + objects the command is "Show[Graphics3D[list]]". + + n Output the normal equation for each facet. Qhull + prints the dimension (plus one), the number of + facets, and the normals for each facet. The + facet's offset follows its normal coefficients. + + o Output the facets in OFF file format. Qhull prints + the dimension, number of points, number of facets, + and number of ridges. Then it prints the + + + +Geometry Center 2003/12/30 5 + + + + + +qhull(1) qhull(1) + + + coordinates of the input points and the vertices + for each facet. Each facet is on a separate line. + The first number is the number of vertices. The + remainder are the indices of the corresponding + points. The vertices are oriented in 2-d, 3-d, and + in simplicial facets. + + For 2-d Voronoi diagrams, the vertices are sorted + by adjacency, but not oriented. In 3-d and higher, + the Voronoi vertices are sorted by index. See the + 'v' option for more information. + + p Output the coordinates of each vertex point. Qhull + prints the dimension, the number of points, and the + coordinates for each vertex. With the 'Gc' and + 'Gi' options, it also prints coplanar and interior + points. For Voronoi diagrams, it prints the coor- + dinates of each Voronoi vertex. + + s Print a summary to stderr. If no output options + are specified at all, a summary goes to stdout. + The summary lists the number of input points, the + dimension, the number of vertices in the convex + hull, the number of facets in the convex hull, the + number of good facets (if 'Pg'), and statistics. + + The last two statistics (if needed) measure the + maximum distance from a point or vertex to a facet. + The number in parenthesis (e.g., 2.1x) is the ratio + between the maximum distance and the worst-case + distance due to merging two simplicial facets. + + + Precision options + + An Maximum angle given as a cosine. If the angle + between a pair of facet normals is greater than n, Qhull + merges one of the facets into a neighbor. If 'n' + is negative, Qhull tests angles after adding each + point to the hull (pre-merging). If 'n' is posi- + tive, Qhull tests angles after constructing the + hull (post-merging). Both pre- and post-merging + can be defined. + + Option 'C0' or 'C-0' is set if the corresponding + 'Cn' or 'C-n' is not set. If 'Qx' is set, then 'A- + n' and 'C-n' are checked after the hull is con- + structed and before 'An' and 'Cn' are checked. + + Cn Centrum radius. If a centrum is less than n below + a neighboring facet, Qhull merges one of the + facets. If 'n' is negative or '-0', Qhull tests + and merges facets after adding each point to the + hull. This is called "pre-merging". If 'n' is + + + +Geometry Center 2003/12/30 6 + + + + + +qhull(1) qhull(1) + + + positive, Qhull tests for convexity after con- + structing the hull ("post-merging"). Both pre- and + post-merging can be defined. + + For 5-d and higher, 'Qx' should be used instead of + 'C-n'. Otherwise, most or all facets may be merged + together. + + En Maximum roundoff error for distance computations. + + Rn Randomly perturb distance computations up to +/- n + * max_coord. This option perturbs every distance, + hyperplane, and angle computation. To use time as + the random number seed, use option 'QR-1'. + + Vn Minimum distance for a facet to be visible. A + facet is visible if the distance from the point to + the facet is greater than 'Vn'. + + Without merging, the default value for 'Vn' is the + round-off error ('En'). With merging, the default + value is the pre-merge centrum ('C-n') in 2-d or + 3--d, or three times that in other dimensions. If + the outside width is specified ('Wn'), the maximum, + default value for 'Vn' is 'Wn'. + + Un Maximum distance below a facet for a point to be + coplanar to the facet. The default value is 'Vn'. + + Wn Minimum outside width of the hull. Points are + added to the convex hull only if they are clearly + outside of a facet. A point is outside of a facet + if its distance to the facet is greater than 'Wn'. + The normal value for 'Wn' is 'En'. If the user + specifies pre-merging and does not set 'Wn', than + 'Wn' is set to the premerge 'Cn' and maxco- + ord*(1-An). + + + Additional input/output formats + + Fa Print area for each facet. For Delaunay triangula- + tions, the area is the area of the triangle. For + Voronoi diagrams, the area is the area of the dual + facet. Use 'PAn' for printing the n largest + facets, and option 'PFn' for printing facets larger + than 'n'. + + The area for non-simplicial facets is the sum of + the areas for each ridge to the centrum. Vertices + far below the facet's hyperplane are ignored. The + reported area may be significantly less than the + actual area. + + + + +Geometry Center 2003/12/30 7 + + + + + +qhull(1) qhull(1) + + + FA Compute the total area and volume for option 's'. + It is an approximation for non-simplicial facets + (see 'Fa'). + + Fc Print coplanar points for each facet. The output + starts with the number of facets. Then each facet + is printed one per line. Each line is the number + of coplanar points followed by the point ids. + Option 'Qi' includes the interior points. Each + coplanar point (interior point) is assigned to the + facet it is furthest above (resp., least below). + + FC Print centrums for each facet. The output starts + with the dimension followed by the number of + facets. Then each facet centrum is printed, one + per line. + + Fd Read input in cdd format with homogeneous points. + The input starts with comments. The first comment + is reported in the summary. Data starts after a + "begin" line. The next line is the number of + points followed by the dimension+1 and "real" or + "integer". Then the points are listed with a + leading "1" or "1.0". The data ends with an "end" + line. + + For halfspaces ('Fd Hn,n,...'), the input format is + the same. Each halfspace starts with its offset. + The sign of the offset is the opposite of Qhull's + convention. + + FD Print normals ('n', 'Fo', 'Fi') or points ('p') in + cdd format. The first line is the command line + that invoked Qhull. Data starts with a "begin" + line. The next line is the number of normals or + points followed by the dimension+1 and "real". + Then the normals or points are listed with the + offset before the coefficients. The offset for + points is 1.0. The offset for normals has the + opposite sign. The data ends with an "end" line. + + FF Print facets (as in 'f') without printing the + ridges. + + Fi Print inner planes for each facet. The inner plane + is below all vertices. + + Fi Print separating hyperplanes for bounded, inner + regions of the Voronoi diagram. The first line is + the number of ridges. Then each hyperplane is + printed, one per line. A line starts with the num- + ber of indices and floats. The first pair lists + adjacent input sites, the next d floats are the + normalized coefficients for the hyperplane, and the + + + +Geometry Center 2003/12/30 8 + + + + + +qhull(1) qhull(1) + + + last float is the offset. The hyperplane is ori- + ented toward verify that the hyperplanes are per- + pendicular bisectors. Use 'Fo' for unbounded + regions, and 'Fv' for the corresponding Voronoi + vertices. + + FI Print facet identifiers. + + Fm Print number of merges for each facet. At most 511 + merges are reported for a facet. See 'PMn' for + printing the facets with the most merges. + + FM Output the hull in Maple format. See 'm' + + Fn Print neighbors for each facet. The output starts + with the number of facets. Then each facet is + printed one per line. Each line is the number of + neighbors followed by an index for each neighbor. + The indices match the other facet output formats. + + A negative index indicates an unprinted facet due + to printing only good facets ('Pg'). It is the + negation of the facet's id (option 'FI'). For + example, negative indices are used for facets "at + infinity" in the Delaunay triangulation. + + FN Print vertex neighbors or coplanar facet for each + point. The first line is the number of points. + Then each point is printed, one per line. If the + point is coplanar, the line is "1" followed by the + facet's id. If the point is not a selected vertex, + the line is "0". Otherwise, each line is the num- + ber of neighbors followed by the corresponding + facet indices (see 'Fn'). + + Fo Print outer planes for each facet in the same for- + mat as 'n'. The outer plane is above all points. + + Fo Print separating hyperplanes for unbounded, outer + regions of the Voronoi diagram. The first line is + the number of ridges. Then each hyperplane is + printed, one per line. A line starts with the num- + ber of indices and floats. The first pair lists + adjacent input sites, the next d floats are the + normalized coefficients for the hyperplane, and the + last float is the offset. The hyperplane is ori- + ented toward verify that the hyperplanes are per- + pendicular bisectors. Use 'Fi' for bounded + regions, and 'Fv' for the corresponding Voronoi + vertices. + + FO List all options to stderr, including the default + values. Additional 'FO's are printed to stdout. + + Fp Print points for halfspace intersections (option + 'Hn,n,...'). Each intersection corresponds to a + + + +Geometry Center 2003/12/30 9 + + + +qhull(1) qhull(1) + + + facet of the dual polytope. The "infinity" point + [-10.101,-10.101,...] indicates an unbounded + intersection. + + FP For each coplanar point ('Qc') print the point id + of the nearest vertex, the point id, the facet id, + and the distance. + + FQ Print command used for qhull and input. + + Fs Print a summary. The first line consists of the + number of integers ("7"), followed by the dimen- + sion, the number of points, the number of vertices, + the number of facets, the number of vertices + selected for output, the number of facets selected + for output, the number of coplanar points selected + for output. + + The second line consists of the number of reals + ("2"), followed by the maxmimum offset to an outer + plane and and minimum offset to an inner plane. + Roundoff is included. Later versions of Qhull may + produce additional integers or reals. + + FS Print the size of the hull. The first line con- + sists of the number of integers ("0"). The second + line consists of the number of reals ("2"), fol- + lowed by the total facet area, and the total vol- + ume. Later versions of Qhull may produce addi- + tional integers or reals. + + The total volume measures the volume of the inter- + section of the halfspaces defined by each facet. + Both area and volume are approximations for non- + simplicial facets. See option 'Fa'. + + Ft Print a triangulation with added points for non- + simplicial facets. The first line is the dimension + and the second line is the number of points and the + number of facets. The points follow, one per line, + then the facets follow as a list of point indices. + With option points include the point-at-infinity. + + Fv Print vertices for each facet. The first line is + the number of facets. Then each facet is printed, + one per line. Each line is the number of vertices + followed by the corresponding point ids. Vertices + are listed in the order they were added to the hull + (the last one is first). + + Fv Print all ridges of a Voronoi diagram. The first + line is the number of ridges. Then each ridge is + printed, one per line. A line starts with the num- + ber of indices. The first pair lists adjacent + + + +Geometry Center 2003/12/30 10 + + + + + +qhull(1) qhull(1) + + + input sites, the remaining indices list Voronoi + vertices. Vertex '0' indicates the vertex-at- + infinity (i.e., an unbounded ray). In 3-d, the + vertices are listed in order. See 'Fi' and 'Fo' + for separating hyperplanes. + + FV Print average vertex. The average vertex is a fea- + sible point for halfspace intersection. + + Fx List extreme points (vertices) of the convex hull. + The first line is the number of points. The other + lines give the indices of the corresponding points. + The first point is '0'. In 2-d, the points occur + in counter-clockwise order; otherwise they occur in + input order. For Delaunay triangulations, 'Fx' + lists the extreme points of the input sites. The + points are unordered. + + + Geomview options + + G Produce a file for viewing with Geomview. Without + other options, Qhull displays edges in 2-d, outer + planes in 3-d, and ridges in 4-d. A ridge can be + explicit or implicit. An explicit ridge is a dim-1 + dimensional simplex between two facets. In 4-d, + the explicit ridges are triangles. When displaying + a ridge in 4-d, Qhull projects the ridge's vertices + to one of its facets' hyperplanes. Use 'Gh' to + project ridges to the intersection of both hyper- + planes. + + Ga Display all input points as dots. + + Gc Display the centrum for each facet in 3-d. The + centrum is defined by a green radius sitting on a + blue plane. The plane corresponds to the facet's + hyperplane. The radius is defined by 'C-n' or + 'Cn'. + + GDn Drop dimension n in 3-d or 4-d. The result is a + 2-d or 3-d object. + + Gh Display hyperplane intersections in 3-d and 4-d. + In 3-d, the intersection is a black line. It lies + on two neighboring hyperplanes (c.f., the blue + squares associated with centrums ('Gc')). In 4-d, + the ridges are projected to the intersection of + both hyperplanes. + + Gi Display inner planes in 2-d and 3-d. The inner + plane of a facet is below all of its vertices. It + is parallel to the facet's hyperplane. The inner + plane's color is the opposite (1-r,1-g,1-b) of the + + + +Geometry Center 2003/12/30 11 + + + + + +qhull(1) qhull(1) + + + outer plane. Its edges are determined by the ver- + tices. + + Gn Do not display inner or outer planes. By default, + Geomview displays the precise plane (no merging) or + both inner and output planes (merging). Under + merging, Geomview does not display the inner plane + if the the difference between inner and outer is + too small. + + Go Display outer planes in 2-d and 3-d. The outer + plane of a facet is above all input points. It is + parallel to the facet's hyperplane. Its color is + determined by the facet's normal, and its edges are + determined by the vertices. + + Gp Display coplanar points and vertices as radii. A + radius defines a ball which corresponds to the + imprecision of the point. The imprecision is the + maximum of the roundoff error, the centrum radius, + and maxcoord * (1-An). It is at least 1/20'th of + the maximum coordinate, and ignores post-merging if + pre-merging is done. + + Gr Display ridges in 3-d. A ridge connects the two + vertices that are shared by neighboring facets. + Ridges are always displayed in 4-d. + + Gt A 3-d Delaunay triangulation looks like a convex + hull with interior facets. Option 'Gt' removes the + outside ridges to reveal the outermost facets. It + automatically sets options 'Gr' and 'GDn'. + + Gv Display vertices as spheres. The radius of the + sphere corresponds to the imprecision of the data. + See 'Gp' for determining the radius. + + + Print options + + PAn Only the n largest facets are marked good for + printing. Unless 'PG' is set, 'Pg' is automati- + cally set. + + Pdk:n Drop facet from output if normal[k] <= n. The + option 'Pdk' uses the default value of 0 for n. + + PDk:n Drop facet from output if normal[k] >= n. The + option 'PDk' uses the default value of 0 for n. + + PFn Only facets with area at least 'n' are marked good + for printing. Unless 'PG' is set, 'Pg' is automat- + ically set. + + + + +Geometry Center 2003/12/30 12 + + + + + +qhull(1) qhull(1) + + + Pg Print only good facets. A good facet is either + visible from a point (the 'QGn' option) or includes + a point (the 'QVn' option). It also meets the + requirements of 'Pdk' and 'PDk' options. Option + 'Pg' is automatically set for options 'PAn' and + 'PFn'. + + PG Print neighbors of good facets. + + PMn Only the n facets with the most merges are marked + good for printing. Unless 'PG' is set, 'Pg' is + automatically set. + + Po Force output despite precision problems. Verify ('Tv') does not check + coplanar points. Flipped facets are reported and + concave facets are counted. If 'Po' is used, + points are not partitioned into flipped facets and + a flipped facet is always visible to a point. + Also, if an error occurs before the completion of + Qhull and tracing is not active, 'Po' outputs a + neighborhood of the erroneous facets (if any). + + Pp Do not report precision problems. + + + Qhull control options + + Qbk:0Bk:0 + Drop dimension k from the input points. This + allows the user to take convex hulls of sub-dimen- + sional objects. It happens before the Delaunay and + Voronoi transformation. + + QbB Scale the input points to fit the unit cube. After + scaling, the lower bound will be -0.5 and the upper + bound +0.5 in all dimensions. For Delaunay and + Voronoi diagrams, scaling happens after projection + to the paraboloid. Under precise arithmetic, scal- + ing does not change the topology of the convex + hull. + + Qbb Scale the last coordinate to [0, m] where m is the + maximum absolute value of the other coordinates. + For Delaunay and Voronoi diagrams, scaling happens + after projection to the paraboloid. It reduces + roundoff error for inputs with integer coordinates. + Under precise arithmetic, scaling does not change + the topology of the convex hull. + + Qbk:n Scale the k'th coordinate of the input points. + After scaling, the lower bound of the input points + will be n. 'Qbk' scales to -0.5. + + + +Geometry Center 2003/12/30 13 + + + + + +qhull(1) qhull(1) + + + QBk:n Scale the k'th coordinate of the input points. + After scaling, the upper bound will be n. 'QBk' + scales to +0.5. + + Qc Keep coplanar points with the nearest facet. Out- + put formats 'p', 'f', 'Gp', 'Fc', 'FN', and 'FP' + will print the points. + + Qf Partition points to the furthest outside facet. + + Qg Only build good facets. With the 'Qg' option, + Qhull will only build those facets that it needs to + determine the good facets in the output. See + 'QGn', 'QVn', and 'PdD' for defining good facets, + and 'Pg' and 'PG' for printing good facets and + their neighbors. + + QGn A facet is good (see 'Qg' and 'Pg') if it is visi- + ble from point n. If n < 0, a facet is good if it + is not visible from point n. Point n is not added + to the hull (unless 'TCn' or 'TPn'). With rbox, + use the 'Pn,m,r' option to define your point; it + will be point 0 (QG0). + + Qi Keep interior points with the nearest facet. Out- + put formats 'p', 'f', 'Gp', 'FN', 'FP', and 'Fc' + will print the points. + + QJn Joggle each input coordinate by adding a random + number in [-n,n]. If a precision error occurs, + then qhull increases n and tries again. It does + not increase n beyond a certain value, and it stops + after a certain number of attempts [see user.h]. + Option 'QJ' selects a default value for n. The + output will be simplicial. For Delaunay triangula- + tions, 'QJn' sets 'Qbb' to scale the last coordi- + nate (not if 'Qbk:n' or 'QBk:n' is set). 'QJn' is + deprecated for Voronoi diagrams. See also 'Qt'. + + Qm Only process points that would otherwise increase + max_outside. Other points are treated as coplanar + or interior points. + + Qr Process random outside points instead of furthest + ones. This makes Qhull equivalent to the random- + ized incremental algorithms. CPU time is not + reported since the randomization is inefficient. + + QRn Randomly rotate the input points. If n=0, use time + as the random number seed. If n>0, use n as the + random number seed. If n=-1, don't rotate but use + time as the random number seed. For Delaunay tri- + angulations ('d' and 'v'), rotate about the last + axis. + + + + +Geometry Center 2003/12/30 14 + + + + + +qhull(1) qhull(1) + + + Qs Search all points for the initial simplex. + + Qt Triangulated output. Triangulate non-simplicial + facets. 'Qt' is deprecated for Voronoi diagrams. + See also 'QJn' + + Qv Test vertex neighbors for convexity after post- + merging. To use the 'Qv' option, you also need to + set a merge option (e.g., 'Qx' or 'C-0'). + + QVn A good facet (see 'Qg' and 'Pg') includes point n. + If n<0, then a good facet does not include point n. + The point is either in the initial simplex or it is + the first point added to the hull. Option 'QVn' + may not be used with merging. + + Qx Perform exact merges while building the hull. The + "exact" merges are merging a point into a coplanar + facet (defined by 'Vn', 'Un', and 'C-n'), merging + concave facets, merging duplicate ridges, and merg- + ing flipped facets. Coplanar merges and angle + coplanar merges ('A-n') are not performed. Concav- + ity testing is delayed until a merge occurs. + + After the hull is built, all coplanar merges are + performed (defined by 'C-n' and 'A-n'), then post- + merges are performed (defined by 'Cn' and 'An'). + + Qz Add a point "at infinity" that is above the + paraboloid for Delaunay triangulations and Voronoi + diagrams. This reduces precision problems and + allows the triangulation of cospherical points. + + + Qhull experiments and speedups + + Q0 Turn off pre-merging as a default option. With + 'Q0'/'Qx' and without explicit pre-merge options, + Qhull ignores precision issues while constructing + the convex hull. This may lead to precision + errors. If so, a descriptive warning is generated. + + Q1 With 'Q1', Qhull sorts merges by type (coplanar, + angle coplanar, concave) instead of by angle. + + Q2 With 'Q2', Qhull merges all facets at once instead + of using independent sets of merges and then + retesting. + + Q3 With 'Q3', Qhull does not remove redundant ver- + tices. + + Q4 With 'Q4', Qhull avoids merges of an old facet into + a new facet. + + Q5 With 'Q5', Qhull does not correct outer planes at + the end. The maximum outer plane is used instead. + + + + +Geometry Center 2003/12/30 15 + + + + + +qhull(1) qhull(1) + + + Q6 With 'Q6', Qhull does not pre-merge concave or + coplanar facets. + + Q7 With 'Q7', Qhull processes facets in depth-first + order instead of breadth-first order. + + Q8 With 'Q8' and merging, Qhull does not retain near- + interior points for adjusting outer planes. 'Qc' + will probably retain all points that adjust outer + planes. + + Q9 With 'Q9', Qhull processes the furthest of all out- + side sets at each iteration. + + Q10 With 'Q10', Qhull does not use special processing + for narrow distributions. + + Q11 With 'Q11', Qhull copies normals and recomputes + centrums for tricoplanar facets. + + Q12 With 'Q12', Qhull does not report a very wide merge due + to a duplicated ridge with nearly coincident vertices + + Trace options + + Tn Trace at level n. Qhull includes full execution + tracing. 'T-1' traces events. 'T1' traces the + overall execution of the program. 'T2' and 'T3' + trace overall execution and geometric and topologi- + cal events. 'T4' traces the algorithm. 'T5' + includes information about memory allocation and + Gaussian elimination. + + Ta Annotate output with codes that identify the + corresponding qh_fprintf() statement. + + Tc Check frequently during execution. This will catch + most inconsistency errors. + + TCn Stop Qhull after building the cone of new facets + for point n. The output for 'f' includes the cone + and the old hull. See also 'TVn'. + + TFn Report progress whenever more than n facets are + created During post-merging, 'TFn' reports progress + after more than n/2 merges. + + TI file + Input data from 'file'. The filename may not include + spaces or quotes. + + TO file + Output results to 'file'. The name may be enclosed + in single quotes. + + TPn Turn on tracing when point n is added to the hull. + Trace partitions of point n. If used with TWn, turn off + tracing after adding point n to the hull. + + TRn Rerun qhull n times. Usually used with 'QJn' to + determine the probability that a given joggle will + fail. + + Ts Collect statistics and print to stderr at the end + of execution. + + Tv Verify the convex hull. This checks the topologi- + cal structure, facet convexity, and point inclu- + sion. If precision problems occurred, facet con- + vexity is tested whether or not 'Tv' is selected. + Option 'Tv' does not check point inclusion if + + + +Geometry Center 2003/12/30 16 + + + + + +qhull(1) qhull(1) + + + forcing output with 'Po', or if 'Q5' is set. + + For point inclusion testing, Qhull verifies that + all points are below all outer planes (facet->max- + outside). Point inclusion is exhaustive if merging + or if the facet-point product is small enough; oth- + erwise Qhull verifies each point with a directed + search (qh_findbest). + + Point inclusion testing occurs after producing out- + put. It prints a message to stderr unless option + 'Pp' is used. This allows the user to interrupt + Qhull without changing the output. + + TVn Stop Qhull after adding point n. If n < 0, stop + Qhull before adding point n. Output shows the hull + at this time. See also 'TCn' + + TMn Turn on tracing at n'th merge. + + TWn Trace merge facets when the width is greater than + n. + + Tz Redirect stderr to stdout. + + +BUGS + Please report bugs to Brad Barber at + qhull_bug@qhull.org. + + If Qhull does not compile, it is due to an incompatibility + between your system and ours. The first thing to check is + that your compiler is ANSI standard. If it is, check the + man page for the best options, or find someone to help + you. If you locate the cause of your problem, please send + email since it might help others. + + If Qhull compiles but crashes on the test case (rbox D4), + there's still incompatibility between your system and + ours. Typically it's been due to mem.c and memory align- + ment. You can use qh_NOmem in mem.h to turn off memory + management. Please let us know if you figure out how to + fix these problems. + + If you do find a problem, try to simplify it before + reporting the error. Try different size inputs to locate + the smallest one that causes an error. You're welcome to + hunt through the code using the execution trace as a + guide. This is especially true if you're incorporating + Qhull into your own program. + + When you do report an error, please attach a data set to + the end of your message. This allows us to see the error + for ourselves. Qhull is maintained part-time. + + + +Geometry Center 2003/12/30 17 + + + + + +qhull(1) qhull(1) + + +E-MAIL + Please send correspondence to qhull@qhull.org and + report bugs to qhull_bug@qhull.org. Let us know how + you use Qhull. If you mention it in a paper, please send + the reference and an abstract. + + If you would like to get Qhull announcements (e.g., a new + version) and news (any bugs that get fixed, etc.), let us + know and we will add you to our mailing list. If you + would like to communicate with other Qhull users, we will + add you to the qhull_users alias. For Internet news about + geometric algorithms and convex hulls, look at comp.graph- + ics.algorithms and sci.math.num-analysis + + +SEE ALSO + rbox(1) + + Barber, C. B., D.P. Dobkin, and H.T. Huhdanpaa, "The + Quickhull Algorithm for Convex Hulls," ACM Trans. on Math- + ematical Software, 22(4):469-483, Dec. 1996. + http://portal.acm.org/citation.cfm?doid=235815.235821 + http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.117.405 + + + Clarkson, K.L., K. Mehlhorn, and R. Seidel, "Four results + on randomized incremental construction," Computational + Geometry: Theory and Applications, vol. 3, p. 185-211, + 1993. + + Preparata, F. and M. Shamos, Computational Geometry, + Springer-Verlag, New York, 1985. + + + +AUTHORS + C. Bradford Barber Hannu Huhdanpaa + bradb@shore.net hannu@qhull.org + + + +ACKNOWLEDGEMENTS + A special thanks to Albert Marden, Victor Milenkovic, the + Geometry Center, Harvard University, and Endocardial Solu- + tions, Inc. for supporting this work. + + Qhull 1.0 and 2.0 were developed under National Science Foundation + grants NSF/DMS-8920161 and NSF-CCR-91-15793 750-7504. David Dobkin + + + +Geometry Center 2003/12/30 18 + + + + + +qhull(1) qhull(1) + + + guided the original work at Princeton University. If you find it + useful, please let us know. + + The Geometry Center was supported by grant DMS-8920161 from the National + Science Foundation, by grant DOE/DE-FG02-92ER25137 from the Department + of Energy, by the University of Minnesota, and by Minnesota Technology, Inc. + + Qhull is available from http://www.qhull.org + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Geometry Center 2003/12/30 19 + + diff --git a/xs/src/qhull/html/qvoron_f.htm b/xs/src/qhull/html/qvoron_f.htm new file mode 100644 index 000000000..db538b5ab --- /dev/null +++ b/xs/src/qhull/html/qvoron_f.htm @@ -0,0 +1,396 @@ + + + + +qvoronoi Qu -- furthest-site Voronoi diagram + + + + +Up: +Home page for Qhull
    +Up: Qhull manual: Table of Contents
    +To: Programs +• Options +• Output +• Formats +• Geomview +• Print +• Qhull +• Precision +• Trace +• Functions
    +To: synopsis +• input • outputs +• controls • graphics +• notes • conventions +• options + +
    + +

    [delaunay]qvoronoi Qu -- furthest-site Voronoi diagram

    + +

    The furthest-site Voronoi diagram is the furthest-neighbor map for a set of +points. Each region contains those points that are further +from one input site than any other input site. See the +survey article by Aurenhammer ['91] +and the brief introduction by O'Rourke ['94]. The furthest-site Voronoi diagram is the dual of the furthest-site Delaunay triangulation. +

    + +
    +
    +
    Example: rbox 10 D2 | qvoronoi Qu s + o TO + result
    +
    Compute the 2-d, furthest-site Voronoi diagram of 10 + random points. Write a summary to the console and the Voronoi + regions and vertices to 'result'. The first vertex of the + result indicates unbounded regions. Almost all regions + are unbounded.
    +
    + +
    +
    Example: rbox r y c G1 D2 | qvoronoi Qu + s + Fn TO + result
    +
    Compute the 2-d furthest-site Voronoi diagram of a square + and a small triangle. Write a summary to the console and the Voronoi + vertices for each input site to 'result'. + The origin is the only furthest-site Voronoi vertex. The + negative indices indicate vertices-at-infinity.
    +
    +
    + +

    +Qhull computes the furthest-site Voronoi diagram via the +furthest-site Delaunay triangulation. +Each furthest-site Voronoi vertex is the circumcenter of an upper +facet of the Delaunay triangulation. Each furthest-site Voronoi +region corresponds to a vertex of the Delaunay triangulation +(i.e., an input site).

    + +

    See Qhull FAQ - Delaunay and +Voronoi diagram questions.

    + +

    The 'qvonoroi' program is equivalent to +'qhull v Qbb' in 2-d to 3-d, and +'qhull v Qbb Qx' +in 4-d and higher. It disables the following Qhull +options: d n m v H U Qb +QB Qc Qf Qg Qi Qm Qr QR Qv Qx TR E V Fa FA FC Fp FS Ft FV Gt Q0,etc. + + +

    Copyright © 1995-2015 C.B. Barber

    + +
    +

    »furthest-site qvoronoi synopsis

    +
    + +See qvoronoi synopsis. The same +program is used for both constructions. Use option 'Qu' +for furthest-site Voronoi diagrams. + + +
    +

    »furthest-site qvoronoi +input

    +
    +

    The input data on stdin consists of:

    +
      +
    • dimension +
    • number of points
    • +
    • point coordinates
    • +
    + +

    Use I/O redirection (e.g., qvoronoi Qu < data.txt), a pipe (e.g., rbox 10 | qvoronoi Qu), +or the 'TI' option (e.g., qvoronoi TI data.txt Qu). + +

    For example, this is a square containing four random points. +Its furthest-site Voronoi diagram has on vertex and four unbounded, +separating hyperplanes (i.e., the coordinate axes) +

    +

    +rbox c 4 D2 > data +
    +2 RBOX c 4 D2
    +8
    +-0.4999921736307369 -0.3684622117955817
    +0.2556053225468894 -0.0413498678629751
    +0.0327672376602583 -0.2810408135699488
    +-0.452955383763607 0.17886471718444
    +  -0.5   -0.5
    +  -0.5    0.5
    +   0.5   -0.5
    +   0.5    0.5
    +
    + +

    qvoronoi Qu s Fo < data +

    +
    +Furthest-site Voronoi vertices by the convex hull of 8 points in 3-d:
    +
    +  Number of Voronoi regions: 8
    +  Number of Voronoi vertices: 1
    +  Number of non-simplicial Voronoi vertices: 1
    +
    +Statistics for: RBOX c 4 D2 | QVORONOI Qu s Fo
    +
    +  Number of points processed: 8
    +  Number of hyperplanes created: 20
    +  Number of facets in hull: 11
    +  Number of distance tests for qhull: 34
    +  Number of merged facets: 1
    +  Number of distance tests for merging: 107
    +  CPU seconds to compute hull (after input):  0
    +
    +4
    +5 4 5      0      1      0
    +5 4 6      1      0      0
    +5 5 7      1      0      0
    +5 6 7      0      1      0
    +
    +
    + +
    +

    » furthest-site qvoronoi +outputs

    +
    + +

    These options control the output of furthest-site Voronoi diagrams.

    +
    + +
    +
     
    +
    furthest-site Voronoi vertices
    +
    p
    +
    print the coordinates of the furthest-site Voronoi vertices. The first line + is the dimension. The second line is the number of vertices. Each + remaining line is a furthest-site Voronoi vertex. The points-in-square example + has one furthest-site Voronoi vertex at the origin.
    +
    Fn
    +
    list the neighboring furthest-site Voronoi vertices for each furthest-site Voronoi + vertex. The first line is the number of Voronoi vertices. Each + remaining line starts with the number of neighboring vertices. Negative indices (e.g., -1) indicate vertices + outside of the Voronoi diagram. In the points-in-square example, the + Voronoi vertex at the origin has four neighbors-at-infinity.
    +
    FN
    +
    list the furthest-site Voronoi vertices for each furthest-site Voronoi region. The first line is + the number of Voronoi regions. Each remaining line starts with the + number of Voronoi vertices. Negative indices (e.g., -1) indicate vertices + outside of the Voronoi diagram. + In the points-in-square example, all regions share the Voronoi vertex + at the origin.
    + +
     
    +
     
    +
    furthest-site Voronoi regions
    +
    o
    +
    print the furthest-site Voronoi regions in OFF format. The first line is the + dimension. The second line is the number of vertices, the number + of input sites, and "1". The third line represents the vertex-at-infinity. + Its coordinates are "-10.101". The next lines are the coordinates + of the furthest-site Voronoi vertices. Each remaining line starts with the number + of Voronoi vertices in a Voronoi region. In 2-d, the vertices are +listed in adjacency order (unoriented). In 3-d and higher, the +vertices are listed in numeric order. In the points-in-square + example, each unbounded region includes the Voronoi vertex at + the origin. Lines consisting of 0 indicate + interior input sites.
    +
    Fi
    +
    print separating hyperplanes for inner, bounded furthest-site Voronoi + regions. The first number is the number of separating + hyperplanes. Each remaining line starts with 3+dim. The + next two numbers are adjacent input sites. The next dim + numbers are the coefficients of the separating hyperplane. The + last number is its offset. The are no bounded, separating hyperplanes + for the points-in-square example.
    +
    Fo
    +
    print separating hyperplanes for outer, unbounded furthest-site Voronoi + regions. The first number is the number of separating + hyperplanes. Each remaining line starts with 3+dim. The + next two numbers are adjacent input sites on the convex hull. The + next dim + numbers are the coefficients of the separating hyperplane. The + last number is its offset. The points-in-square example has four + unbounded, separating hyperplanes.
    +
     
    +
     
    +
    Input sites
    +
    Fv
    +
    list ridges of furthest-site Voronoi vertices for pairs of input sites. The + first line is the number of ridges. Each remaining line starts with + two plus the number of Voronoi vertices in the ridge. The next + two numbers are two adjacent input sites. The remaining numbers list + the Voronoi vertices. As with option 'o', a 0 indicates + the vertex-at-infinity + and an unbounded, separating hyperplane. + The perpendicular bisector (separating hyperplane) + of the input sites is a flat through these vertices. + In the points-in-square example, the ridge for each edge of the square + is unbounded.
    +
     
    +
     
    +
    General
    +
    s
    +
    print summary of the furthest-site Voronoi diagram. Use 'Fs' for numeric data.
    +
    i
    +
    list input sites for each furthest-site Delaunay region. Use option 'Pp' + to avoid the warning. The first line is the number of regions. The + remaining lines list the input sites for each region. The regions are + oriented. In the points-in-square example, the square region has four + input sites. In 3-d and higher, report cospherical sites by adding extra points. +
    +
    G
    +
    Geomview output for 2-d furthest-site Voronoi diagrams.
    +
    +
    + +
    +

    » furthest-site qvoronoi +controls

    +
    + +

    These options provide additional control:

    +
    + +
    +
    Qu
    +
    must be used.
    +
    QVn
    +
    select furthest-site Voronoi vertices for input site n
    +
    Tv
    +
    verify result
    +
    TI file
    +
    input data from file. The filename may not use spaces or quotes.
    +
    TO file
    +
    output results to file. Use single quotes if the filename + contains spaces (e.g., TO 'file with spaces.txt'
    +
    TFn
    +
    report progress after constructing n facets
    +
    PDk:1
    +
    include upper and lower facets in the output. Set k + to the last dimension (e.g., 'PD2:1' for 2-d inputs).
    +
    f
    +
    facet dump. Print the data structure for each facet (i.e., + furthest-site Voronoi vertex).
    +
    + +
    +
    +

    » furthest-site qvoronoi +graphics

    +
    +

    In 2-d, Geomview output ('G') +displays a furthest-site Voronoi diagram with extra edges to +close the unbounded furthest-site Voronoi regions. All regions +will be unbounded. Since the points-in-box example has only +one furthest-site Voronoi vertex, the Geomview output is one +point.

    + +

    See the Delaunay and Voronoi +examples for a 2-d example. Turn off normalization (on +Geomview's 'obscure' menu) when comparing the furthest-site +Voronoi diagram with the corresponding Voronoi diagram.

    + +
    +

    »furthest-site qvoronoi +notes

    +
    + +

    See Voronoi notes.

    + +
    +

    »furthest-site qvoronoi conventions

    +
    + +

    The following terminology is used for furthest-site Voronoi +diagrams in Qhull. The underlying structure is a furthest-site +Delaunay triangulation from a convex hull in one higher +dimension. Upper facets of the Delaunay triangulation correspond +to vertices of the furthest-site Voronoi diagram. Vertices of the +furthest-site Delaunay triangulation correspond to input sites. +They also define regions of the furthest-site Voronoi diagram. +All vertices are extreme points of the input sites. See qconvex conventions, furthest-site delaunay +conventions, and Qhull's data structures.

    + +
      +
    • input site - a point in the input (one dimension + lower than a point on the convex hull)
    • +
    • point - a point has d+1 coordinates. The + last coordinate is the sum of the squares of the input + site's coordinates
    • +
    • vertex - a point on the upper facets of the + paraboloid. It corresponds to a unique input site.
    • +
    • furthest-site Delaunay facet - an upper facet of the + paraboloid. The last coefficient of its normal is + clearly positive.
    • +
    • furthest-site Voronoi vertex - the circumcenter + of a furthest-site Delaunay facet
    • +
    • furthest-site Voronoi region - the region of + Euclidean space further from an input site than any other + input site. Qhull lists the furthest-site Voronoi + vertices that define each furthest-site Voronoi region.
    • +
    • furthest-site Voronoi diagram - the graph of the + furthest-site Voronoi regions with the ridges (edges) + between the regions.
    • +
    • infinity vertex - the Voronoi vertex for + unbounded furthest-site Voronoi regions in 'o' output format. Its + coordinates are -10.101.
    • +
    • good facet - an furthest-site Voronoi vertex with + optional restrictions by 'QVn', + etc.
    • +
    + +
    +

    »furthest-site qvoronoi options

    +
    + +See qvoronoi options. The same +program is used for both constructions. Use option 'Qu' +for furthest-site Voronoi diagrams. +
    + + +
    + +

    Up: Home page for Qhull
    +Up: Qhull manual: Table of Contents
    +To: Programs +• Options +• Output +• Formats +• Geomview +• Print +• Qhull +• Precision +• Trace +• Functions
    +To: synopsis +• input • outputs +• controls • graphics +• notes • conventions +• options + +


    + +

    The Geometry Center +Home Page

    + +

    Comments to: qhull@qhull.org +
    +Created: Sept. 25, 1995 --- Last modified: see top

    + + diff --git a/xs/src/qhull/html/qvoronoi.htm b/xs/src/qhull/html/qvoronoi.htm new file mode 100644 index 000000000..6d81d48c1 --- /dev/null +++ b/xs/src/qhull/html/qvoronoi.htm @@ -0,0 +1,667 @@ + + + + +qvoronoi -- Voronoi diagram + + + + +Up: +Home page for Qhull
    +Up: Qhull manual: Table of Contents
    +To: Programs +• Options +• Output +• Formats +• Geomview +• Print +• Qhull +• Precision +• Trace +• Functions
    +To: synopsis +• input • outputs +• controls • graphics +• notes • conventions +• options + +
    + +

    [voronoi]qvoronoi -- Voronoi diagram

    + +

    The Voronoi diagram is the nearest-neighbor map for a set of +points. Each region contains those points that are nearer +one input site than any other input site. It has many useful properties and applications. See the +survey article by Aurenhammer ['91] +and the detailed introduction by O'Rourke ['94]. The Voronoi diagram is the +dual of the Delaunay triangulation.

    + +
    +
    +
    Example: rbox 10 D3 | qvoronoi s + o TO + result
    +
    Compute the 3-d Voronoi diagram of 10 random points. Write a + summary to the console and the Voronoi vertices and + regions to 'result'. The first vertex of the result + indicates unbounded regions.
    + +
     
    +
    Example: rbox r y c G0.1 D2 | qvoronoi + s + o TO + result
    +
    Compute the 2-d Voronoi diagram of a triangle and a small + square. Write a + summary to the console and Voronoi vertices and regions + to 'result'. Report a single Voronoi vertex for + cocircular input sites. The first vertex of the result + indicates unbounded regions. The origin is the Voronoi + vertex for the square.
    + +
     
    +
    Example: rbox r y c G0.1 D2 | qvoronoi Fv + TO result
    +
    Compute the 2-d Voronoi diagram of a triangle and a small + square. Write a + summary to the console and the Voronoi ridges to + 'result'. Each ridge is the perpendicular bisector of a + pair of input sites. Vertex "0" indicates + unbounded ridges. Vertex "8" is the Voronoi + vertex for the square.
    + +
     
    +
    Example: rbox r y c G0.1 D2 | qvoronoi Fi
    +
    Print the bounded, separating hyperplanes for the 2-d Voronoi diagram of a + triangle and a small + square. Note the four hyperplanes (i.e., lines) for Voronoi vertex + "8". It is at the origin. +
    +
    +
    + +

    Qhull computes the Voronoi diagram via the Delaunay +triangulation. Each Voronoi +vertex is the circumcenter of a facet of the Delaunay +triangulation. Each Voronoi region corresponds to a vertex (i.e., input site) of the +Delaunay triangulation.

    + +

    Qhull outputs the Voronoi vertices for each Voronoi region. With +option 'Fv', +it lists all ridges of the Voronoi diagram with the corresponding +pairs of input sites. With +options 'Fi' and 'Fo', +it lists the bounded and unbounded separating hyperplanes. +You can also output a single Voronoi region +for further processing [see graphics].

    + +

    Use option 'Qz' if the input is circular, cospherical, or +nearly so. It improves precision by adding a point "at infinity," above the corresponding paraboloid. + +

    See Qhull FAQ - Delaunay and +Voronoi diagram questions.

    + +

    The 'qvonoroi' program is equivalent to +'qhull v Qbb' in 2-d to 3-d, and +'qhull v Qbb Qx' +in 4-d and higher. It disables the following Qhull +options: d n v Qbb QbB Qf Qg Qm +Qr QR Qv Qx Qz TR E V Fa FA FC FD FS Ft FV Gt Q0,etc. + +

    Copyright © 1995-2015 C.B. Barber

    + +

    Voronoi image by KOOK Architecture, Silvan Oesterle and Michael Knauss. + +


    +

    »qvoronoi synopsis

    + +
    +qvoronoi- compute the Voronoi diagram.
    +    input (stdin): dimension, number of points, point coordinates
    +    comments start with a non-numeric character
    +
    +options (qh-voron.htm):
    +    Qu   - compute furthest-site Voronoi diagram
    +    Tv   - verify result: structure, convexity, and in-circle test
    +    .    - concise list of all options
    +    -    - one-line description of all options
    +
    +output options (subset):
    +    s    - summary of results (default)
    +    p    - Voronoi vertices
    +    o    - OFF file format (dim, Voronoi vertices, and Voronoi regions)
    +    FN   - count and Voronoi vertices for each Voronoi region
    +    Fv   - Voronoi diagram as Voronoi vertices between adjacent input sites
    +    Fi   - separating hyperplanes for bounded regions, 'Fo' for unbounded
    +    G    - Geomview output (2-d only)
    +    QVn  - Voronoi vertices for input point n, -n if not
    +    TO file- output results to file, may be enclosed in single quotes
    +
    +examples:
    +rbox c P0 D2 | qvoronoi s o         rbox c P0 D2 | qvoronoi Fi
    +rbox c P0 D2 | qvoronoi Fo          rbox c P0 D2 | qvoronoi Fv
    +rbox c P0 D2 | qvoronoi s Qu Fv     rbox c P0 D2 | qvoronoi Qu Fo
    +rbox c G1 d D2 | qvoronoi s p       rbox c P0 D2 | qvoronoi s Fv QV0
    +
    + +

    »qvoronoi input

    +
    +The input data on stdin consists of: +
      +
    • dimension +
    • number of points
    • +
    • point coordinates
    • +
    + +

    Use I/O redirection (e.g., qvoronoi < data.txt), a pipe (e.g., rbox 10 | qvoronoi), +or the 'TI' option (e.g., qvoronoi TI data.txt). + +

    For example, this is four cocircular points inside a square. Their Voronoi +diagram has nine vertices and eight regions. Notice the Voronoi vertex +at the origin, and the Voronoi vertices (on each axis) for the four +sides of the square. +

    +

    +rbox s 4 W0 c G1 D2 > data +
    +2 RBOX s 4 W0 c D2
    +8
    +-0.4941988586954018 -0.07594397977563715
    +-0.06448037284989526 0.4958248496365813
    +0.4911154367094632 0.09383830681375946
    +-0.348353580869097 -0.3586778257652367
    +    -1     -1
    +    -1      1
    +     1     -1
    +     1      1
    +
    + +

    qvoronoi s p < data +

    +
    +Voronoi diagram by the convex hull of 8 points in 3-d:
    +
    +  Number of Voronoi regions: 8
    +  Number of Voronoi vertices: 9
    +  Number of non-simplicial Voronoi vertices: 1
    +
    +Statistics for: RBOX s 4 W0 c D2 | QVORONOI s p
    +
    +  Number of points processed: 8
    +  Number of hyperplanes created: 18
    +  Number of facets in hull: 10
    +  Number of distance tests for qhull: 33
    +  Number of merged facets: 2
    +  Number of distance tests for merging: 102
    +  CPU seconds to compute hull (after input): 0.094
    +
    +2
    +9
    +4.217546450968612e-17 1.735507986399734
    +-8.402566836762659e-17 -1.364368854147395
    +0.3447488772716865 -0.6395484723719818
    +1.719446929853986 2.136555906154247e-17
    +0.4967882915039657 0.68662371396699
    +-1.729928876283549 1.343733067524222e-17
    +-0.8906163241424728 -0.4594150543829102
    +-0.6656840313875723 0.5003013793414868
    +-7.318364664277155e-19 -1.188217818408333e-16
    +
    +
    + +
    +

    » qvoronoi +outputs

    +
    + +

    These options control the output of Voronoi diagrams.

    +
    + +
    +
     
    +
    Voronoi vertices
    +
    p
    +
    print the coordinates of the Voronoi vertices. The first line + is the dimension. The second line is the number of vertices. Each + remaining line is a Voronoi vertex.
    +
    Fn
    +
    list the neighboring Voronoi vertices for each Voronoi + vertex. The first line is the number of Voronoi vertices. Each + remaining line starts with the number of neighboring vertices. + Negative vertices (e.g., -1) indicate vertices + outside of the Voronoi diagram. + In the circle-in-box example, the + Voronoi vertex at the origin has four neighbors.
    +
    FN
    +
    list the Voronoi vertices for each Voronoi region. The first line is + the number of Voronoi regions. Each remaining line starts with the + number of Voronoi vertices. Negative indices (e.g., -1) indicate vertices + outside of the Voronoi diagram. + In the circle-in-box example, the four bounded regions are defined by four + Voronoi vertices.
    + +
     
    +
     
    +
    Voronoi regions
    +
    o
    +
    print the Voronoi regions in OFF format. The first line is the + dimension. The second line is the number of vertices, the number + of input sites, and "1". The third line represents the vertex-at-infinity. + Its coordinates are "-10.101". The next lines are the coordinates + of the Voronoi vertices. Each remaining line starts with the number + of Voronoi vertices in a Voronoi region. In 2-d, the vertices are +listed in adjacency order (unoriented). In 3-d and higher, the +vertices are listed in numeric order. In the circle-in-square + example, each bounded region includes the Voronoi vertex at + the origin. Lines consisting of 0 indicate + coplanar input sites or 'Qz'.
    +
    Fi
    +
    print separating hyperplanes for inner, bounded Voronoi + regions. The first number is the number of separating + hyperplanes. Each remaining line starts with 3+dim. The + next two numbers are adjacent input sites. The next dim + numbers are the coefficients of the separating hyperplane. The + last number is its offset. Use 'Tv' to verify that the +hyperplanes are perpendicular bisectors. It will list relevant +statistics to stderr.
    +
    Fo
    +
    print separating hyperplanes for outer, unbounded Voronoi + regions. The first number is the number of separating + hyperplanes. Each remaining line starts with 3+dim. The + next two numbers are adjacent input sites on the convex hull. The + next dim + numbers are the coefficients of the separating hyperplane. The + last number is its offset. Use 'Tv' to verify that the +hyperplanes are perpendicular bisectors. It will list relevant +statistics to stderr,
    +
     
    +
     
    +
    Input sites
    +
    Fv
    +
    list ridges of Voronoi vertices for pairs of input sites. The + first line is the number of ridges. Each remaining line starts with + two plus the number of Voronoi vertices in the ridge. The next + two numbers are two adjacent input sites. The remaining numbers list + the Voronoi vertices. As with option 'o', a 0 indicates + the vertex-at-infinity + and an unbounded, separating hyperplane. + The perpendicular bisector (separating hyperplane) + of the input sites is a flat through these vertices. + In the circle-in-square example, the ridge for each edge of the square + is unbounded.
    +
    Fc
    +
    list coincident input sites for each Voronoi vertex. + The first line is the number of vertices. The remaining lines start with + the number of coincident sites and deleted vertices. Deleted vertices + indicate highly degenerate input (see'Fs'). + A coincident site is assigned to one Voronoi + vertex. Do not use 'QJ' with 'Fc'; the joggle will separate + coincident sites.
    +
    FP
    +
    print coincident input sites with distance to + nearest site (i.e., vertex). The first line is the + number of coincident sites. Each remaining line starts with the point ID of + an input site, followed by the point ID of a coincident point, its vertex, and distance. + Includes deleted vertices which + indicate highly degenerate input (see'Fs'). + Do not use 'QJ' with 'FP'; the joggle will separate + coincident sites.
    +
     
    +
     
    +
    General
    +
    s
    +
    print summary of the Voronoi diagram. Use 'Fs' for numeric data.
    +
    i
    +
    list input sites for each Delaunay region. Use option 'Pp' + to avoid the warning. The first line is the number of regions. The + remaining lines list the input sites for each region. The regions are + oriented. In the circle-in-square example, the cocircular region has four + edges. In 3-d and higher, report cospherical sites by adding extra points. +
    +
    G
    +
    Geomview output for 2-d Voronoi diagrams.
    +
    +
    +
    +

    » qvoronoi +controls

    +
    + +

    These options provide additional control:

    +
    + +
    +
    Qu
    +
    compute the furthest-site Voronoi diagram.
    +
    QVn
    +
    select Voronoi vertices for input site n
    +
    Qz
    +
    add a point above the paraboloid to reduce precision + errors. Use it for nearly cocircular/cospherical input + (e.g., 'rbox c | qvoronoi Qz').
    +
    Tv
    +
    verify result
    +
    TI file
    +
    input data from file. The filename may not use spaces or quotes.
    +
    TO file
    +
    output results to file. Use single quotes if the filename + contains spaces (e.g., TO 'file with spaces.txt'
    +
    TFn
    +
    report progress after constructing n facets
    +
    PDk:1
    +
    include upper and lower facets in the output. Set k + to the last dimension (e.g., 'PD2:1' for 2-d inputs).
    +
    f
    +
    facet dump. Print the data structure for each facet (i.e., + Voronoi vertex).
    +
    + +
    +
    +

    » qvoronoi +graphics

    +
    + +

    In 2-d, Geomview output ('G') +displays a Voronoi diagram with extra edges to close the +unbounded Voronoi regions. To view the unbounded rays, enclose +the input points in a square.

    + +

    You can also view individual Voronoi regions in 3-d. To +view the Voronoi region for site 3 in Geomview, execute

    + +
    +

    qvoronoi <data QV3 p | qconvex s G >output

    +
    + +

    The qvoronoi command returns the Voronoi vertices +for input site 3. The qconvex command computes their convex hull. +This is the Voronoi region for input site 3. Its +hyperplane normals (qconvex 'n') are the same as the separating hyperplanes +from options 'Fi' +and 'Fo' (up to roundoff error). + +

    See the Delaunay and Voronoi +examples for 2-d and 3-d examples. Turn off normalization (on +Geomview's 'obscure' menu) when comparing the Voronoi diagram +with the corresponding Delaunay triangulation.

    + +
    +

    »qvoronoi +notes

    +
    + +

    You can simplify the Voronoi diagram by enclosing the input +sites in a large square or cube. This is particularly recommended +for cocircular or cospherical input data.

    + +

    See Voronoi graphics for computing +the convex hull of a Voronoi region.

    + +

    Voronoi diagrams do not include facets that are +coplanar with the convex hull of the input sites. A facet is +coplanar if the last coefficient of its normal is +nearly zero (see qh_ZEROdelaunay). + +

    Unbounded regions can be confusing. For example, 'rbox c | +qvoronoi Qz o' produces the Voronoi regions for the vertices +of a cube centered at the origin. All regions are unbounded. The +output is

    + +
    +
    3
    +2 9 1
    +-10.101 -10.101 -10.101
    +     0      0      0
    +2 0 1
    +2 0 1
    +2 0 1
    +2 0 1
    +2 0 1
    +2 0 1
    +2 0 1
    +2 0 1
    +0
    +
    +
    + +

    The first line is the dimension. The second line is the number +of vertices and the number of regions. There is one region per +input point plus a region for the point-at-infinity added by +option 'Qz'. The next two lines +lists the Voronoi vertices. The first vertex is the infinity +vertex. It is indicate by the coordinates -10.101. The +second vertex is the origin. The next nine lines list the +regions. Each region lists two vertices -- the infinity vertex +and the origin. The last line is "0" because no region +is associated with the point-at-infinity. A "0" would +also be listed for nearly incident input sites.

    + +

    To use option 'Fv', add an +interior point. For example,

    + +
    +
    +rbox c P0 | qvoronoi Fv
    +20
    +5 0 7 1 3 5
    +5 0 3 1 4 5
    +5 0 5 1 2 3
    +5 0 1 1 2 4
    +5 0 6 2 3 6
    +5 0 2 2 4 6
    +5 0 4 4 5 6
    +5 0 8 5 3 6
    +5 1 2 0 2 4
    +5 1 3 0 1 4
    +5 1 5 0 1 2
    +5 2 4 0 4 6
    +5 2 6 0 2 6
    +5 3 4 0 4 5
    +5 3 7 0 1 5
    +5 4 8 0 6 5
    +5 5 6 0 2 3
    +5 5 7 0 1 3
    +5 6 8 0 6 3
    +5 7 8 0 3 5
    +
    +
    + +

    The output consists of 20 ridges and each ridge lists a pair +of input sites and a triplet of Voronoi vertices. The first eight +ridges connect the origin ('P0'). The remainder list the edges of +the cube. Each edge generates an unbounded ray through the +midpoint. The corresponding separating planes ('Fo') follow each +pair of coordinate axes.

    + +

    Options 'Qt' (triangulated output) +and 'QJ' (joggled input) are deprecated. They may produce +unexpected results. If you use these options, cocircular and cospherical input sites will +produce duplicate or nearly duplicate Voronoi vertices. See also Merged facets or joggled input.

    + +
    +

    »qvoronoi conventions

    +
    + +

    The following terminology is used for Voronoi diagrams in +Qhull. The underlying structure is a Delaunay triangulation from +a convex hull in one higher dimension. Facets of the Delaunay +triangulation correspond to vertices of the Voronoi diagram. +Vertices of the Delaunay triangulation correspond to input sites. +They also correspond to regions of the Voronoi diagram. See convex hull conventions, Delaunay conventions, and +Qhull's data structures.

    +
    + +
      +
    • input site - a point in the input (one dimension + lower than a point on the convex hull)
    • +
    • point - a point has d+1 coordinates. The + last coordinate is the sum of the squares of the input + site's coordinates
    • +
    • coplanar point - a nearly incident + input site
    • +
    • vertex - a point on the paraboloid. It + corresponds to a unique input site.
    • +
    • point-at-infinity - a point added above the + paraboloid by option 'Qz'
    • +
    • Delaunay facet - a lower facet of the + paraboloid. The last coefficient of its normal is + clearly negative.
    • +
    • Voronoi vertex - the circumcenter of a Delaunay + facet
    • +
    • Voronoi region - the Voronoi vertices for an + input site. The region of Euclidean space nearest to an + input site.
    • +
    • Voronoi diagram - the graph of the Voronoi + regions. It includes the ridges (i.e., edges) between the + regions.
    • +
    • vertex-at-infinity - the Voronoi vertex that + indicates unbounded Voronoi regions in 'o' output format. Its + coordinates are -10.101.
    • +
    • good facet - a Voronoi vertex with optional + restrictions by 'QVn', etc.
    • +
    + +
    +
    +

    »qvoronoi options

    + +
    +qvoronoi- compute the Voronoi diagram
    +    http://www.qhull.org
    +
    +input (stdin):
    +    first lines: dimension and number of points (or vice-versa).
    +    other lines: point coordinates, best if one point per line
    +    comments:    start with a non-numeric character
    +
    +options:
    +    Qu   - compute furthest-site Voronoi diagram
    +
    +Qhull control options:
    +    QJn  - randomly joggle input in range [-n,n]
    +    Qs   - search all points for the initial simplex
    +    Qz   - add point-at-infinity to Voronoi diagram
    +    QGn  - Voronoi vertices if visible from point n, -n if not
    +    QVn  - Voronoi vertices for input point n, -n if not
    +
    +Trace options:
    +    T4   - trace at level n, 4=all, 5=mem/gauss, -1= events
    +    Tc   - check frequently during execution
    +    Ts   - statistics
    +    Tv   - verify result: structure, convexity, and in-circle test
    +    Tz   - send all output to stdout
    +    TFn  - report summary when n or more facets created
    +    TI file - input data from file, no spaces or single quotes
    +    TO file - output results to file, may be enclosed in single quotes
    +    TPn  - turn on tracing when point n added to hull
    +     TMn - turn on tracing at merge n
    +     TWn - trace merge facets when width > n
    +    TVn  - stop qhull after adding point n, -n for before (see TCn)
    +     TCn - stop qhull after building cone for point n (see TVn)
    +
    +Precision options:
    +    Cn   - radius of centrum (roundoff added).  Merge facets if non-convex
    +     An  - cosine of maximum angle.  Merge facets if cosine > n or non-convex
    +           C-0 roundoff, A-0.99/C-0.01 pre-merge, A0.99/C0.01 post-merge
    +    Rn   - randomly perturb computations by a factor of [1-n,1+n]
    +    Wn   - min facet width for non-coincident point (before roundoff)
    +
    +Output formats (may be combined; if none, produces a summary to stdout):
    +    s    - summary to stderr
    +    p    - Voronoi vertices
    +    o    - OFF format (dim, Voronoi vertices, and Voronoi regions)
    +    i    - Delaunay regions (use 'Pp' to avoid warning)
    +    f    - facet dump
    +
    +More formats:
    +    Fc   - count plus coincident points (by Voronoi vertex)
    +    Fd   - use cdd format for input (homogeneous with offset first)
    +    FD   - use cdd format for output (offset first)
    +    FF   - facet dump without ridges
    +    Fi   - separating hyperplanes for bounded Voronoi regions
    +    FI   - ID for each Voronoi vertex
    +    Fm   - merge count for each Voronoi vertex (511 max)
    +    Fn   - count plus neighboring Voronoi vertices for each Voronoi vertex
    +    FN   - count and Voronoi vertices for each Voronoi region
    +    Fo   - separating hyperplanes for unbounded Voronoi regions
    +    FO   - options and precision constants
    +    FP   - nearest point and distance for each coincident point
    +    FQ   - command used for qvoronoi
    +    Fs   - summary: #int (8), dimension, #points, tot vertices, tot facets,
    +                    for output: #Voronoi regions, #Voronoi vertices,
    +                                #coincident points, #non-simplicial regions
    +                    #real (2), max outer plane and min vertex
    +    Fv   - Voronoi diagram as Voronoi vertices between adjacent input sites
    +    Fx   - extreme points of Delaunay triangulation (on convex hull)
    +
    +Geomview options (2-d only)
    +    Ga   - all points as dots
    +     Gp  -  coplanar points and vertices as radii
    +     Gv  -  vertices as spheres
    +    Gi   - inner planes only
    +     Gn  -  no planes
    +     Go  -  outer planes only
    +    Gc   - centrums
    +    Gh   - hyperplane intersections
    +    Gr   - ridges
    +    GDn  - drop dimension n in 3-d and 4-d output
    +
    +Print options:
    +    PAn  - keep n largest Voronoi vertices by 'area'
    +    Pdk:n - drop facet if normal[k] <= n (default 0.0)
    +    PDk:n - drop facet if normal[k] >= n
    +    Pg   - print good Voronoi vertices (needs 'QGn' or 'QVn')
    +    PFn  - keep Voronoi vertices whose 'area' is at least n
    +    PG   - print neighbors of good Voronoi vertices
    +    PMn  - keep n Voronoi vertices with most merges
    +    Po   - force output.  If error, output neighborhood of facet
    +    Pp   - do not report precision problems
    +
    +    .    - list of all options
    +    -    - one line descriptions of all options
    +
    + + +
    + +

    Up: Home page for Qhull
    +Up: Qhull manual: Table of Contents
    +To: Programs +• Options +• Output +• Formats +• Geomview +• Print +• Qhull +• Precision +• Trace +• Functions
    +To: synopsis +• input • outputs +• controls • graphics +• notes • conventions +• options + +


    + +

    The Geometry Center +Home Page

    + +

    Comments to: qhull@qhull.org +
    +Created: Sept. 25, 1995 --- Last modified: see top

    + + diff --git a/xs/src/qhull/html/rbox.htm b/xs/src/qhull/html/rbox.htm new file mode 100644 index 000000000..9c28face5 --- /dev/null +++ b/xs/src/qhull/html/rbox.htm @@ -0,0 +1,277 @@ + + + + +rbox -- generate point distributions + + + + +

    Up: Home page for Qhull
    +Up: Qhull manual: Table of Contents
    +To: Programs +• Options +• Output +• Formats +• Geomview +• Print +• Qhull +• Precision +• Trace +• Functions
    +To: synopsis • outputs +• examples • notes +• options +


    + +

    [CONE]rbox -- generate point distributions

    + +
    + rbox generates random or regular points according to the + options given, and outputs the points to stdout. The + points are generated in a cube, unless 's', 'x', or 'y' + are given. + +
    +

    »rbox synopsis

    +
    +rbox- generate various point distributions.  Default is random in cube.
    +
    +args (any order, space separated):
    +  3000    number of random points in cube, lens, spiral, sphere or grid
    +  D3      dimension 3-d
    +  c       add a unit cube to the output ('c G2.0' sets size)
    +  d       add a unit diamond to the output ('d G2.0' sets size)
    +  l       generate a regular 3-d spiral
    +  r       generate a regular polygon, ('r s Z1 G0.1' makes a cone)
    +  s       generate cospherical points
    +  x       generate random points in simplex, may use 'r' or 'Wn'
    +  y       same as 'x', plus simplex
    +  Cn,r,m  add n nearly coincident points within radius r of m points
    +  Pn,m,r  add point [n,m,r] first, pads with 0
    +
    +  Ln      lens distribution of radius n.  Also 's', 'r', 'G', 'W'.
    +  Mn,m,r  lattice (Mesh) rotated by [n,-m,0], [m,n,0], [0,0,r], ...
    +          '27 M1,0,1' is {0,1,2} x {0,1,2} x {0,1,2}.  Try 'M3,4 z'.
    +  W0.1    random distribution within 0.1 of the cube's or sphere's surface
    +  Z0.5 s  random points in a 0.5 disk projected to a sphere
    +  Z0.5 s G0.6 same as Z0.5 within a 0.6 gap
    +
    +  Bn      bounding box coordinates, default 0.5
    +  h       output as homogeneous coordinates for cdd
    +  n       remove command line from the first line of output
    +  On      offset coordinates by n
    +  t       use time as the random number seed (default is command line)
    +  tn      use n as the random number seed
    +  z       print integer coordinates, default 'Bn' is 1e+06
    +
    + +

    »rbox outputs

    +
    + +The format of the output is the following: first line contains + the dimension and a comment, second line contains the + number of points, and the following lines contain the points, + one point per line. Points are represented by their coordinate values. + +

    For example, rbox c 10 D2 generates +

    +
    +2 RBOX c 10 D2
    +14
    +-0.4999921736307369 -0.3684622117955817
    +0.2556053225468894 -0.0413498678629751
    +0.0327672376602583 -0.2810408135699488
    +-0.452955383763607 0.17886471718444
    +0.1792964061529342 0.4346928963760779
    +-0.1164979223315585 0.01941637230982666
    +0.3309653464993139 -0.4654278894564396
    +-0.4465383649305798 0.02970019358182344
    +0.1711493843897706 -0.4923018137852678
    +-0.1165843490665633 -0.433157762450313
    +  -0.5   -0.5
    +  -0.5    0.5
    +   0.5   -0.5
    +   0.5    0.5
    +
    + +
    + +
    +

    »rbox examples

    + +
    +       rbox 10
    +              10 random points in the unit cube centered  at  the
    +              origin.
    +
    +       rbox 10 s D2
    +              10 random points on a 2-d circle.
    +
    +       rbox 100 W0
    +              100 random points on the surface of a cube.
    +
    +       rbox 1000 s D4
    +              1000 random points on a 4-d sphere.
    +
    +       rbox c D5 O0.5
    +              a 5-d hypercube with one corner at the origin.
    +
    +       rbox d D10
    +              a 10-d diamond.
    +
    +       rbox x 1000 r W0
    +              100 random points on the surface of a fixed simplex
    +
    +       rbox y D12
    +              a 12-d simplex.
    +
    +       rbox l 10
    +              10 random points along a spiral
    +
    +       rbox l 10 r
    +              10 regular points  along  a  spiral  plus  two  end
    +              points
    +
    +       rbox 1000 L10000 D4 s
    +              1000 random points on the surface of a narrow lens.
    +
    +           rbox 1000 L100000 s G1e-6
    +                  1000 random points near the edge of a narrow lens
    +
    +       rbox c G2 d G3
    +              a cube with coordinates +2/-2 and  a  diamond  with
    +              coordinates +3/-3.
    +
    +       rbox 64 M3,4 z
    +              a  rotated,  {0,1,2,3} x {0,1,2,3} x {0,1,2,3} lat-
    +              tice (Mesh) of integer points.
    +
    +       rbox P0 P0 P0 P0 P0
    +              5 copies of the origin in 3-d.  Try 'rbox P0 P0  P0
    +              P0 P0 | qhull QJ'.
    +
    +       r 100 s Z1 G0.1
    +              two  cospherical  100-gons plus another cospherical
    +              point.
    +
    +       100 s Z1
    +              a cone of points.
    +
    +       100 s Z1e-7
    +              a narrow cone of points with many precision errors.
    +
    + +

    »rbox notes

    +
    +Some combinations of arguments generate odd results. + +
    +

    »rbox options

    + +
    +       n      number of points
    +
    +       Dn     dimension n-d (default 3-d)
    +
    +       Bn     bounding box coordinates (default 0.5)
    +
    +       l      spiral distribution, available only in 3-d
    +
    +       Ln     lens  distribution  of  radius n.  May be used with
    +              's', 'r', 'G', and 'W'.
    +
    +       Mn,m,r lattice  (Mesh)  rotated  by  {[n,-m,0],   [m,n,0],
    +              [0,0,r],  ...}.   Use  'Mm,n'  for a rigid rotation
    +              with r = sqrt(n^2+m^2).  'M1,0'  is  an  orthogonal
    +              lattice.   For  example,  '27  M1,0'  is  {0,1,2} x
    +              {0,1,2} x {0,1,2}.
    +
    +       s      cospherical points randomly generated in a cube and
    +              projected to the unit sphere
    +
    +       x      simplicial  distribution.   It  is fixed for option
    +              'r'.  May be used with 'W'.
    +
    +       y      simplicial distribution plus a simplex.   Both  'x'
    +              and 'y' generate the same points.
    +
    +       Wn     restrict  points  to distance n of the surface of a
    +              sphere or a cube
    +
    +       c      add a unit cube to the output
    +
    +       c Gm   add a cube with all combinations of +m  and  -m  to
    +              the output
    +
    +       d      add a unit diamond to the output.
    +
    +       d Gm   add a diamond made of 0, +m and -m to the output
    +
    +       Cn,r,m add n nearly coincident points within radius r of m points
    +
    +       Pn,m,r add point [n,m,r] to the output first.  Pad coordi-
    +              nates with 0.0.
    +
    +       n      Remove the command line from the first line of out-
    +              put.
    +
    +       On     offset the data by adding n to each coordinate.
    +
    +       t      use  time  in  seconds  as  the  random number seed
    +              (default is command line).
    +
    +       tn     set the random number seed to n.
    +
    +       z      generate integer coordinates.  Use 'Bn'  to  change
    +              the  range.   The  default  is 'B1e6' for six-digit
    +              coordinates.  In R^4, seven-digit coordinates  will
    +              overflow hyperplane normalization.
    +
    +       Zn s   restrict points to a disk about the z+ axis and the
    +              sphere (default Z1.0).  Includes the opposite pole.
    +              'Z1e-6'  generates  degenerate  points under single
    +              precision.
    +
    +       Zn Gm s
    +              same as Zn with an empty center (default G0.5).
    +
    +       r s D2 generate a regular polygon
    +
    +       r s Z1 G0.1
    +              generate a regular cone
    +
    + + +
    + +

    Up: Home page for Qhull
    +Up: Qhull manual: Table of Contents
    +To: Programs +• Options +• Output +• Formats +• Geomview +• Print +• Qhull +• Precision +• Trace +• Functions
    +To: synopsis • outputs +• examples • notes +• options + +


    + +

    The Geometry Center +Home Page

    + +

    Comments to: qhull@qhull.org +
    +Created: Sept. 25, 1995 --- Last modified: August 12, 1998

    + + diff --git a/xs/src/qhull/html/rbox.man b/xs/src/qhull/html/rbox.man new file mode 100644 index 000000000..3ea6395e6 --- /dev/null +++ b/xs/src/qhull/html/rbox.man @@ -0,0 +1,176 @@ +.\" This is the Unix manual page for rbox, written in nroff, the standard +.\" manual formatter for Unix systems. To format it, type +.\" +.\" nroff -man rbox.man +.\" +.\" This will print a formatted copy to standard output. If you want +.\" to ensure that the output is plain ascii, free of any control +.\" characters that nroff uses for underlining etc, pipe the output +.\" through "col -b": +.\" +.\" nroff -man rbox.man | col -b +.\" +.TH rbox 1 "August 10, 1998" "Geometry Center" +.SH NAME +rbox \- generate point distributions for qhull +.SH SYNOPSIS +Command "rbox" (w/o arguments) lists the options. +.SH DESCRIPTION +.PP +rbox generates random or regular points according to the options given, and +outputs +the points to stdout. The points are generated in a cube, unless 's' or 'k' +option is +given. The format of the output is the following: first line +contains the dimension and a comment, +second line contains the number of points, and the +following lines contain the points, one point per line. Points are represented +by their coordinate values. +.SH EXAMPLES +.TP +rbox 10 +10 random points in the unit cube centered at the origin. +.TP +rbox 10 s D2 +10 random points on a 2\[hy]d circle. +.TP +rbox 100 W0 +100 random points on the surface of a cube. +.TP +rbox 1000 s D4 +1000 random points on a 4\[hy]d sphere. +.TP +rbox c D5 O0.5 +a 5\[hy]d hypercube with one corner at the origin. +.TP +rbox d D10 +a 10\[hy]d diamond. +.TP +rbox x 1000 r W0 +100 random points on the surface of a fixed simplex +.TP +rbox y D12 +a 12\[hy]d simplex. +.TP +rbox l 10 +10 random points along a spiral +.TP +rbox l 10 r +10 regular points along a spiral plus two end points +.TP +rbox 1000 L10000 D4 s +1000 random points on the surface of a narrow lens. +.TP +rbox c G2 d G3 +a cube with coordinates +2/\-2 and a diamond with coordinates +3/\-3. +.TP +rbox 64 M3,4 z +a rotated, {0,1,2,3} x {0,1,2,3} x {0,1,2,3} lattice (Mesh) of integer +points. 'rbox 64 M1,0' is orthogonal. +.TP +rbox P0 P0 P0 P0 P0 +5 copies of the origin in 3\-d. Try 'rbox P0 P0 P0 P0 P0 | qhull QJ'. +.TP +r 100 s Z1 G0.1 +two cospherical 100\-gons plus another cospherical point. +.TP +100 s Z1 +a cone of points. +.TP +100 s Z1e\-7 +a narrow cone of points with many precision errors. +.SH OPTIONS +.TP +n +number of points +.TP +Dn +dimension n\[hy]d (default 3\[hy]d) +.TP +Bn +bounding box coordinates (default 0.5) +.TP +l +spiral distribution, available only in 3\[hy]d +.TP +Ln +lens distribution of radius n. May be used with 's', 'r', 'G', and 'W'. +.TP +Mn,m,r +lattice (Mesh) rotated by {[n,\-m,0], [m,n,0], [0,0,r], ...}. +Use 'Mm,n' for a rigid rotation with r = sqrt(n^2+m^2). 'M1,0' is an +orthogonal lattice. For example, '27 M1,0' is {0,1,2} x {0,1,2} x +{0,1,2}. '27 M3,4 z' is a rotated integer lattice. +.TP +s +cospherical points randomly generated in a cube and projected to the unit sphere +.TP +x +simplicial distribution. It is fixed for option 'r'. May be used with 'W'. +.TP +y +simplicial distribution plus a simplex. Both 'x' and 'y' generate the same points. +.TP +Wn +restrict points to distance n of the surface of a sphere or a cube +.TP +c +add a unit cube to the output +.TP +c Gm +add a cube with all combinations of +m and \-m to the output +.TP +d +add a unit diamond to the output. +.TP +d Gm +add a diamond made of 0, +m and \-m to the output +.TP +Cn,r,m +add n nearly coincident points within radius r of m points +.TP +Pn,m,r +add point [n,m,r] to the output first. Pad coordinates with 0.0. +.TP +n +Remove the command line from the first line of output. +.TP +On +offset the data by adding n to each coordinate. +.TP +t +use time in seconds as the random number seed (default is command line). +.TP +tn +set the random number seed to n. +.TP +z +generate integer coordinates. Use 'Bn' to change the range. +The default is 'B1e6' for six\[hy]digit coordinates. In R^4, seven\[hy]digit +coordinates will overflow hyperplane normalization. +.TP +Zn s +restrict points to a disk about the z+ axis and the sphere (default Z1.0). +Includes the opposite pole. 'Z1e\-6' generates degenerate points under +single precision. +.TP +Zn Gm s +same as Zn with an empty center (default G0.5). +.TP +r s D2 +generate a regular polygon +.TP +r s Z1 G0.1 +generate a regular cone +.SH BUGS +Some combinations of arguments generate odd results. + +Report bugs to qhull_bug@qhull.org, other correspondence to qhull@qhull.org +.SH SEE ALSO +qhull(1) +.SH AUTHOR +.nf +C. Bradford Barber +bradb@shore.net +.fi + diff --git a/xs/src/qhull/html/rbox.txt b/xs/src/qhull/html/rbox.txt new file mode 100644 index 000000000..e3cf72189 --- /dev/null +++ b/xs/src/qhull/html/rbox.txt @@ -0,0 +1,195 @@ + + + +rbox(1) rbox(1) + + +NAME + rbox - generate point distributions for qhull + +SYNOPSIS + Command "rbox" (w/o arguments) lists the options. + +DESCRIPTION + rbox generates random or regular points according to the + options given, and outputs the points to stdout. The + points are generated in a cube, unless 's' or given. The + format of the output is the following: first line contains + the dimension and a comment, second line contains the num- + ber of points, and the following lines contain the points, + one point per line. Points are represented by their coor- + dinate values. + +EXAMPLES + rbox 10 + 10 random points in the unit cube centered at the + origin. + + rbox 10 s D2 + 10 random points on a 2-d circle. + + rbox 100 W0 + 100 random points on the surface of a cube. + + rbox 1000 s D4 + 1000 random points on a 4-d sphere. + + rbox c D5 O0.5 + a 5-d hypercube with one corner at the origin. + + rbox d D10 + a 10-d diamond. + + rbox x 1000 r W0 + 100 random points on the surface of a fixed simplex + + rbox y D12 + a 12-d simplex. + + rbox l 10 + 10 random points along a spiral + + rbox l 10 r + 10 regular points along a spiral plus two end + points + + rbox 1000 L10000 D4 s + 1000 random points on the surface of a narrow lens. + + rbox c G2 d G3 + a cube with coordinates +2/-2 and a diamond with + + + +Geometry Center August 10, 1998 1 + + + + + +rbox(1) rbox(1) + + + coordinates +3/-3. + + rbox 64 M3,4 z + a rotated, {0,1,2,3} x {0,1,2,3} x {0,1,2,3} lat- + tice (Mesh) of integer points. + + rbox P0 P0 P0 P0 P0 + 5 copies of the origin in 3-d. Try 'rbox P0 P0 P0 + P0 P0 | qhull QJ'. + + r 100 s Z1 G0.1 + two cospherical 100-gons plus another cospherical + point. + + 100 s Z1 + a cone of points. + + 100 s Z1e-7 + a narrow cone of points with many precision errors. + +OPTIONS + n number of points + + Dn dimension n-d (default 3-d) + + Bn bounding box coordinates (default 0.5) + + l spiral distribution, available only in 3-d + + Ln lens distribution of radius n. May be used with + 's', 'r', 'G', and 'W'. + + Mn,m,r lattice (Mesh) rotated by {[n,-m,0], [m,n,0], + [0,0,r], ...}. Use 'Mm,n' for a rigid rotation + with r = sqrt(n^2+m^2). 'M1,0' is an orthogonal + lattice. For example, '27 M1,0' is {0,1,2} x + {0,1,2} x {0,1,2}. + + s cospherical points randomly generated in a cube and + projected to the unit sphere + + x simplicial distribution. It is fixed for option + 'r'. May be used with 'W'. + + y simplicial distribution plus a simplex. Both 'x' + and 'y' generate the same points. + + Wn restrict points to distance n of the surface of a + sphere or a cube + + c add a unit cube to the output + + c Gm add a cube with all combinations of +m and -m to + the output + + + +Geometry Center August 10, 1998 2 + + + + + +rbox(1) rbox(1) + + + d add a unit diamond to the output. + + d Gm add a diamond made of 0, +m and -m to the output + + Cn,r,m add n nearly coincident points within radius r of m points + + Pn,m,r add point [n,m,r] to the output first. Pad coordi- + nates with 0.0. + + n Remove the command line from the first line of out- + put. + + On offset the data by adding n to each coordinate. + + t use time in seconds as the random number seed + (default is command line). + + tn set the random number seed to n. + + z generate integer coordinates. Use 'Bn' to change + the range. The default is 'B1e6' for six-digit + coordinates. In R^4, seven-digit coordinates will + overflow hyperplane normalization. + + Zn s restrict points to a disk about the z+ axis and the + sphere (default Z1.0). Includes the opposite pole. + 'Z1e-6' generates degenerate points under single + precision. + + Zn Gm s + same as Zn with an empty center (default G0.5). + + r s D2 generate a regular polygon + + r s Z1 G0.1 + generate a regular cone + +BUGS + Some combinations of arguments generate odd results. + + Report bugs to qhull_bug@qhull.org, other correspon- + dence to qhull@qhull.org + +SEE ALSO + qhull(1) + +AUTHOR + C. Bradford Barber + bradb@shore.net + + + + + +Geometry Center August 10, 1998 3 + + diff --git a/xs/src/qhull/index.htm b/xs/src/qhull/index.htm new file mode 100644 index 000000000..4ea7806c9 --- /dev/null +++ b/xs/src/qhull/index.htm @@ -0,0 +1,284 @@ + + + + +Qhull code for Convex Hull, Delaunay Triangulation, Voronoi Diagram, and Halfspace Intersection about a Point + + + + +URL: http://www.qhull.org +
    To: +News +• Download +• CiteSeer +• Images +• Manual +• FAQ +• Programs +• Options +

    + +
    + + +
    +

    Qhull

    + [CONE] +
    +Qhull computes the convex hull, Delaunay triangulation, Voronoi diagram, +halfspace intersection about a point, furthest-site Delaunay +triangulation, and furthest-site Voronoi diagram. The source code runs in +2-d, 3-d, 4-d, and higher dimensions. Qhull implements the Quickhull +algorithm for computing the convex hull. It handles roundoff +errors from floating point arithmetic. It computes volumes, +surface areas, and approximations to the convex hull.

    + + +

    Qhull does not support triangulation of non-convex surfaces, mesh +generation of non-convex objects, medium-sized inputs in 9-D +and higher, alpha shapes, weighted Voronoi diagrams, Voronoi volumes, or +constrained Delaunay triangulations,

    + +

    Qhull 2015.2 introduces reentrant Qhull. It allows concurrent Qhull runs and simplifies the C++ interface to Qhull. +If you call Qhull from your program, you should use reentrant Qhull (libqhull_r) instead of qh_QHpointer (libqhull). +If you use Qhull 2003.1. please upgrade or apply poly.c-qh_gethash.patch. +

    +
    + +
    +
    + + + +
    + +

    Introduction +

      +
    • Fukuda's introduction to convex hulls, Delaunay + triangulations, Voronoi diagrams, and linear programming
    • +
    • Lambert's Java visualization of convex hull algorithms
    • +
    • LEDA Guide to geometry algorithms +
    • MathWorld's Computational Geometry from Wolfram Research +
    • Skiena's Computational Geometry from his Algorithm Design Manual. +
    • Stony Brook Algorithm Repository, computational geometry
    • +
    + +

    Qhull Documentation and Support +

    + +

    Related URLs +

    + +

    FAQs and Newsgroups +

    + +
    + +

    The program includes options for input transformations, +randomization, tracing, multiple output formats, and execution +statistics. The program can be called from within your +application.

    + +

    You can view the results in 2-d, 3-d and 4-d with Geomview. An alternative +is VTK.

    + +

    For an article about Qhull, download from + ACM or CiteSeer: +

    + +
    +

    Barber, C.B., Dobkin, D.P., and Huhdanpaa, H.T., "The + Quickhull algorithm for convex hulls," ACM Trans. on + Mathematical Software, 22(4):469-483, Dec 1996, http://www.qhull.org

    +
    + +

    Abstract:

    + +
    +

    The convex hull of a set of points is the smallest convex + set that contains the points. This article presents a + practical convex hull algorithm that combines the + two-dimensional Quickhull Algorithm with the general + dimension Beneath-Beyond Algorithm. It is similar to the + randomized, incremental algorithms for convex hull and + Delaunay triangulation. We provide empirical evidence that + the algorithm runs faster when the input contains non-extreme + points, and that it uses less memory.

    +

    Computational geometry algorithms have traditionally + assumed that input sets are well behaved. When an algorithm + is implemented with floating point arithmetic, this + assumption can lead to serious errors. We briefly describe a + solution to this problem when computing the convex hull in + two, three, or four dimensions. The output is a set of + "thick" facets that contain all possible exact convex hulls + of the input. A variation is effective in five or more + dimensions.

    +
    + +
    + +

    Up: Past Software +Projects of the Geometry Center
    +URL: http://www.qhull.org +
    To: +News +• Download +• CiteSeer +• Images +• Manual +• FAQ +• Programs +• Options +

    + +
    + +

    [HOME] The Geometry Center Home Page

    + +

    Comments to: qhull@qhull.org +
    +Created: May 17 1995 --- + + diff --git a/xs/src/qhull/origCMakeLists.txt b/xs/src/qhull/origCMakeLists.txt new file mode 100644 index 000000000..1034d1dea --- /dev/null +++ b/xs/src/qhull/origCMakeLists.txt @@ -0,0 +1,426 @@ +# CMakeLists.txt -- CMake configuration file for qhull, qhull6, and related programs +# +# To install CMake +# Download from http://www.cmake.org/download/ +# +# To find the available targets for CMake -G "..." +# cmake --help +# +# To build with MSYS/mingw +# cd build && cmake -G "MSYS Makefiles" .. && cmake .. +# make +# make install +# +# To uninstall on unix or MSYS/mingw +# xargs rm [B. Boeckel] + - Moved include file for each C++ source file to the top of the includes + - Prepend cpp includes with "libqhullcpp/" + - RoadLogEvent includes RoadLogEvent.h + - QhullIterator.h: Only QHULL_DECLARE_SEQUENTIAL_ITERATOR is used. + + - Compared src/libqhull/* to src/libqhull_r/* and resolved differences + - qh_printpoint in io.c skips qh_IDnone like io_r.c + - qhull_p-exports.def: Added three missing exports + - set_r.h: Removed countT. Too many issues + + - libqhull_r/Makefile: Add help prompts to 'make qtest' + - libqhull.pro: Add '../libqhull/' to sources and headers + - libqhull/Makefile: Fixed -I,./,,/src + + - qhull-zip.sh: Add CMakeModules to tarball [C. Rosenvik] + - CMakeLists.txt: Add targets qhullp and user_egp for qh_QHpointer and libqhull_p + - Reorganized 'make help' + - Makefile cleanall: Delete testqset and qhulltest from bin/ + - Fix filetype of Unix-only files + - Fix Unix line endings for Makefile and check in qhull-zip.sh + - Fix Windows line-endings and check in qhull-zip.sh + - qhull-zip.sh: Check for Unix text files + + ------------ +Qhull 2015.1 2016/01/03 (7.1.0) + - Add Rbox option 'Cn,r,m' to add nearly coincident points. Trigger for duplicate ridges + - Add Qhull option 'Q12' to ignore error on wide merge due to duplicate ridge + + - qh_findbestlower: Call qh_findfacet_all to fix rare "flipped or upper Delaunay" error QH6228. + QH6228 input provided by J. Metz. Reported (date order): L. Fiaschi, N. Bowler, A. Liebscher, V. Vieira, N. Rhinehart, N. Vance, P. Shafer + - qh_check_dupridge: Check if wide merge due to duplicate ridge from nearly coincident points + - qh_initialhull: Fix error messages for initial simplex is flat + - qh_determinant: increased 2-d and 3-d nearzero by 10x due to a counter-example + - rbox: Add qh_outcoord() to output coordinates w/ or w/o iscdd + - qh_meminit (mem.c): Add call to qh_memcheck + - Compare libqhull/... to libqhull_r/... and resolve differences + - Update builds for DevStudio (qhull.sln for msdev 2005..2009, qhull-32.sln and qhull-64.sln for recent releases) + + - qh-impre.htm: Add a section about precision errors for 'Nearly coincident points on an edge' + - html/index.htm#geomview: Document how to install, build, and use Geomview. + - html/index.htm: Emphasize program links and move related urls to end + - qhull/index.htm: Emphasize manual, geomview, and imprecision + - Fix documentation links in libqhull_r/index.htm + - Add 'Functions' link to documentation headers + - Change '...' to '...' + - libqhull_r/index.htm -- Add instructions for configuring web browsers for source links. + - libqhull_r/ -- Fix source links for ..._r.htm files + +------------ +Qhull 2015.0.7 2015/11/09 (7.0.7) + - Fix return type of operator-> in QhullLinkedList and other collection classes [F. Jares] + - Fix return types for QhullLinkedList + - Fix return types for QhullPoints + - Simplify return type for Coordinates::operator[] (same as QList) + - Add const to operators for QhullSet::iterator and add documentation + - Coordinates.h: Fix return types for operations of iterator and const_iterator + - Drop use of Perforce changelist number in qhull_VERSION of CMakeLists.txt + - Rename the md5sum files as *.tgz.md5sum instead of *-tgz.md5sum + - Fix build dependency for testqset_r [asekez] + - rbox.c depends on Qhull due to qh_lib_check which uses qh_version2 for error messages + - QhullFacet_test.cpp: Annotate Qhull invocations. Allows their repetition. + - QhullFacet_test.cpp: Adjust epsilon on distance tests + - Do not create libqhullcpp as a shared library. Qhull C++ classes may change layout and size. + - qhull-cpp.xml: Make a relative path to road-faq.xsl + +------------ +Qhull 2015.0.6 2015/10/20 (7.0.6.2013) + - In the libraries, exit() is only called from qh_exit(). qh_exit may be redefined. + - Add qh_fprintf_stderr to usermem.c. May be overridden to avoid use of stderr [D. Sterratt] + Add usermem to testqset builds + Used by qh_fprintf_rbox + - Remove most instances of stderr/stdout from libqhull, libqhull_r, and libqhullcpp [D. Sterratt] + qh_fprintf_stderr may be redefined. qh_meminit and qh_new_qhull use stderr as the default ferr + - qh_initflags: Use qh.fout instead of stdout for 'TO file'. A library caller may define a different qh.fout. + - qh_settemppush: Call qh_fprintf() instead of fprintf() on error. + - Rename qh_call_qhull as "Qhull-template" from user.c. Updated its references. + + - qh-code.htm: "How to avoid exit(), fprintf(), stderr, and stdout" + - html/index.htm: Fix table of contents for qh-code + - libqhull_r/index.htm: Rewrite introduction to Reentrant Qhull + - qh-faq.htm: Rewrite "Can Qhull use coordinates without placing them in a data file?" + - qh-get.html: Link to github + - Remove qhull_interface.cpp from the documentation + +------------ +Qhull 2015.0.5 2015/10/12 (7.0.5.1995) +- qh_new_qhull: default 'errfile' is 'stderr'. outfile and errfile are optional [B. Pearlmutter] +- qh_new_qhull: returns qh_ERRinput instead of exit() if qhull_cmd is not "qhull ..." [B. Pearlmutter] +- qhalf_r.c,etc: Add clear of qh.NOerrexit +- global.c: gcc 4.4.0 mingw32 segfault cleared by adding comment +- usermem_r-cpp.cpp: Optional file to redefine qh_exit() as throw "QH10003.." [B. Pearlmutter] + qh_exit() is called by qhull_r when qh_errexit() is not available. + +- html/index.htm: Add bibliographic reference to Golub & van Loan and other references [R. Gaul] +- qhalf.htm: A halfspace is the points on or below a hyperplane [D. Strawn] +- qh-opto.htm#n: Defined inside, outside, on, above, and below a hyperplane [D. Strawn] +- qhalf.htm#notes: Recast the linear program using negative halfspaces (as used by Qhull) [D. Strawn] +- qhull_a.h: Fix comment '#include "libqhull/qhull_a.h" [fe rew] + +- build/qhull*.pc.in: Templates for pkg-config (derived from Fedorra) [P. McMunn] + https://bitbucket.org/mgorny/pkg-config-spec/src/c1bf12afe0df6d95f2fe3f5e1ffb4c50f018825d/pkg-config-spec.txt?at=master&fileviewer=file-view-default +- Makefile: Remove user_eg3.o from LIBQHULLCPP_OBJS +- Makefile: Add .h dependencies for unix_r.o, etc. +- libqhull/Makefile: Fix build of rbox +- libqhull_r/Makefile: Fix build -I +- qhull.sln/user_eg3: Add dependency on libcpp +- Removed bin/libqhull_r.dll (should be qhull_r.dll) +- Removed build/qhulltest.vcproj (see build/qhulltest/qhulltest.vcproj) + +------------ +Qhull 2015.0.4 2015/9/30 (7.0.4.1984) + - qh-get.htm: Unix tarball includes version number (e.g., qhull-2015-src-7.1.0.1940.tgz) [Hauptman] + - qglobal.c: Add qh_version2 with Unix version for "-V" option [Hauptman] + - build/qhull-32.sln, *-32.vcxproj: Add Visual Studio 32-bit build for 2010+ + - build/qhull-64.sln, *-64.vcxproj: Add Visual Studio 64-bit build for 2010+ [G. Lodron] + - make-vcproj.sh: Restore to eg/... It is required for Visual Studio builds + - README.txt: updated builds and reentrant Qhull + - Add documentation for QHULL_LIB_CHECK + - qh_lib_check: Check for unknown QHULL_LIB_TYPE + - qh-code.htm: Add memory requirements for 32- and 64-bit + +------------ +Qhull 2015.0.3 2015/9/22 + - qh_mem, qh_merge: Log before 'delete' instead of afterwards [Coverity, K. Schwehr] + - qh_merge: Test for NULL horizon in qh_checkzero [Coverity, K. Schwehr] + - qh_matchneighbor: Check for matchfacet not a neighbor of facet [Coverity, K. Schwehr] + - qh_triangulate: Explicit check for visible==NULL [Coverity, K. Schwehr] + - qh_findbestfacet (unused by qhull): Fix test of isoutside [Coverity, K. Schwehr] + - qh_check_maxout: Check bestfacet!=0 for logging its id [Coverity, K. Schwehr] + - qh_nearvertex: Check for bestvertex not found [Coverity, K. Schwehr] + - qh_checkfacet: Check for missing neighbors of simplicial facets [Coverity, K. Schwehr] + - qh_setdelnth: Check 'nth' before using it [Coverity, K. Schwehr] + - Annotate code for Coverity warnings (most of these protected by qh_errexit) [K. Schwehr] + + - qh_printfacet3math: explicit format string (duplicates change to io.c) [B. Pearlmutter] + - libqhull_r.h: fix spelling error (duplicates change to libqhull.h) [B. Pearlmutter] + - unix_r.c: fix spelling error (duplicates change to unix.c) [B. Pearlmutter] + - qhull_a.h: define qhullUnused() only if defined(__cplusplus) [R. Stogner] + - qh_version: Use const char str[]= "string" instead of const char * str= "string" [U. Drepper, p. 27] + - qh_newvertex: Use UINT_MAX instead of 0xFFFFFFFF + - qh_newridge: Use UINT_MAX instead of 0xFFFFFFFF + - Reviewed FIXUP notes + + - QhullRidge_test: t_foreach use 'foreach(const QhullVertex &v, vertices) + - Made '#include "RoadTest.h" consistent across all C++ tests + + - qh-code.htm: May also use libqhull_r (e.g., FOREACHfacet_(...)) + - qh-get.htm: Add list of download build repositories + + - Add CMakeModules/CheckLFS.cmake: Enables Large File Support [B. Pearlmutter] + - Makefile: Use -fpic at all times instead of -fPIC, [U. Drepper p. 15] + +------------ +Qhull 2015.0.2 2015/9/1 + - global_r.c: Fixed spelling of /* duplicated in...qh_clear_outputflags */ [K. Schwehr] + - Replaced Gitorious with GitHub + - Moved 'to do' comments into Changes.txt + +------------ +Qhull 2015.0.1 2015/8/31 + + Source code changes + - Increased size of vertexT.id and ridgeT.id to 2^32 [H. Strandenes, C. Carson, K. Nguyen] + Reworded the warning message for ridgeT.id overflow. It does not affect Qhull output + - Add qh_lib_check to check for a compatible Qhull library. + Programs should call QHULL_LIB_CHECK before calling Qhull. + - Include headers prefixed with libqhull/, libqhull_r/, or libqhullcpp/ + - Renamed debugging routines dfacet/dvertex to qh_dfacet/qh_dvertex + - Rewrote user_eg, user_eg2, and user_eg3 as reentrant code + - Renamed 'qh_rand_seed' to 'qh_last_random'. Declare it as DATA + - qh_initqhull_start2 sets qh->NOerrexit on initialization + User must clear NOerrexit after setjmp() + + Other source code changes + - Define ptr_intT as 'long long' for __MINGW64__ [A. Voskov] + - poly_r.c: initialize horizon_skip [K. Schwehr] + - Removed vertexT.dim and MAX_vdim. It is not used by reentrant Qhull. + - Removed qhull_inuse. Not used by C++ + - Removed old __MWERKS__/__POWERPC__ code that speed up SIOUX I/O + - Moved #include libqhull/... before system includes (e.g., + - Comment-out _isatty declaration. Avoids "C4273 ... inconsistent dll linkage" + - Add random.h/random_r.h as an include file to random.c/random_r.c + - Rename rbox routines to qh_roundi/qh_out1/qh_out2n/qh_out3n + - Rename dfacet and dvertex to qh_dfacet and qh_dvertex + - Replace 'qhmem .zzz' with 'qhmem.zzz' + - Removed spaces between function name and parentheses + - Rename 'enum statistics' to 'enum qh_statistics' + - Declare rbox as DATA in qhull-exports.def and qhull_p-exports.def + - In comments, use 'qh.zzz' to reference qhT fields + - In qh_fprintf, use qhmem.ferr to report errors + - qh_fprintf may be called for errors in qh_initstatistics and qh_meminit + - qh_pointid returns qh_IDnone, qh_IDinterior, qh_IDunknown in place of -3, -2, -1 resp. + - getid_() returns qh_IDunknown in place of -1 + - After qh_meminit, qhmem.ferr is non-zero (stderr is the default) + - Update qh_MEMalign in testqset.c to user.h (with realT and void*) + - Split rboxT into a header file + - Add rboxlib.h to libqhull_a.h + - Rename PI to qh_PI and extend to 30 digits + - Rename MAXdim to qh_MAXdim + - Change spacing for type annotations '*' and '&' in C++ header files + - Test for !rbox_output/cpp_object in qh_fprintf_rbox + - Remove 'inline' annotation from explicit inline declarations + - Column 25 formatting for iterators, etc. + - Use '#//!\name' for section headers + - QhullFacet.cpp: zinc_(Zdistio); + - Clear qhT.ALLOWrestart in qh_errexit + - Replace longjmp with qh_errexit_rbox in qh_rboxpoints + - Add jmpExtra after rbox_errexit to protect against compiler errors + - Add qh.ISqhullQh to indicate initialization by QhullQh() + - Add library warnings to 'rbox D4', user_eg, user_eg2, user_eg3 + - Add headers to q_eg, q_egtest, and q_test + - Check that qh.NOerrexit is cleared before call to qh_initflags + +Qhull documentation + - README.txt: Added references to qh-code.htm + - README.txt: Added section 'Calling Qhull from C programs' + - qh-code.htm: Moved Performance after C++ and C interface + - qh-code.htm: Moved Cpp Questions to end of the C++ section + - qh-code.htm: Fixed documentation for 'include' path. It should be include/libqhull + - qconvex.htm: Fixed documentation for 'i'. It triangulates in 4-d and higher [ref] + - Clarified qhalf space documentation for the interior point [J. Santos] + - rbox.c: Version is same date as qh_version in global.c + - gobal_r.c: Version includes a '.r' suffix to indicate 'reentrant' + +Qhull builds + - Development moved to http://github.com/qhull/qhull + git clone git@github.com:qhull/qhull.git + - Exchanged make targets for testing. + 'make test' is a quick test of qhull programs. + 'make testall' is a thorough test + - Added 'make help' and 'make test' to libqhull and libqhull_r Makefiles + - CMakeLists.txt: Remove libqhull, libqhull_r, and libqhullcpp from include_directories + - CMakeLists.txt: Add qhull_SHAREDR for qhull_r + - CMakeLists.txt: Retain qhull_SHARED and qhull_SHAREDP (qh_QHpointer) + - CMakeLists.txt: Move qhull_SHARED and qhull_SHAREDP (qh_QHpointer) to qhull_TARGETS_OLD + Drop qhull_STATICP (use qhull_SHAREDP or qhull_STATIC) + Set SOVERSION and VERSION for shared libraries + - Move qhull_p-exports.def back to libqhull + - Switched to mingw-w64-install for gcc + - Improved prompts for 'make' + - qhull-all.pro: Remove user_eg3.cpp from OTHER_FILES + - libqhull.pro: Ordered object files by frequency of execution, as done before + - Add the folder name to C++ includes and remove libqhullcpp from INCLUDEPATH + - Changed CONFIG+=qtestlib to QT+=testlib + - Changed Makefile to gcc -O3 (was -O2) + - Changed libqhull/libqhull_r Makefiles to both produce rbox, qhull, ..., user_eg, and user_eg2 + - Removed Debian 'config/...'. It was needed for Qhull 2012. + +libqhull_r (reentrant Qhull) + - Replaced qh_qh with a parameter to each procedure [P. Klosterman] + No more globally defined data structures in Qhull + Simplified multithreading and C++ user interface + All functions are reentrant (Qt: "A reentrant function can ... be called simultaneously from multiple threads, but only if each invocation uses its own data.") + No more qh_QHpointer. + See user_eg3 and qhulltest + New libraries + libqhull_r -- Shared library with reentrant sources (e.g., poly_r.h and poly_r.c which replace libqhull's poly.h and poly.c) + libqhullstatic_r -- Static library with the same sources as libqhull_r + libqhullcpp -- The C++ interface using libqhullstatic_r (further notes below) + New executables + testqset_r -- Test qset_r.c (the reentrant version of qset.c + + Source code changes for libqhull_r + - Add qh_zero() to initialize and zero memory for qh_new_qhull + - Remove qh_save_qhull(), qh_restore_qhull(), and qh.old_qhstat from global_r.c + - Remove qh_freeqhull2() (global_r.c) + - Remove qh_freestatistics() (stat_r.c) + - Remove qh_compare_vertexpoint (qhT is not available, unused code) + - Remove conditional code for __POWERPC__ from unix_r.c and rbox_r.c + - Move qh_last_random into qh->last_random (random_r.c) + - Rename sources files with a '_r' suffix. qhull_a.h becomes qhull_ra.h + - Replace 'qh' macro with 'qh->' + - Replace global qhT with parameter-0 + - Add qhmemT to beginning of qhT. It may not be used standalone. + - Add qhstatT to end of qhT + - Remove qhull_inuse + - Change qhmem.zzz to qh->qhmem.zzz + - Replace qh_qhstat with qh->qhstat + - Remove qh_freestatistics + - Replace qh_last_random with qh->last_random + - Replace rboxT with qh->rbox_errexit, rbox_isinteger, rbox_out_offset + - Replace rbox.ferr/fout with qh->ferr/fout + - No qh for qh_exit, qh_free, qh_malloc, qh_strtod, qh_strtol, qh_stddev + - New qmake include files qhull-app-c_r.pri, qhull-app-shared_r.pri, qhull-libqhull-src_r.pri + - Replace 'int' with 'countT' and 'COUNTmax' for large counts and identifiers + - qhset converted to countT + - Removed vertexT.dim -- No longer needed by cpp + Removed MAX_vdim + - Guarantee that qh->run_id!=0. Old code assumed that qh_RANDOMint was 31 bits + +Changes to libqhullcpp + - Added QhullVertexSet.h to libqhullcpp.pro and libqhullpcpp.pro + - QhullVertexSet: error if qhsettemp_defined at copy constructor/assignment (otherwise double free) + - Enable QhullSet.operator=. Copy constructor and assignment only copies pointers + - Changed QhullPoint.operator==() to sqrt(distanceEpsilon) + - Added assignment of base class QhullPoints to PointCoordinates.operator= + - Enable QhullPoints.operator= + - Rename PointCoordinates.point_comment to describe_points + - Add 'typename T' to definition of QhullSet::value() + +C++ interface + - Reimplemented C++ interface on reentrant libqhull_r instead of libqhull + - Prepend include files with libqhullcpp/ + - Replaced UsingLibQhull with QhullQh and macro QH_TRY + Removed UsingLibQhull.currentAngleEpsilon and related routines + Removed UsingLibQhull_test.cpp + Replaced globalDistanceEpsilon with QhullQh.distanceEpsilon + Replaced globalAngleEpsilon with QhullQh.angleEpsilon + Moved UsingQhullLib.checkQhullMemoryEmpty to QhullQh.checkAndFreeQhullMemory + Replaced FACTORepsilon=10 with QhullQh.factor_epsilon=1.0 + - To avoid -Wshadow for QhullQh*, use 'qqh' for parameters and 'qh()' for methods + - Moved messaging from Qhull to QhullQh + - Add check of RboxPoints* in qh_fprintf_rbox + - Renamed Qhull.initializeQhull to Qhull.allocateQhullQh + Added qh_freeqhull(!qh_ALL) as done by unix.c and other programs + - Moved QhullPoints.extraCoordinatesCount into QhullPoints.cpp + - Replaced section tags with '#//!\name ...' + - Removed qhRunId from print() to ostream. + - Removed print() to ostream. Use '<< qhullPoint' or '<< qhullPoint.print("message")' + +C++ interface for most classes + - Remove qhRunId + - Add QhullQh *qh_qh to all types + Pointer comparisons of facetT,etc. do not test corresponding qh_qh + Added to end of type for debugging information, unless wasteful alignment + - Add QhullQh * to all constructors + - All constructors may use Qhull & instead of QhullQh * + - For inherited QhullQh types, change to 'protected' + - Renamed 'o' to 'other' except where used extensively in iterators + - Except for conditional code, merged the Conversion section into GetSet + - Removed empty(). Use isEmpty() instead + - Add operator= instead of keeping it private + - print_message=0 not allowed. Use "" instead. + - Rename isDefined() to isValid() to match Qt conventions + +C++ interface by class + - Coordinates + Removed empty(). Use isEmpty() instead + Added append(dim, coordT*) + Reformated the iterators + Convert to countT + - PointCoordinates + Added constructors for Qhull or QhullQh* (provides access to QhullPoint.operator==) + Removed PointCoordinates(int pointDimension) since PointCoordinates should have a comment. Also, it is ambiguous with PointCoordinates(QhullQh*) + Renamed point_comment to describe_points + Convert to countT + - Qhull + Remove qhull_run_i + Remove qh_active + Replace property feasiblePoint with field feasible_point and methods setFeasiblePoint/feasiblePoint + Returns qh.feasible_point if defined + Moved useOutputStream to QhullQh use_output_stream + Renamed useOutputStream() to hasOutputStream() + Replaced qhull_dimension with qh->input_dim //! Dimension of result (qh.hull_dim or one less for Delaunay/Voronoi) + Removed global s_qhull_output= 0; + Move qhull_status, qhull_message, error_stream, output_stream to QhullQh + Renamed qhullQh() to qh() + Added check of base address to allocateQhullQh(), Was not needed for qhullpcpp + - QhullFacet + Constructor requires Qhull or QhullQh* pointer + Convert to countT + Dropped implicit conversion from facetT + Dropped runId + Add print("message") to replace print() + - QhullFacetList + Constructor requires Qhull or QhullQh* pointer + Convert to countT + Dropped runId + - QhullFacetSet + Removed empty(). Use isEmpty() instead + Constructor requires Qhull or QhullQh* pointer + Convert to countT + Dropped runId + Add operator= + Implement print("message") + - QhullHyperplane + Add hyperplaneAngle() method + Rewrite operator== to use hyperplaneAngle() + Reorganize fields to keep pointers aligned + Except for default constructor requires Qhull or QhullQh* pointer + Enable copy assignment + Reorganized header + - QhullLinkedList + Add operator= + Removed empty(). Use isEmpty() instead + Convert to countT + iterator(T) made iterator(const T &) + const_iterator(T) made const_iterator(const T &) + const_iterator(iterator) made const_iterator(const iterator &) + - QhullPoint + Add constructors for Qhull or QhullQh* pointer (for id() and operator==) + Add defineAs(coordT*) + Add getBaseT() and base_type for QhullSet + Added checks for point_coordinates==0 + Removed static QhullPoint::id(), use QhullPoint.id() instead + distance() throws an error if dimension doesn't agree or if a point is undefined + Convert to countT + If !qh_qh, operator==() requires equal coordinates + Use cout<

    [R. Richter, S. Pasko] + - Remove deprecated libqhull/qhull.h + Use libqhull/libqhull.h instead. Avoids confusion with libqhullcpp/Qhull.h + - Makefile: Add LIBDIR, INCDIR, and DESTDIR to install [L.H. de Mello] + Separate MAN install from DOC install + Create install directories + Installs headers to include/libqhull, include/libqhullcpp, include/road + - CMakeLists.txt: Add MAN_INSTALL_DIR for qhull.1 and rbox.1 man pages + Add RoadTest.h to include/road for Qt users (road_HEADERS) + - Renamed md5sum files to avoid two extensions + - qh-get.htm: Add Readme links and 2009.1 note. + - qh-optf.htm: Fix link + - index.htm: Updated Google Scholar link + - qhull-zip.sh: Improved error message. + +------------ +Qhull 2011.1 2011/04/17 6.2.0.1373 + +Changes to deliverables + - qvoronoi: Deprecated 'Qt' and 'QJn'. Removed from documentation and prompts. + These options produced duplicate Voronoi vertices for cospherical data. + - Removed doskey from Qhull-go.bat. It is incompatible with Windows 7 + - Added 'facets' argument to user_eg3.cpp + - user_eg links with shared library + - qhulltest.cpp: Add closing prompt. + +Changes to build system + - Reorganized source directories + - Moved executables to bin directory + - Add CMake build for all targets (CMakeFiles.txt) [M. Moll assisted] + - Add gcc build for all targets (Makefile) + - Fixed location of qhull.man and rbox.man [M. Moll] + - Add DevStudio builds for all targets (build/*.vcproj) + - Added shared library (lib/qhull6.dll) + Added qh_QHpointer_dllimport to work around problems with MSVC + - Added static libraries with and without qh_QHpointer (lib/qhullstatic.lib) + - Added eg/make-vcproj.sh to create vcproj/sln files from cmake and qmake + - Document location of qh_QHpointer + - Use shadow build directory + - Made -fno-strict-aliasing conditional on gcc version + - Added src/qhull-app-cpp.pri, src/qhull-app-c.pri, etc. for common settings + - Add .gitignore with ignored files and directories. + - Use .git/info/exclude for locally excluded files. + - Fixed MBorland for new directory structure + - cleanall (Makefile): Delete 'linked' programs due to libqhull_r and libqhull/Makefile + +Changes to documentation + - qvoronoi.htm: Remove quotes from qvoronoi example + - qhull-cpp.xml: Add naming conventions + - index.htm: Add Google Scholar references + - qh-optf.htm: Add note about order of 'Fn' matching 'Fv' order [Q. Pan] + - Add patch for old builds in qh-get.htm + - Added C++ compiling instructions to README.txt + - Add instructions for fixing the DOS window + - Changed DOS window to command window + - Fixed html links + - qh-get.htm: Dropped the Spanish mirror site. It was disabled. + +Changes to C code + - mem.h: Define ptr_intT as 'long long' for Microsoft Windows _win64 builds. + On Linux and Mac, 'long' is 64-bits on a 64-bit host + - Added qh_QHpointer_dllimport to work around MSVC problem + - qconvex.c,etc.: Define prototype for _isatty + - Define MSG_QHULL_ERROR in user.h + - Move MSG_FIXUP to 11000 and updated FIXUP QH11... + +Changes to test code + - Add note to q_test than R1e-3 may error (qh-code.htm, Enhancements) + - Add test for executables to q_eg, etc. + - Fixed Qhull-go.bat. QHULL-GO invokes it with command.com, + +Changes to C++ interface + - QhullFacet: Added isSimplicial, isTopOrient, isTriCoplanar, isUpperDelaunay + - Added Qhull::defineVertexFacetNeighbors() for facetNeighbors of vertices. + Automatically called for facet merging and Voronoi diagrams + Do not print QhullVertex::facetNeighbors is !facetNeighborsDefined() + - Assigned FIXUP identifiers + - QhullError: Add copy constructor, assignment operator, and destructor + - Add throw() specifiers to RoadError and QhullError + - Renamed RoadError::defined() to RoadError::isDefined() + - Add #error to Qhull.h if qh_QHpointer is not defined + +Changes to C++ code + - Fixed bug reported by renangms. Vertex output throws error QH10034 + and defineVertexNeighbors() does not exist. + - Define QHULL_USES_QT for qt-qhull.cpp [renangms] + - Reviewed all copy constructors and copy assignments. Updated comments. + Defined Qhull copy constructor and copy assignment [G. Rivet-Sabourin] + Disabled UsingQhullLib default constructor, copy construct, and copy assign + - Merged changes from J. Obermayr in gitorious/jobermayrs-qhull:next + - Fix strncat limit in rboxlib.c and global.c + - Changes to CMakeLists.txt for openSUSE + - Fixed additional uses of strncat + - Fixed QhullFacet::PrintRidges to check hasNextRidge3d() + - Removed gcc warnings for shadowing from code (src/qhull-warn.pri) + - Removed semicolon after extern "C" {...} + - Removed experimental QhullEvent/QhullLog + - Use fabs() instead of abs() to avoid accidental conversions to int + - Fixed type of vertex->neighbors in qh_printvoronoi [no effect on results] + - Removed unnecessary if statement in qh_printvoronoi + +------------ +qhull 2010.1 2010/01/14 +- Fixed quote for #include in qhull.h [U.Hergenhahn, K.Roland] +- Add qt-qhull.cpp with Qt conditional code +- Add libqhullp.proj +- Add libqhull5 to Readme, Announce, download +- Reviewed #pragma +- Reviewed FIXUP and assigned QH tags +- All projects compile with warnings enabled +- Replaced 'up' glyphs with » +- Moved cpp questions to qh-code.htm#questions-cpp +- Moved suggestions to qh-code.htm#enhance +- Moved documentation requests to qh-code.htm#enhance +- Add md5sum file to distributions +- Switched to DevStudio builds to avoid dependent libraries, 10% slower + Removed user_eg3.exe and qhullcpp.dll from Windows build + Fix qhull.sln and project files for qh_QHpointer +- Add eg/qhull-zip.sh to build qhull distribution files + +------------ +qhull 2010.1 2010/01/10 +- Test for NULL fp in qh_eachvoronoi [D. Szczerba] + +qhull 2010.1 2010/01/09 + +Changes to build and distribution +- Use qh_QHpointer=0 for libqhull.a, qhull, rbox, etc. + Use -Dqh_QHpointer for libqhullp.a, qhullcpp.dll, etc. + qh_QHpointer [2010, gcc] 4% time 4% space, [2003, msvc] 8% time 2% space +- Add config/ and project/debian/ for Autoconf build [R. Laboissiere] + from debian branch in git and http://savannah.nongnu.org/cvs/?group=qhull +- Add CMakeLists.txt [kwilliams] +- Fix tabs in Makefile.txt [mschamschula] +- Add -fno-strict-aliasing to Makefile for gcc 4.1, 4.2, and 4.3 qset segfault +- Remove user_eg.exe and user_eg2.exe from Windows distribution +- Order object files by frequency of execution for better locality. + +Changes to source +- Remove ptr_intT from qh_matchvertices. It was int since the beginning. +- user.h requires for CLOCKS_PER_SEC +- Move ostream<

      ---------------------------------
    +
    +   geom.c
    +   geometric routines of qhull
    +
    +   see qh-geom.htm and geom.h
    +
    +   Copyright (c) 1993-2015 The Geometry Center.
    +   $Id: //main/2015/qhull/src/libqhull/geom.c#2 $$Change: 1995 $
    +   $DateTime: 2015/10/13 21:59:42 $$Author: bbarber $
    +
    +   infrequent code goes into geom2.c
    +*/
    +
    +#include "qhull_a.h"
    +
    +/*---------------------------------
    +
    +  qh_distplane( 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.c
    +    qh_distplane [geom.c], QhullFacet::distance, and QhullHyperplane::distance are copies
    +*/
    +void qh_distplane(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 ferr, 8001, "qh_distplane: ");
    +    qh_fprintf(qh ferr, 8002, qh_REAL_1, *dist);
    +    qh_fprintf(qh ferr, 8003, "from p%d to f%d\n", qh_pointid(point), facet->id);
    +  }
    +  return;
    +} /* distplane */
    +
    +
    +/*---------------------------------
    +
    +  qh_findbest( 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(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(point))) {
    +    if (qh TRACElevel > qh IStracing)
    +      qh IStracing= qh TRACElevel;
    +    qh_fprintf(qh ferr, 8004, "qh_findbest: point p%d starting at f%d isnewfacets? %d, unless %d exit if > %2.2g\n",
    +             qh_pointid(point), startfacet->id, isnewfacets, bestoutside, qh MINoutside);
    +    qh_fprintf(qh ferr, 8005, "  testhorizon? %d noupper? %d", testhorizon, noupper);
    +    qh_fprintf(qh ferr, 8006, "  Last point added was p%d.", qh furthest_id);
    +    qh_fprintf(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(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 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(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(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()) {
    +        /* seldom used, qh_findbestnew will retest all facets */
    +        zinc_(Zfindnewsharp);
    +        bestfacet= qh_findbestnew(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(lastfacet, point, &bestdist, numpart);
    +  if (testhorizon)
    +    bestfacet= qh_findbesthorizon(!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 */
    +
    +
    +/*---------------------------------
    +
    +  qh_findbesthorizon( 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(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 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(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 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 ferr, 4003, "qh_findbesthorizon: newbest? %d bestfacet f%d bestdist %2.2g\n", newbest, getid_(bestfacet), *bestdist));
    +  return bestfacet;
    +}  /* findbesthorizon */
    +
    +/*---------------------------------
    +
    +  qh_findbestnew( 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(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 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 ferr, 6002, "qhull internal error (qh_findbestnew): no new facets for point p%d\n",
    +              qh furthest_id);
    +    qh_errexit(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(point))) {
    +    if (qh TRACElevel > qh IStracing)
    +      qh IStracing= qh TRACElevel;
    +    qh_fprintf(qh ferr, 8008, "qh_findbestnew: point p%d facet f%d. Stop? %d if dist > %2.2g\n",
    +             qh_pointid(point), startfacet->id, isdistoutside, distoutside);
    +    qh_fprintf(qh ferr, 8009, "  Last point added p%d visitid %d.",  qh furthest_id, visitid);
    +    qh_fprintf(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(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_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 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 [?] ============ */
    +
    +/*---------------------------------
    +
    +  qh_backnormal( 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(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 ferr, 4005, "qh_backnormal: zero diagonal at column %d.\n", i));
    +    qh_precision("zero diagonal on back substitution");
    +  }
    +} /* backnormal */
    +
    +/*---------------------------------
    +
    +  qh_gausselim( 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(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 ferr, 8011, "qh_gausselim: 0 pivot at column %d. (%2.2g < %2.2g)\n", k, pivot_abs, qh DISTround);
    +          qh_printmatrix(qh ferr, "Matrix:", rows, numrow, numcol);
    +        }
    +        zzinc_(Zgauss0);
    +        qh_precision("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 ferr, "qh_gausselem: result", rows, numrow, numcol);
    +} /* gausselim */
    +
    +
    +/*---------------------------------
    +
    +  qh_getangle( 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(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 ferr, 4006, "qh_getangle: %2.2g\n", angle));
    +  return(angle);
    +} /* getangle */
    +
    +
    +/*---------------------------------
    +
    +  qh_getcenter( vertices )
    +    returns arithmetic center of a set of vertices as a new point
    +
    +  notes:
    +    allocates point array for center
    +*/
    +pointT *qh_getcenter(setT *vertices) {
    +  int k;
    +  pointT *center, *coord;
    +  vertexT *vertex, **vertexp;
    +  int count= qh_setsize(vertices);
    +
    +  if (count < 2) {
    +    qh_fprintf(qh ferr, 6003, "qhull internal error (qh_getcenter): not defined for %d points\n", count);
    +    qh_errexit(qh_ERRqhull, NULL, NULL);
    +  }
    +  center= (pointT *)qh_memalloc(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 */
    +
    +
    +/*---------------------------------
    +
    +  qh_getcentrum( facet )
    +    returns the centrum for a facet as a new point
    +
    +  notes:
    +    allocates the centrum
    +*/
    +pointT *qh_getcentrum(facetT *facet) {
    +  realT dist;
    +  pointT *centrum, *point;
    +
    +  point= qh_getcenter(facet->vertices);
    +  zzinc_(Zcentrumtests);
    +  qh_distplane(point, facet, &dist);
    +  centrum= qh_projectpoint(point, facet, dist);
    +  qh_memfree(point, qh normal_size);
    +  trace4((qh ferr, 4007, "qh_getcentrum: for f%d, %d vertices dist= %2.2g\n",
    +          facet->id, qh_setsize(facet->vertices), dist));
    +  return centrum;
    +} /* getcentrum */
    +
    +
    +/*---------------------------------
    +
    +  qh_getdistance( 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(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(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 */
    +
    +
    +/*---------------------------------
    +
    +  qh_normalize( normal, dim, toporient )
    +    normalize a vector and report if too small
    +    does not use min norm
    +
    +  see:
    +    qh_normalize2
    +*/
    +void qh_normalize(coordT *normal, int dim, boolT toporient) {
    +  qh_normalize2( normal, dim, toporient, NULL, NULL);
    +} /* normalize */
    +
    +/*---------------------------------
    +
    +  qh_normalize2( 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(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 ferr, 1, "qh_normalize: norm=%2.2g too small during p%d\n",
    +               norm, qh furthest_id));
    +        return;
    +      }
    +    }
    +  }
    +} /* normalize */
    +
    +
    +/*---------------------------------
    +
    +  qh_projectpoint( 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(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_(normsize, freelistp, newpoint, pointT);
    +  np= newpoint;
    +  normal= facet->normal;
    +  for (k=qh hull_dim; k--; )
    +    *(np++)= *point++ - dist * *normal++;
    +  return(newpoint);
    +} /* projectpoint */
    +
    +
    +/*---------------------------------
    +
    +  qh_setfacetplane( 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(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_(normsize, freelistp, facet->normal, coordT);
    +  if (facet == qh tracefacet) {
    +    oldtrace= qh IStracing;
    +    qh IStracing= 5;
    +    qh_fprintf(qh ferr, 8012, "qh_setfacetplane: facet f%d created.\n", facet->id);
    +    qh_fprintf(qh ferr, 8013, "  Last point added to hull was p%d.", qh furthest_id);
    +    if (zzval_(Ztotmerge))
    +      qh_fprintf(qh ferr, 8014, "  Last merge was #%d.", zzval_(Ztotmerge));
    +    qh_fprintf(qh ferr, 8015, "\n\nCurrent summary is:\n");
    +      qh_printsummary(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 RANDOMa, qh RANDOMb);
    +      }
    +    }else {
    +      FOREACHvertex_(facet->vertices)
    +       qh gm_row[i++]= vertex->point;
    +    }
    +    qh_sethyperplane_det(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 RANDOMa, qh RANDOMb);
    +      }
    +    }
    +    qh_sethyperplane_gauss(qh hull_dim, qh gm_row, point0, facet->toporient,
    +                facet->normal, &facet->offset, &nearzero);
    +    if (nearzero) {
    +      if (qh_orientoutside(facet)) {
    +        trace0((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(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() */
    +            if (dist > qh TRACEdist)
    +              istrace= True;
    +          }
    +        }else if (-dist > qh TRACEdist)
    +          istrace= True;
    +        if (istrace) {
    +          qh_fprintf(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(vertex->point), vertex->id, dist, facet->id, qh furthest_id);
    +          qh_errprint("DISTANT", facet, NULL, NULL, NULL);
    +        }
    +      }
    +    }
    +    qh RANDOMdist= qh old_randomdist;
    +  }
    +  if (qh IStracing >= 3) {
    +    qh_fprintf(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 ferr, 8018, "%2.2g ", facet->normal[k]);
    +    qh_fprintf(qh ferr, 8019, "\n");
    +  }
    +  if (facet == qh tracefacet)
    +    qh IStracing= oldtrace;
    +} /* setfacetplane */
    +
    +
    +/*---------------------------------
    +
    +  qh_sethyperplane_det( 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(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(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(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(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 ferr, 3, "qh_sethyperplane_det: degenerate norm during p%d.\n", qh furthest_id));
    +    zzinc_(Znearlysingular);
    +  }
    +} /* sethyperplane_det */
    +
    +
    +/*---------------------------------
    +
    +  qh_sethyperplane_gauss( 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(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(rows, dim-1, dim, &sign, nearzero);
    +  for (k=dim-1; k--; ) {
    +    if ((rows[k])[k] < 0)
    +      sign ^= 1;
    +  }
    +  if (*nearzero) {
    +    zzinc_(Znearlysingular);
    +    trace0((qh ferr, 4, "qh_sethyperplane_gauss: nearly singular or axis parallel hyperplane during p%d.\n", qh furthest_id));
    +    qh_backnormal(rows, dim-1, dim, sign, normal, &nearzero2);
    +  }else {
    +    qh_backnormal(rows, dim-1, dim, sign, normal, &nearzero2);
    +    if (nearzero2) {
    +      zzinc_(Znearlysingular);
    +      trace0((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(normal, dim, True, NULL, NULL);
    +  pointcoord= point0;
    +  normalcoef= normal;
    +  *offset= -(*pointcoord++ * *normalcoef++);
    +  for (k=dim-1; k--; )
    +    *offset -= *pointcoord++ * *normalcoef++;
    +} /* sethyperplane_gauss */
    +
    +
    +
    diff --git a/xs/src/qhull/src/libqhull/geom.h b/xs/src/qhull/src/libqhull/geom.h
    new file mode 100644
    index 000000000..16ef48d2d
    --- /dev/null
    +++ b/xs/src/qhull/src/libqhull/geom.h
    @@ -0,0 +1,176 @@
    +/*
      ---------------------------------
    +
    +  geom.h
    +    header file for geometric routines
    +
    +   see qh-geom.htm and geom.c
    +
    +   Copyright (c) 1993-2015 The Geometry Center.
    +   $Id: //main/2015/qhull/src/libqhull/geom.h#1 $$Change: 1981 $
    +   $DateTime: 2015/09/28 20:26:32 $$Author: bbarber $
    +*/
    +
    +#ifndef qhDEFgeom
    +#define qhDEFgeom 1
    +
    +#include "libqhull.h"
    +
    +/* ============ -macros- ======================== */
    +
    +/*----------------------------------
    +
    +  fabs_(a)
    +    returns the absolute value of a
    +*/
    +#define fabs_( a ) ((( a ) < 0 ) ? -( a ):( a ))
    +
    +/*----------------------------------
    +
    +  fmax_(a,b)
    +    returns the maximum value of a and b
    +*/
    +#define fmax_( a,b )  ( ( a ) < ( b ) ? ( b ) : ( a ) )
    +
    +/*----------------------------------
    +
    +  fmin_(a,b)
    +    returns the minimum value of a and b
    +*/
    +#define fmin_( a,b )  ( ( a ) > ( b ) ? ( b ) : ( a ) )
    +
    +/*----------------------------------
    +
    +  maximize_(maxval, val)
    +    set maxval to val if val is greater than maxval
    +*/
    +#define maximize_( maxval, val ) { if (( maxval ) < ( val )) ( maxval )= ( val ); }
    +
    +/*----------------------------------
    +
    +  minimize_(minval, val)
    +    set minval to val if val is less than minval
    +*/
    +#define minimize_( minval, val ) { if (( minval ) > ( val )) ( minval )= ( val ); }
    +
    +/*----------------------------------
    +
    +  det2_(a1, a2,
    +        b1, b2)
    +
    +    compute a 2-d determinate
    +*/
    +#define det2_( a1,a2,b1,b2 ) (( a1 )*( b2 ) - ( a2 )*( b1 ))
    +
    +/*----------------------------------
    +
    +  det3_(a1, a2, a3,
    +       b1, b2, b3,
    +       c1, c2, c3)
    +
    +    compute a 3-d determinate
    +*/
    +#define det3_( a1,a2,a3,b1,b2,b3,c1,c2,c3 ) ( ( a1 )*det2_( b2,b3,c2,c3 ) \
    +                - ( b1 )*det2_( a2,a3,c2,c3 ) + ( c1 )*det2_( a2,a3,b2,b3 ) )
    +
    +/*----------------------------------
    +
    +  dX( p1, p2 )
    +  dY( p1, p2 )
    +  dZ( p1, p2 )
    +
    +    given two indices into rows[],
    +
    +    compute the difference between X, Y, or Z coordinates
    +*/
    +#define dX( p1,p2 )  ( *( rows[p1] ) - *( rows[p2] ))
    +#define dY( p1,p2 )  ( *( rows[p1]+1 ) - *( rows[p2]+1 ))
    +#define dZ( p1,p2 )  ( *( rows[p1]+2 ) - *( rows[p2]+2 ))
    +#define dW( p1,p2 )  ( *( rows[p1]+3 ) - *( rows[p2]+3 ))
    +
    +/*============= prototypes in alphabetical order, infrequent at end ======= */
    +
    +void    qh_backnormal(realT **rows, int numrow, int numcol, boolT sign, coordT *normal, boolT *nearzero);
    +void    qh_distplane(pointT *point, facetT *facet, realT *dist);
    +facetT *qh_findbest(pointT *point, facetT *startfacet,
    +                     boolT bestoutside, boolT isnewfacets, boolT noupper,
    +                     realT *dist, boolT *isoutside, int *numpart);
    +facetT *qh_findbesthorizon(boolT ischeckmax, pointT *point,
    +                     facetT *startfacet, boolT noupper, realT *bestdist, int *numpart);
    +facetT *qh_findbestnew(pointT *point, facetT *startfacet, realT *dist,
    +                     boolT bestoutside, boolT *isoutside, int *numpart);
    +void    qh_gausselim(realT **rows, int numrow, int numcol, boolT *sign, boolT *nearzero);
    +realT   qh_getangle(pointT *vect1, pointT *vect2);
    +pointT *qh_getcenter(setT *vertices);
    +pointT *qh_getcentrum(facetT *facet);
    +realT   qh_getdistance(facetT *facet, facetT *neighbor, realT *mindist, realT *maxdist);
    +void    qh_normalize(coordT *normal, int dim, boolT toporient);
    +void    qh_normalize2(coordT *normal, int dim, boolT toporient,
    +            realT *minnorm, boolT *ismin);
    +pointT *qh_projectpoint(pointT *point, facetT *facet, realT dist);
    +
    +void    qh_setfacetplane(facetT *newfacets);
    +void    qh_sethyperplane_det(int dim, coordT **rows, coordT *point0,
    +              boolT toporient, coordT *normal, realT *offset, boolT *nearzero);
    +void    qh_sethyperplane_gauss(int dim, coordT **rows, pointT *point0,
    +             boolT toporient, coordT *normal, coordT *offset, boolT *nearzero);
    +boolT   qh_sharpnewfacets(void);
    +
    +/*========= infrequently used code in geom2.c =============*/
    +
    +coordT *qh_copypoints(coordT *points, int numpoints, int dimension);
    +void    qh_crossproduct(int dim, realT vecA[3], realT vecB[3], realT vecC[3]);
    +realT   qh_determinant(realT **rows, int dim, boolT *nearzero);
    +realT   qh_detjoggle(pointT *points, int numpoints, int dimension);
    +void    qh_detroundoff(void);
    +realT   qh_detsimplex(pointT *apex, setT *points, int dim, boolT *nearzero);
    +realT   qh_distnorm(int dim, pointT *point, pointT *normal, realT *offsetp);
    +realT   qh_distround(int dimension, realT maxabs, realT maxsumabs);
    +realT   qh_divzero(realT numer, realT denom, realT mindenom1, boolT *zerodiv);
    +realT   qh_facetarea(facetT *facet);
    +realT   qh_facetarea_simplex(int dim, coordT *apex, setT *vertices,
    +          vertexT *notvertex,  boolT toporient, coordT *normal, realT *offset);
    +pointT *qh_facetcenter(setT *vertices);
    +facetT *qh_findgooddist(pointT *point, facetT *facetA, realT *distp, facetT **facetlist);
    +void    qh_getarea(facetT *facetlist);
    +boolT   qh_gram_schmidt(int dim, realT **rows);
    +boolT   qh_inthresholds(coordT *normal, realT *angle);
    +void    qh_joggleinput(void);
    +realT  *qh_maxabsval(realT *normal, int dim);
    +setT   *qh_maxmin(pointT *points, int numpoints, int dimension);
    +realT   qh_maxouter(void);
    +void    qh_maxsimplex(int dim, setT *maxpoints, pointT *points, int numpoints, setT **simplex);
    +realT   qh_minabsval(realT *normal, int dim);
    +int     qh_mindiff(realT *vecA, realT *vecB, int dim);
    +boolT   qh_orientoutside(facetT *facet);
    +void    qh_outerinner(facetT *facet, realT *outerplane, realT *innerplane);
    +coordT  qh_pointdist(pointT *point1, pointT *point2, int dim);
    +void    qh_printmatrix(FILE *fp, const char *string, realT **rows, int numrow, int numcol);
    +void    qh_printpoints(FILE *fp, const char *string, setT *points);
    +void    qh_projectinput(void);
    +void    qh_projectpoints(signed char *project, int n, realT *points,
    +             int numpoints, int dim, realT *newpoints, int newdim);
    +void    qh_rotateinput(realT **rows);
    +void    qh_rotatepoints(realT *points, int numpoints, int dim, realT **rows);
    +void    qh_scaleinput(void);
    +void    qh_scalelast(coordT *points, int numpoints, int dim, coordT low,
    +                   coordT high, coordT newhigh);
    +void    qh_scalepoints(pointT *points, int numpoints, int dim,
    +                realT *newlows, realT *newhighs);
    +boolT   qh_sethalfspace(int dim, coordT *coords, coordT **nextp,
    +              coordT *normal, coordT *offset, coordT *feasible);
    +coordT *qh_sethalfspace_all(int dim, int count, coordT *halfspaces, pointT *feasible);
    +pointT *qh_voronoi_center(int dim, setT *points);
    +
    +#endif /* qhDEFgeom */
    +
    +
    +
    diff --git a/xs/src/qhull/src/libqhull/geom2.c b/xs/src/qhull/src/libqhull/geom2.c
    new file mode 100644
    index 000000000..82ec4936e
    --- /dev/null
    +++ b/xs/src/qhull/src/libqhull/geom2.c
    @@ -0,0 +1,2094 @@
    +/*
      ---------------------------------
    +
    +
    +   geom2.c
    +   infrequently used geometric routines of qhull
    +
    +   see qh-geom.htm and geom.h
    +
    +   Copyright (c) 1993-2015 The Geometry Center.
    +   $Id: //main/2015/qhull/src/libqhull/geom2.c#6 $$Change: 2065 $
    +   $DateTime: 2016/01/18 13:51:04 $$Author: bbarber $
    +
    +   frequently used code goes into geom.c
    +*/
    +
    +#include "qhull_a.h"
    +
    +/*================== functions in alphabetic order ============*/
    +
    +/*---------------------------------
    +
    +  qh_copypoints( points, numpoints, dimension)
    +    return qh_malloc'd copy of points
    +  notes:
    +    qh_free the returned points to avoid a memory leak
    +*/
    +coordT *qh_copypoints(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 ferr, 6004, "qhull error: insufficient memory to copy %d points\n",
    +        numpoints);
    +    qh_errexit(qh_ERRmem, NULL, NULL);
    +  }
    +  memcpy((char *)newpoints, (char *)points, (size_t)size); /* newpoints!=0 by QH6004 */
    +  return newpoints;
    +} /* copypoints */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  qh_determinant( 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(realT **rows, int dim, boolT *nearzero) {
    +  realT det=0;
    +  int i;
    +  boolT sign= False;
    +
    +  *nearzero= False;
    +  if (dim < 2) {
    +    qh_fprintf(qh ferr, 6005, "qhull internal error (qh_determinate): only implemented for dimension >= 2\n");
    +    qh_errexit(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(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 */
    +
    +/*---------------------------------
    +
    +  qh_detjoggle( 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(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_(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 hull_dim, maxabs, sumabs);
    +  joggle= distround * qh_JOGGLEdefault;
    +  maximize_(joggle, REALepsilon * qh_JOGGLEdefault);
    +  trace2((qh ferr, 2001, "qh_detjoggle: joggle=%2.2g maxwidth=%2.2g\n", joggle, maxwidth));
    +  return joggle;
    +} /* detjoggle */
    +
    +/*---------------------------------
    +
    +  qh_detroundoff()
    +    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(void) {
    +
    +  qh_option("_max-width", NULL, &qh MAXwidth);
    +  if (!qh SETroundoff) {
    +    qh DISTround= qh_distround(qh hull_dim, qh MAXabs_coord, qh MAXsumcoord);
    +    if (qh RANDOMdist)
    +      qh DISTround += qh RANDOMfactor * qh MAXabs_coord;
    +    qh_option("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("Angle-premerge-with-random", NULL, &qh premerge_cos);
    +  }
    +  if (qh postmerge_cos < REALmax/2) {
    +    qh postmerge_cos -= qh ANGLEround;
    +    if (qh RANDOMdist)
    +      qh_option("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("Centrum-premerge-with-random", NULL, &qh premerge_centrum);
    +  if (qh RANDOMdist && qh POSTmerge)
    +    qh_option("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("_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("_near-inside", NULL, &qh NEARinside);
    +  if (qh JOGGLEmax < qh DISTround) {
    +    qh_fprintf(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_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("Visible-distance", NULL, &qh MINvisible);
    +  }
    +  if (qh MAXcoplanar > REALmax/2) {
    +    qh MAXcoplanar= qh MINvisible;
    +    qh_option("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("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("_wide-facet", NULL, &qh WIDEfacet);
    +  if (qh MINvisible > qh MINoutside + 3 * REALepsilon
    +  && !qh BESToutside && !qh FORCEoutput)
    +    qh_fprintf(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 */
    +
    +/*---------------------------------
    +
    +  qh_detsimplex( 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(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 ferr, 6007, "qhull internal error (qh_detsimplex): #points %d < dimension %d\n",
    +               i, dim);
    +    qh_errexit(qh_ERRqhull, NULL, NULL);
    +  }
    +  det= qh_determinant(rows, dim, nearzero);
    +  trace2((qh ferr, 2002, "qh_detsimplex: det=%2.2g for point p%d, dim %d, nearzero? %d\n",
    +          det, qh_pointid(apex), dim, *nearzero));
    +  return det;
    +} /* detsimplex */
    +
    +/*---------------------------------
    +
    +  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.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 */
    +
    +/*---------------------------------
    +
    +  qh_distround(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(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 ferr, 4008, "qh_distround: %2.2g maxabs %2.2g maxsumabs %2.2g maxdistsum %2.2g\n",
    +                 maxround, maxabs, maxsumabs, maxdistsum));
    +  return maxround;
    +} /* distround */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +
    +/*---------------------------------
    +
    +  qh_facetarea( 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(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 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(facet);
    +    FOREACHridge_(facet->ridges)
    +      area += qh_facetarea_simplex(qh hull_dim, centrum, ridge->vertices,
    +                 NULL, (boolT)(ridge->top == facet),  facet->normal, &facet->offset);
    +    if (qh CENTERtype != qh_AScentrum)
    +      qh_memfree(centrum, qh normal_size);
    +  }
    +  if (facet->upperdelaunay && qh DELAUNAY)
    +    area= -area;  /* the normal should be [0,...,1] */
    +  trace4((qh ferr, 4009, "qh_facetarea: f%d area %2.2g\n", facet->id, area));
    +  return area;
    +} /* facetarea */
    +
    +/*---------------------------------
    +
    +  qh_facetarea_simplex( 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(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 ferr, 6008, "qhull internal error (qh_facetarea_simplex): #points %d != dim %d -1\n",
    +               i, dim);
    +    qh_errexit(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(rows, dim, &nearzero);
    +  if (toporient)
    +    area= -area;
    +  area *= qh AREAfactor;
    +  trace4((qh ferr, 4010, "qh_facetarea_simplex: area=%2.2g for point p%d, toporient %d, nearzero? %d\n",
    +          area, qh_pointid(apex), toporient, nearzero));
    +  return area;
    +} /* facetarea_simplex */
    +
    +/*---------------------------------
    +
    +  qh_facetcenter( 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(setT *vertices) {
    +  setT *points= qh_settemp(qh_setsize(vertices));
    +  vertexT *vertex, **vertexp;
    +  pointT *center;
    +
    +  FOREACHvertex_(vertices)
    +    qh_setappend(&points, vertex->point);
    +  center= qh_voronoi_center(qh hull_dim-1, points);
    +  qh_settempfree(&points);
    +  return center;
    +} /* facetcenter */
    +
    +/*---------------------------------
    +
    +  qh_findgooddist( 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(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(point, facetA, &bestdist);
    +    bestfacet= facetA;
    +    goodseen= True;
    +  }
    +  qh_removefacet(facetA);
    +  qh_appendfacet(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(point, neighbor, &dist);
    +      if (dist > 0) {
    +        qh_removefacet(neighbor);
    +        qh_appendfacet(neighbor);
    +        if (neighbor->good) {
    +          goodseen= True;
    +          if (dist > bestdist) {
    +            bestdist= dist;
    +            bestfacet= neighbor;
    +          }
    +        }
    +      }
    +    }
    +  }
    +  if (bestfacet) {
    +    *distp= bestdist;
    +    trace2((qh ferr, 2003, "qh_findgooddist: p%d is %2.2g above good facet f%d\n",
    +      qh_pointid(point), bestdist, bestfacet->id));
    +    return bestfacet;
    +  }
    +  trace4((qh ferr, 4011, "qh_findgooddist: no good facet for p%d above f%d\n",
    +      qh_pointid(point), facetA->id));
    +  return NULL;
    +}  /* findgooddist */
    +
    +/*---------------------------------
    +
    +  qh_getarea( 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(facetT *facetlist) {
    +  realT area;
    +  realT dist;
    +  facetT *facet;
    +
    +  if (qh hasAreaVolume)
    +    return;
    +  if (qh REPORTfreq)
    +    qh_fprintf(qh ferr, 8020, "computing area of each facet and volume of the convex hull\n");
    +  else
    +    trace1((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(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 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 */
    +
    +/*---------------------------------
    +
    +  qh_gram_schmidt( 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(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 */
    +
    +
    +/*---------------------------------
    +
    +  qh_inthresholds( 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(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 */
    +
    +
    +/*---------------------------------
    +
    +  qh_joggleinput()
    +    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(void) {
    +  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 ferr, 6009, "qhull error: insufficient memory to joggle %d points\n",
    +          qh num_points);
    +      qh_errexit(qh_ERRmem, NULL, NULL);
    +    }
    +    qh POINTSmalloc= True;
    +    if (qh JOGGLEmax == 0.0) {
    +      qh JOGGLEmax= qh_detjoggle(qh input_points, qh num_points, qh hull_dim);
    +      qh_option("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("QJoggle", NULL, &qh JOGGLEmax);
    +  }
    +  if (qh build_cnt > 1 && qh JOGGLEmax > fmax_(qh MAXwidth/4, 0.1)) {
    +      qh_fprintf(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_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("_joggle-seed", &seed, NULL);
    +  trace0((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 hull_dim, qh num_points, qh first_point);
    +  }
    +} /* joggleinput */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +
    +/*---------------------------------
    +
    +  qh_maxmin( 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(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 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_ERRinput, NULL, NULL);
    +  }
    +  set= qh_settemp(2*dimension);
    +  for (k=0; k < dimension; k++) {
    +    if (points == qh GOODpointp)
    +      minimum= maximum= points + dimension;
    +    else
    +      minimum= maximum= points;
    +    FORALLpoint_(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(&set, maximum);
    +    qh_setappend(&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 ferr, "qh_maxmin: found the max and min points(by dim):", set);
    +  return(set);
    +} /* maxmin */
    +
    +/*---------------------------------
    +
    +  qh_maxouter()
    +    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(void) {
    +  realT dist;
    +
    +  dist= fmax_(qh max_outside, qh DISTround);
    +  dist += qh DISTround;
    +  trace4((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 */
    +
    +/*---------------------------------
    +
    +  qh_maxsimplex( 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(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(*simplex);
    +  if (sizinit < 2) {
    +    if (qh_setsize(maxpoints) >= 2) {
    +      FOREACHpoint_(maxpoints) {
    +        if (maxcoord < point[0]) {
    +          maxcoord= point[0];
    +          maxx= point;
    +        }
    +        if (mincoord > point[0]) {
    +          mincoord= point[0];
    +          minx= point;
    +        }
    +      }
    +    }else {
    +      FORALLpoint_(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(simplex, minx);
    +    if (qh_setsize(*simplex) < 2)
    +      qh_setunique(simplex, maxx);
    +    sizinit= qh_setsize(*simplex);
    +    if (sizinit < 2) {
    +      qh_precision("input has same x coordinate");
    +      if (zzval_(Zsetplane) > qh hull_dim+1) {
    +        qh_fprintf(qh ferr, 6012, "qhull precision error (qh_maxsimplex for voronoi_center):\n%d points with the same x coordinate.\n",
    +                 qh_setsize(maxpoints)+numpoints);
    +        qh_errexit(qh_ERRprec, NULL, NULL);
    +      }else {
    +        qh_fprintf(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_ERRinput, NULL, NULL);
    +      }
    +    }
    +  }
    +  for (k=sizinit; k < dim+1; k++) {
    +    maxpoint= NULL;
    +    maxdet= -REALmax;
    +    FOREACHpoint_(maxpoints) {
    +      if (!qh_setin(*simplex, point)) {
    +        det= qh_detsimplex(point, *simplex, k, &nearzero);
    +        if ((det= fabs_(det)) > maxdet) {
    +          maxdet= det;
    +          maxpoint= point;
    +          maxnearzero= nearzero;
    +        }
    +      }
    +    }
    +    if (!maxpoint || maxnearzero) {
    +      zinc_(Zsearchpoints);
    +      if (!maxpoint) {
    +        trace0((qh ferr, 7, "qh_maxsimplex: searching all points for %d-th initial vertex.\n", k+1));
    +      }else {
    +        trace0((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(maxpoint), maxdet));
    +      }
    +      FORALLpoint_(points, numpoints) {
    +        if (point == qh GOODpointp)
    +          continue;
    +        if (!qh_setin(*simplex, point)) {
    +          det= qh_detsimplex(point, *simplex, k, &nearzero);
    +          if ((det= fabs_(det)) > maxdet) {
    +            maxdet= det;
    +            maxpoint= point;
    +            maxnearzero= nearzero;
    +          }
    +        }
    +      }
    +    } /* !maxpoint */
    +    if (!maxpoint) {
    +      qh_fprintf(qh ferr, 6014, "qhull internal error (qh_maxsimplex): not enough points available\n");
    +      qh_errexit(qh_ERRqhull, NULL, NULL);
    +    }
    +    qh_setappend(simplex, maxpoint);
    +    trace1((qh ferr, 1002, "qh_maxsimplex: selected point p%d for %d`th initial vertex, det=%2.2g\n",
    +            qh_pointid(maxpoint), k+1, maxdet));
    +  } /* k */
    +} /* maxsimplex */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +
    +/*---------------------------------
    +
    +  qh_mindif( 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 */
    +
    +
    +
    +/*---------------------------------
    +
    +  qh_orientoutside( facet  )
    +    make facet outside oriented via qh.interior_point
    +
    +  returns:
    +    True if facet reversed orientation.
    +*/
    +boolT qh_orientoutside(facetT *facet) {
    +  int k;
    +  realT dist;
    +
    +  qh_distplane(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 */
    +
    +/*---------------------------------
    +
    +  qh_outerinner( 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_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(facetT *facet, realT *outerplane, realT *innerplane) {
    +  realT dist, mindist;
    +  vertexT *vertex, **vertexp;
    +
    +  if (outerplane) {
    +    if (!qh_MAXoutside || !facet || !qh maxoutdone) {
    +      *outerplane= qh_maxouter();       /* 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(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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +
    +/*---------------------------------
    +
    +  qh_printmatrix( fp, string, rows, numrow, numcol )
    +    print matrix to fp given by row vectors
    +    print string as header
    +
    +  notes:
    +    print a vector by qh_printmatrix(fp, "", &vect, 1, len)
    +*/
    +void qh_printmatrix(FILE *fp, const char *string, realT **rows, int numrow, int numcol) {
    +  realT *rowp;
    +  realT r; /*bug fix*/
    +  int i,k;
    +
    +  qh_fprintf(fp, 9001, "%s\n", string);
    +  for (i=0; i < numrow; i++) {
    +    rowp= rows[i];
    +    for (k=0; k < numcol; k++) {
    +      r= *rowp++;
    +      qh_fprintf(fp, 9002, "%6.3g ", r);
    +    }
    +    qh_fprintf(fp, 9003, "\n");
    +  }
    +} /* printmatrix */
    +
    +
    +/*---------------------------------
    +
    +  qh_printpoints( fp, string, points )
    +    print pointids to fp for a set of points
    +    if string, prints string and 'p' point ids
    +*/
    +void qh_printpoints(FILE *fp, const char *string, setT *points) {
    +  pointT *point, **pointp;
    +
    +  if (string) {
    +    qh_fprintf(fp, 9004, "%s", string);
    +    FOREACHpoint_(points)
    +      qh_fprintf(fp, 9005, " p%d", qh_pointid(point));
    +    qh_fprintf(fp, 9006, "\n");
    +  }else {
    +    FOREACHpoint_(points)
    +      qh_fprintf(fp, 9007, " %d", qh_pointid(point));
    +    qh_fprintf(fp, 9008, "\n");
    +  }
    +} /* printpoints */
    +
    +
    +/*---------------------------------
    +
    +  qh_projectinput()
    +    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(void) {
    +  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(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(project, projectsize);
    +    qh_fprintf(qh ferr, 6015, "qhull internal error (qh_projectinput): dimension after projection %d != hull_dim %d\n", newdim, qh hull_dim);
    +    qh_errexit(qh_ERRqhull, NULL, NULL);
    +  }
    +  if (!(newpoints= qh temp_malloc= (coordT*)qh_malloc(newnum*newdim*sizeof(coordT)))){
    +    qh_memfree(project, projectsize);
    +    qh_fprintf(qh ferr, 6016, "qhull error: insufficient memory to project %d points\n",
    +           qh num_points);
    +    qh_errexit(qh_ERRmem, NULL, NULL);
    +  }
    +  /* qh_projectpoints throws error if mismatched dimensions */
    +  qh_projectpoints(project, qh input_dim+1, qh first_point,
    +                    qh num_points, qh input_dim, newpoints, newdim);
    +  trace1((qh ferr, 1003, "qh_projectinput: updating lower and upper_bound\n"));
    +  qh_projectpoints(project, qh input_dim+1, qh lower_bound,
    +                    1, qh input_dim+1, qh lower_bound, newdim+1);
    +  qh_projectpoints(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(project, projectsize);
    +      qh_fprintf(qh ferr, 6017, "qhull internal error (qh_projectinput): HALFspace defined without qh.feasible_point\n");
    +      qh_errexit(qh_ERRqhull, NULL, NULL);
    +    }
    +    qh_projectpoints(project, qh input_dim, qh feasible_point,
    +                      1, qh input_dim, qh feasible_point, newdim);
    +  }
    +  qh_memfree(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 ferr, 9, "qh_projectinput: projected points to paraboloid for Delaunay\n"));
    +  }else if (qh DELAUNAY)  /* !qh ATinfinity */
    +    qh_setdelaunay( qh hull_dim, qh num_points, qh first_point);
    +} /* projectinput */
    +
    +
    +/*---------------------------------
    +
    +  qh_projectpoints( 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(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 ferr, 6018, "qhull internal error (qh_projectpoints): newdim %d should be %d after projection\n",
    +      newdim, testdim);
    +    qh_errexit(qh_ERRqhull, NULL, NULL);
    +  }
    +  for (j=0; j= 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 ferr, 1004, "qh_projectpoints: projected %d points from dim %d to dim %d\n",
    +    numpoints, dim, newdim));
    +} /* projectpoints */
    +
    +
    +/*---------------------------------
    +
    +  qh_rotateinput( 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(realT **rows) {
    +
    +  if (!qh POINTSmalloc) {
    +    qh first_point= qh_copypoints(qh first_point, qh num_points, qh hull_dim);
    +    qh POINTSmalloc= True;
    +  }
    +  qh_rotatepoints(qh first_point, qh num_points, qh hull_dim, rows);
    +}  /* rotateinput */
    +
    +/*---------------------------------
    +
    +  qh_rotatepoints( 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(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 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 */
    +
    +
    +/*---------------------------------
    +
    +  qh_scaleinput()
    +    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(void) {
    +
    +  if (!qh POINTSmalloc) {
    +    qh first_point= qh_copypoints(qh first_point, qh num_points, qh hull_dim);
    +    qh POINTSmalloc= True;
    +  }
    +  qh_scalepoints(qh first_point, qh num_points, qh hull_dim,
    +       qh lower_bound, qh upper_bound);
    +}  /* scaleinput */
    +
    +/*---------------------------------
    +
    +  qh_scalelast( 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(coordT *points, int numpoints, int dim, coordT low,
    +                   coordT high, coordT newhigh) {
    +  realT scale, shift;
    +  coordT *coord;
    +  int i;
    +  boolT nearzero= False;
    +
    +  trace4((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 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 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_ERRinput, NULL, NULL);
    +  }
    +  shift= - low * newhigh / (high-low);
    +  coord= points + dim - 1;
    +  for (i=numpoints; i--; coord += dim)
    +    *coord= *coord * scale + shift;
    +} /* scalelast */
    +
    +/*---------------------------------
    +
    +  qh_scalepoints( 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(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 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_ERRinput, NULL, NULL);
    +    }
    +    scale= qh_divzero(newhigh - newlow, high - low,
    +                  qh MINdenom_1, &nearzero);
    +    if (nearzero) {
    +      qh_fprintf(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_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 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 */
    +
    +
    +/*---------------------------------
    +
    +  qh_setdelaunay( 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 hull_dim, 1, point)
    +
    +    Do not use options 'Qbk', 'QBk', or 'QbB' since they scale
    +    the coordinates after the original projection.
    +
    +*/
    +void qh_setdelaunay(int dim, int count, pointT *points) {
    +  int i, k;
    +  coordT *coordp, coord;
    +  realT paraboloid;
    +
    +  trace0((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(points, count, dim, qh last_low, qh last_high, qh last_newhigh);
    +} /* setdelaunay */
    +
    +
    +/*---------------------------------
    +
    +  qh_sethalfspace( 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(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 ferr, 8021, "qh_sethalfspace: halfspace at offset %6.2g to point: ", *offset);
    +    for (k=dim, coordp=coords; k--; ) {
    +      r= *coordp++;
    +      qh_fprintf(qh ferr, 8022, " %6.2g", r);
    +    }
    +    qh_fprintf(qh ferr, 8023, "\n");
    +  }
    +  return True;
    +LABELerroroutside:
    +  feasiblep= feasible;
    +  normp= normal;
    +  qh_fprintf(qh ferr, 6023, "qhull input error: feasible point is not clearly inside halfspace\nfeasible point: ");
    +  for (k=dim; k--; )
    +    qh_fprintf(qh ferr, 8024, qh_REAL_1, r=*(feasiblep++));
    +  qh_fprintf(qh ferr, 8025, "\n     halfspace: ");
    +  for (k=dim; k--; )
    +    qh_fprintf(qh ferr, 8026, qh_REAL_1, r=*(normp++));
    +  qh_fprintf(qh ferr, 8027, "\n     at offset: ");
    +  qh_fprintf(qh ferr, 8028, qh_REAL_1, *offset);
    +  qh_fprintf(qh ferr, 8029, " and distance: ");
    +  qh_fprintf(qh ferr, 8030, qh_REAL_1, dist);
    +  qh_fprintf(qh ferr, 8031, "\n");
    +  return False;
    +} /* sethalfspace */
    +
    +/*---------------------------------
    +
    +  qh_sethalfspace_all( 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(int dim, int count, coordT *halfspaces, pointT *feasible) {
    +  int i, newdim;
    +  pointT *newpoints;
    +  coordT *coordp, *normalp, *offsetp;
    +
    +  trace0((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 ferr, 6024, "qhull error: insufficient memory to compute dual of %d halfspaces\n",
    +          count);
    +    qh_errexit(qh_ERRmem, NULL, NULL);
    +  }
    +  coordp= newpoints;
    +  normalp= halfspaces;
    +  for (i=0; i < count; i++) {
    +    offsetp= normalp + newdim;
    +    if (!qh_sethalfspace(newdim, coordp, &coordp, normalp, offsetp, feasible)) {
    +      qh_free(newpoints);  /* feasible is not inside halfspace as reported by qh_sethalfspace */
    +      qh_fprintf(qh ferr, 8032, "The halfspace was at index %d\n", i);
    +      qh_errexit(qh_ERRinput, NULL, NULL);
    +    }
    +    normalp= offsetp + 1;
    +  }
    +  return newpoints;
    +} /* sethalfspace_all */
    +
    +
    +/*---------------------------------
    +
    +  qh_sharpnewfacets()
    +
    +  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(void) {
    +  facetT *facet;
    +  boolT issharp = False;
    +  int *quadrant, k;
    +
    +  quadrant= (int*)qh_memalloc(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( quadrant, qh hull_dim * sizeof(int));
    +  trace3((qh ferr, 3001, "qh_sharpnewfacets: %d\n", issharp));
    +  return issharp;
    +} /* sharpnewfacets */
    +
    +/*---------------------------------
    +
    +  qh_voronoi_center( 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(int dim, setT *points) {
    +  pointT *point, **pointp, *point0;
    +  pointT *center= (pointT*)qh_memalloc(qh center_size);
    +  setT *simplex;
    +  int i, j, k, size= qh_setsize(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(center, qh center_size);
    +    qh_fprintf(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_ERRqhull, NULL, NULL);
    +    simplex= points;  /* never executed -- avoids warning */
    +  }else {
    +    simplex= qh_settemp(dim+1);
    +    qh_maxsimplex(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 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 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 gm_row, dim, &nearzero)*factor + point0[i];
    +    }
    +#ifndef qh_NOtrace
    +    if (qh IStracing >= 3) {
    +      qh_fprintf(qh ferr, 8033, "qh_voronoi_center: det %2.2g factor %2.2g ", det, factor);
    +      qh_printmatrix(qh ferr, "center:", ¢er, 1, dim);
    +      if (qh IStracing >= 5) {
    +        qh_printpoints(qh ferr, "points", simplex);
    +        FOREACHpoint_(simplex)
    +          qh_fprintf(qh ferr, 8034, "p%d dist %.2g, ", qh_pointid(point),
    +                   qh_pointdist(point, center, dim));
    +        qh_fprintf(qh ferr, 8035, "\n");
    +      }
    +    }
    +#endif
    +  }
    +  if (simplex != points)
    +    qh_settempfree(&simplex);
    +  return center;
    +} /* voronoi_center */
    +
    diff --git a/xs/src/qhull/src/libqhull/global.c b/xs/src/qhull/src/libqhull/global.c
    new file mode 100644
    index 000000000..0328fea7b
    --- /dev/null
    +++ b/xs/src/qhull/src/libqhull/global.c
    @@ -0,0 +1,2217 @@
    +
    +/*
      ---------------------------------
    +
    +   global.c
    +   initializes all the globals of the qhull application
    +
    +   see README
    +
    +   see libqhull.h for qh.globals and function prototypes
    +
    +   see qhull_a.h for internal functions
    +
    +   Copyright (c) 1993-2015 The Geometry Center.
    +   $Id: //main/2015/qhull/src/libqhull/global.c#17 $$Change: 2066 $
    +   $DateTime: 2016/01/18 19:29:17 $$Author: bbarber $
    + */
    +
    +#include "qhull_a.h"
    +
    +/*========= qh definition -- globals defined in libqhull.h =======================*/
    +
    +#if qh_QHpointer
    +qhT *qh_qh= NULL;       /* pointer to all global variables */
    +#else
    +qhT qh_qh;              /* all global variables.
    +                           Add "= {0}" if this causes a compiler error.
    +                           Also qh_qhstat in stat.c and qhmem in mem.c.  */
    +#endif
    +
    +/*----------------------------------
    +
    +  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.c, rbox.c, libqhull.c, qconvex.c, qdelaun.c qvoronoi.c, qhalf.c, testqset.c
    +*/
    +
    +const char qh_version[]= "2015.2 2016/01/18";
    +const char qh_version2[]= "qhull 7.2.0 (2015.2 2016/01/18)";
    +
    +/*---------------------------------
    +
    +  qh_appendprint( printFormat )
    +    append printFormat to qh.PRINTout unless already defined
    +*/
    +void qh_appendprint(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 */
    +
    +/*---------------------------------
    +
    +  qh_checkflags( 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(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 ferr, 6026, "qhull error (qh_checkflags): hiddenflags must start and end with a space: \"%s\"", hiddenflags);
    +    qh_errexit(qh_ERRinput, NULL, NULL);
    +  }
    +  if (strpbrk(hiddenflags, ",\n\r\t")) {
    +    qh_fprintf(qh ferr, 6027, "qhull error (qh_checkflags): hiddenflags contains commas, newlines, or tabs: \"%s\"", hiddenflags);
    +    qh_errexit(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(++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 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_ERRinput, NULL, NULL);
    +} /* checkflags */
    +
    +/*---------------------------------
    +
    +  qh_clear_outputflags()
    +    Clear output flags for QhullPoints
    +*/
    +void qh_clear_outputflags(void) {
    +  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 */
    +
    +/*---------------------------------
    +
    +  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(void) {
    +
    +#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 ferr, 6030, "qhull internal error (qh_clock): sysconf() failed.  Use qh_CLOCKtype 1 in user.h\n");
    +      qh_errexit(qh_ERRqhull, NULL, NULL);
    +    }
    +  }
    +  if (times(&time) == -1) {
    +    qh_fprintf(qh ferr, 6031, "qhull internal error (qh_clock): times() failed.  Use qh_CLOCKtype 1 in user.h\n");
    +    qh_errexit(qh_ERRqhull, NULL, NULL);
    +  }
    +  ratio= qh_SECticks / (double)clktck;
    +  ticks= time.tms_utime * ratio;
    +  return ticks;
    +#else
    +  qh_fprintf(qh ferr, 6032, "qhull internal error (qh_clock): use qh_CLOCKtype 2 in user.h\n");
    +  qh_errexit(qh_ERRqhull, NULL, NULL); /* never returns */
    +  return 0;
    +#endif
    +} /* clock */
    +
    +/*---------------------------------
    +
    +  qh_freebuffers()
    +    free up global memory buffers
    +
    +  notes:
    +    must match qh_initbuffers()
    +*/
    +void qh_freebuffers(void) {
    +
    +  trace5((qh ferr, 5001, "qh_freebuffers: freeing up global memory buffers\n"));
    +  /* allocated by qh_initqhull_buffers */
    +  qh_memfree(qh NEARzero, qh hull_dim * sizeof(realT));
    +  qh_memfree(qh lower_threshold, (qh input_dim+1) * sizeof(realT));
    +  qh_memfree(qh upper_threshold, (qh input_dim+1) * sizeof(realT));
    +  qh_memfree(qh lower_bound, (qh input_dim+1) * sizeof(realT));
    +  qh_memfree(qh upper_bound, (qh input_dim+1) * sizeof(realT));
    +  qh_memfree(qh gm_matrix, (qh hull_dim+1) * qh hull_dim * sizeof(coordT));
    +  qh_memfree(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 other_points);
    +  qh_setfree(&qh del_vertices);
    +  qh_setfree(&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 ferr, 5002, "qh_freebuffers: finished\n"));
    +} /* freebuffers */
    +
    +
    +/*---------------------------------
    +
    +  qh_freebuild( 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(boolT allmem) {
    +  facetT *facet;
    +  vertexT *vertex;
    +  ridgeT *ridge, **ridgep;
    +  mergeT *merge, **mergep;
    +
    +  trace1((qh ferr, 1005, "qh_freebuild: free memory from qh_inithull and qh_buildhull\n"));
    +  if (qh del_vertices)
    +    qh_settruncate(qh del_vertices, 0);
    +  if (allmem) {
    +    while ((vertex= qh vertex_list)) {
    +      if (vertex->next)
    +        qh_delvertex(vertex);
    +      else {
    +        qh_memfree(vertex, (int)sizeof(vertexT));
    +        qh newvertex_list= qh vertex_list= NULL;
    +      }
    +    }
    +  }else if (qh VERTEXneighbors) {
    +    FORALLvertices
    +      qh_setfreelong(&(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(&(ridge->vertices));
    +          qh_memfree(ridge, (int)sizeof(ridgeT));
    +        }else
    +          ridge->seen= True;
    +      }
    +      qh_setfree(&(facet->outsideset));
    +      qh_setfree(&(facet->coplanarset));
    +      qh_setfree(&(facet->neighbors));
    +      qh_setfree(&(facet->ridges));
    +      qh_setfree(&(facet->vertices));
    +      if (facet->next)
    +        qh_delfacet(facet);
    +      else {
    +        qh_memfree(facet, (int)sizeof(facetT));
    +        qh visible_list= qh newfacet_list= qh facet_list= NULL;
    +      }
    +    }
    +  }else {
    +    FORALLfacets {
    +      qh_setfreelong(&(facet->outsideset));
    +      qh_setfreelong(&(facet->coplanarset));
    +      if (!facet->simplicial) {
    +        qh_setfreelong(&(facet->neighbors));
    +        qh_setfreelong(&(facet->ridges));
    +        qh_setfreelong(&(facet->vertices));
    +      }
    +    }
    +  }
    +  qh_setfree(&(qh hash_table));
    +  qh_memfree(qh interior_point, qh normal_size);
    +  qh interior_point= NULL;
    +  FOREACHmerge_(qh facet_mergeset)  /* usually empty */
    +    qh_memfree(merge, (int)sizeof(mergeT));
    +  qh facet_mergeset= NULL;  /* temp set */
    +  qh degen_mergeset= NULL;  /* temp set */
    +  qh_settempfree_all();
    +} /* freebuild */
    +
    +/*---------------------------------
    +
    +  qh_freeqhull( allmem )
    +    see qh_freeqhull2
    +    if qh_QHpointer, frees qh_qh
    +*/
    +void qh_freeqhull(boolT allmem) {
    +    qh_freeqhull2(allmem);
    +#if qh_QHpointer
    +    qh_free(qh_qh);
    +    qh_qh= NULL;
    +#endif
    +}
    +
    +/*---------------------------------
    +
    +qh_freeqhull2( 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()
    +
    +design:
    +  free global and temporary memory from qh_initbuild and qh_buildhull
    +  free buffers
    +  free statistics
    +*/
    +void qh_freeqhull2(boolT allmem) {
    +
    +  qh NOerrexit= True;  /* no more setjmp since called at exit and ~QhullQh */
    +  trace1((qh ferr, 1006, "qh_freeqhull: free global memory\n"));
    +  qh_freebuild(allmem);
    +  qh_freebuffers();
    +  qh_freestatistics();
    +#if qh_QHpointer
    +  memset((char *)qh_qh, 0, sizeof(qhT));
    +  /* qh_qh freed by caller, qh_freeqhull() */
    +#else
    +  memset((char *)&qh_qh, 0, sizeof(qhT));
    +#endif
    +  qh NOerrexit= True;
    +} /* freeqhull2 */
    +
    +/*---------------------------------
    +
    +  qh_init_A( 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(FILE *infile, FILE *outfile, FILE *errfile, int argc, char *argv[]) {
    +  qh_meminit(errfile);
    +  qh_initqhull_start(infile, outfile, errfile);
    +  qh_init_qhull_command(argc, argv);
    +} /* init_A */
    +
    +/*---------------------------------
    +
    +  qh_init_B( 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()
    +
    +    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(coordT *points, int numpoints, int dim, boolT ismalloc) {
    +  qh_initqhull_globals(points, numpoints, dim, ismalloc);
    +  if (qhmem.LASTsize == 0)
    +    qh_initqhull_mem();
    +  /* mem.c and qset.c are initialized */
    +  qh_initqhull_buffers();
    +  qh_initthresholds(qh qhull_command);
    +  if (qh PROJECTinput || (qh DELAUNAY && qh PROJECTdelaunay))
    +    qh_projectinput();
    +  if (qh SCALEinput)
    +    qh_scaleinput();
    +  if (qh ROTATErandom >= 0) {
    +    qh_randommatrix(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 hull_dim, qh gm_row);
    +    qh_rotateinput(qh gm_row);
    +  }
    +} /* init_B */
    +
    +/*---------------------------------
    +
    +  qh_init_qhull_command( 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(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 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 */
    +
    +/*---------------------------------
    +
    +  qh_initflags( 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.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(char *command) {
    +  int k, i, lastproject;
    +  char *s= command, *t, *prev_s, *start, key;
    +  boolT isgeom= False, wasproject;
    +  realT r;
    +
    +  if(qh NOerrexit){/* without this comment, segfault in gcc 4.4.0 mingw32 */
    +    qh_fprintf(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("delaunay", NULL, NULL);
    +      qh DELAUNAY= True;
    +      break;
    +    case 'f':
    +      qh_option("facets", NULL, NULL);
    +      qh_appendprint(qh_PRINTfacets);
    +      break;
    +    case 'i':
    +      qh_option("incidence", NULL, NULL);
    +      qh_appendprint(qh_PRINTincidences);
    +      break;
    +    case 'm':
    +      qh_option("mathematica", NULL, NULL);
    +      qh_appendprint(qh_PRINTmathematica);
    +      break;
    +    case 'n':
    +      qh_option("normals", NULL, NULL);
    +      qh_appendprint(qh_PRINTnormals);
    +      break;
    +    case 'o':
    +      qh_option("offFile", NULL, NULL);
    +      qh_appendprint(qh_PRINToff);
    +      break;
    +    case 'p':
    +      qh_option("points", NULL, NULL);
    +      qh_appendprint(qh_PRINTpoints);
    +      break;
    +    case 's':
    +      qh_option("summary", NULL, NULL);
    +      qh PRINTsummary= True;
    +      break;
    +    case 'v':
    +      qh_option("voronoi", NULL, NULL);
    +      qh VORONOI= True;
    +      qh DELAUNAY= True;
    +      break;
    +    case 'A':
    +      if (!isdigit(*s) && *s != '.' && *s != '-')
    +        qh_fprintf(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("Angle-premerge-", NULL, &qh premerge_cos);
    +          qh PREmerge= True;
    +        }else {
    +          qh postmerge_cos= qh_strtod(s, &s);
    +          qh_option("Angle-postmerge", NULL, &qh postmerge_cos);
    +          qh POSTmerge= True;
    +        }
    +        qh MERGING= True;
    +      }
    +      break;
    +    case 'C':
    +      if (!isdigit(*s) && *s != '.' && *s != '-')
    +        qh_fprintf(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("Centrum-premerge-", NULL, &qh premerge_centrum);
    +          qh PREmerge= True;
    +        }else {
    +          qh postmerge_centrum= qh_strtod(s, &s);
    +          qh_option("Centrum-postmerge", NULL, &qh postmerge_centrum);
    +          qh POSTmerge= True;
    +        }
    +        qh MERGING= True;
    +      }
    +      break;
    +    case 'E':
    +      if (*s == '-')
    +        qh_fprintf(qh ferr, 7004, "qhull warning: negative maximum roundoff given for option 'An'.  Ignored.\n");
    +      else if (!isdigit(*s))
    +        qh_fprintf(qh ferr, 7005, "qhull warning: no maximum roundoff given for option 'En'.  Ignored.\n");
    +      else {
    +        qh DISTround= qh_strtod(s, &s);
    +        qh_option("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 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 ferr, 6034, "qhull error: insufficient memory for 'Hn,n,n'\n");
    +          qh_errexit(qh_ERRmem, NULL, NULL);
    +        }
    +        strncpy(qh feasible_string, start, (size_t)(t-start));
    +        qh_option("Halfspace-about", NULL, NULL);
    +        qh_option(qh feasible_string, NULL, NULL);
    +      }else
    +        qh_option("Halfspace", NULL, NULL);
    +      break;
    +    case 'R':
    +      if (!isdigit(*s))
    +        qh_fprintf(qh ferr, 7007, "qhull warning: missing random perturbation for option 'Rn'.  Ignored\n");
    +      else {
    +        qh RANDOMfactor= qh_strtod(s, &s);
    +        qh_option("Random_perturb", NULL, &qh RANDOMfactor);
    +        qh RANDOMdist= True;
    +      }
    +      break;
    +    case 'V':
    +      if (!isdigit(*s) && *s != '-')
    +        qh_fprintf(qh ferr, 7008, "qhull warning: missing visible distance for option 'Vn'.  Ignored\n");
    +      else {
    +        qh MINvisible= qh_strtod(s, &s);
    +        qh_option("Visible", NULL, &qh MINvisible);
    +      }
    +      break;
    +    case 'U':
    +      if (!isdigit(*s) && *s != '-')
    +        qh_fprintf(qh ferr, 7009, "qhull warning: missing coplanar distance for option 'Un'.  Ignored\n");
    +      else {
    +        qh MAXcoplanar= qh_strtod(s, &s);
    +        qh_option("U-coplanar", NULL, &qh MAXcoplanar);
    +      }
    +      break;
    +    case 'W':
    +      if (*s == '-')
    +        qh_fprintf(qh ferr, 7010, "qhull warning: negative outside width for option 'Wn'.  Ignored.\n");
    +      else if (!isdigit(*s))
    +        qh_fprintf(qh ferr, 7011, "qhull warning: missing outside width for option 'Wn'.  Ignored\n");
    +      else {
    +        qh MINoutside= qh_strtod(s, &s);
    +        qh_option("W-outside", NULL, &qh MINoutside);
    +        qh APPROXhull= True;
    +      }
    +      break;
    +    /************  sub menus ***************/
    +    case 'F':
    +      while (*s && !isspace(*s)) {
    +        switch (*s++) {
    +        case 'a':
    +          qh_option("Farea", NULL, NULL);
    +          qh_appendprint(qh_PRINTarea);
    +          qh GETarea= True;
    +          break;
    +        case 'A':
    +          qh_option("FArea-total", NULL, NULL);
    +          qh GETarea= True;
    +          break;
    +        case 'c':
    +          qh_option("Fcoplanars", NULL, NULL);
    +          qh_appendprint(qh_PRINTcoplanars);
    +          break;
    +        case 'C':
    +          qh_option("FCentrums", NULL, NULL);
    +          qh_appendprint(qh_PRINTcentrums);
    +          break;
    +        case 'd':
    +          qh_option("Fd-cdd-in", NULL, NULL);
    +          qh CDDinput= True;
    +          break;
    +        case 'D':
    +          qh_option("FD-cdd-out", NULL, NULL);
    +          qh CDDoutput= True;
    +          break;
    +        case 'F':
    +          qh_option("FFacets-xridge", NULL, NULL);
    +          qh_appendprint(qh_PRINTfacets_xridge);
    +          break;
    +        case 'i':
    +          qh_option("Finner", NULL, NULL);
    +          qh_appendprint(qh_PRINTinner);
    +          break;
    +        case 'I':
    +          qh_option("FIDs", NULL, NULL);
    +          qh_appendprint(qh_PRINTids);
    +          break;
    +        case 'm':
    +          qh_option("Fmerges", NULL, NULL);
    +          qh_appendprint(qh_PRINTmerges);
    +          break;
    +        case 'M':
    +          qh_option("FMaple", NULL, NULL);
    +          qh_appendprint(qh_PRINTmaple);
    +          break;
    +        case 'n':
    +          qh_option("Fneighbors", NULL, NULL);
    +          qh_appendprint(qh_PRINTneighbors);
    +          break;
    +        case 'N':
    +          qh_option("FNeighbors-vertex", NULL, NULL);
    +          qh_appendprint(qh_PRINTvneighbors);
    +          break;
    +        case 'o':
    +          qh_option("Fouter", NULL, NULL);
    +          qh_appendprint(qh_PRINTouter);
    +          break;
    +        case 'O':
    +          if (qh PRINToptions1st) {
    +            qh_option("FOptions", NULL, NULL);
    +            qh_appendprint(qh_PRINToptions);
    +          }else
    +            qh PRINToptions1st= True;
    +          break;
    +        case 'p':
    +          qh_option("Fpoint-intersect", NULL, NULL);
    +          qh_appendprint(qh_PRINTpointintersect);
    +          break;
    +        case 'P':
    +          qh_option("FPoint-nearest", NULL, NULL);
    +          qh_appendprint(qh_PRINTpointnearest);
    +          break;
    +        case 'Q':
    +          qh_option("FQhull", NULL, NULL);
    +          qh_appendprint(qh_PRINTqhull);
    +          break;
    +        case 's':
    +          qh_option("Fsummary", NULL, NULL);
    +          qh_appendprint(qh_PRINTsummary);
    +          break;
    +        case 'S':
    +          qh_option("FSize", NULL, NULL);
    +          qh_appendprint(qh_PRINTsize);
    +          qh GETarea= True;
    +          break;
    +        case 't':
    +          qh_option("Ftriangles", NULL, NULL);
    +          qh_appendprint(qh_PRINTtriangles);
    +          break;
    +        case 'v':
    +          /* option set in qh_initqhull_globals */
    +          qh_appendprint(qh_PRINTvertices);
    +          break;
    +        case 'V':
    +          qh_option("FVertex-average", NULL, NULL);
    +          qh_appendprint(qh_PRINTaverage);
    +          break;
    +        case 'x':
    +          qh_option("Fxtremes", NULL, NULL);
    +          qh_appendprint(qh_PRINTextremes);
    +          break;
    +        default:
    +          s--;
    +          qh_fprintf(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_PRINTgeom);
    +      while (*s && !isspace(*s)) {
    +        switch (*s++) {
    +        case 'a':
    +          qh_option("Gall-points", NULL, NULL);
    +          qh PRINTdots= True;
    +          break;
    +        case 'c':
    +          qh_option("Gcentrums", NULL, NULL);
    +          qh PRINTcentrums= True;
    +          break;
    +        case 'h':
    +          qh_option("Gintersections", NULL, NULL);
    +          qh DOintersections= True;
    +          break;
    +        case 'i':
    +          qh_option("Ginner", NULL, NULL);
    +          qh PRINTinner= True;
    +          break;
    +        case 'n':
    +          qh_option("Gno-planes", NULL, NULL);
    +          qh PRINTnoplanes= True;
    +          break;
    +        case 'o':
    +          qh_option("Gouter", NULL, NULL);
    +          qh PRINTouter= True;
    +          break;
    +        case 'p':
    +          qh_option("Gpoints", NULL, NULL);
    +          qh PRINTcoplanar= True;
    +          break;
    +        case 'r':
    +          qh_option("Gridges", NULL, NULL);
    +          qh PRINTridges= True;
    +          break;
    +        case 't':
    +          qh_option("Gtransparent", NULL, NULL);
    +          qh PRINTtransparent= True;
    +          break;
    +        case 'v':
    +          qh_option("Gvertices", NULL, NULL);
    +          qh PRINTspheres= True;
    +          break;
    +        case 'D':
    +          if (!isdigit(*s))
    +            qh_fprintf(qh ferr, 6035, "qhull input error: missing dimension for option 'GDn'\n");
    +          else {
    +            if (qh DROPdim >= 0)
    +              qh_fprintf(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("GDrop-dim", &qh DROPdim, NULL);
    +          }
    +          break;
    +        default:
    +          s--;
    +          qh_fprintf(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("Pdrop-facets-dim-less", &i, &r);
    +          else
    +            qh_option("PDrop-facets-dim-more", &i, &r);
    +          break;
    +        case 'g':
    +          qh_option("Pgood-facets", NULL, NULL);
    +          qh PRINTgood= True;
    +          break;
    +        case 'G':
    +          qh_option("PGood-facet-neighbors", NULL, NULL);
    +          qh PRINTneighbors= True;
    +          break;
    +        case 'o':
    +          qh_option("Poutput-forced", NULL, NULL);
    +          qh FORCEoutput= True;
    +          break;
    +        case 'p':
    +          qh_option("Pprecision-ignore", NULL, NULL);
    +          qh PRINTprecision= False;
    +          break;
    +        case 'A':
    +          if (!isdigit(*s))
    +            qh_fprintf(qh ferr, 6036, "qhull input error: missing facet count for keep area option 'PAn'\n");
    +          else {
    +            qh KEEParea= qh_strtol(s, &s);
    +            qh_option("PArea-keep", &qh KEEParea, NULL);
    +            qh GETarea= True;
    +          }
    +          break;
    +        case 'F':
    +          if (!isdigit(*s))
    +            qh_fprintf(qh ferr, 6037, "qhull input error: missing facet area for option 'PFn'\n");
    +          else {
    +            qh KEEPminArea= qh_strtod(s, &s);
    +            qh_option("PFacet-area-keep", NULL, &qh KEEPminArea);
    +            qh GETarea= True;
    +          }
    +          break;
    +        case 'M':
    +          if (!isdigit(*s))
    +            qh_fprintf(qh ferr, 6038, "qhull input error: missing merge count for option 'PMn'\n");
    +          else {
    +            qh KEEPmerge= qh_strtol(s, &s);
    +            qh_option("PMerge-keep", &qh KEEPmerge, NULL);
    +          }
    +          break;
    +        default:
    +          s--;
    +          qh_fprintf(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("QbBound-unit-box", NULL, &r);
    +            break;
    +          }
    +          if (key == 'b' && *s == 'b') {
    +            s++;
    +            qh SCALElast= True;
    +            qh_option("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 ferr, 2004, "qh_initflags: project dimension %d\n", k));
    +                  qh_option("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("Qbound-dim-low", &k, &r);
    +            }else {
    +              qh SCALEinput= True;
    +              if (r == 0.0)
    +                r= qh_DEFAULTbox;
    +              qh_option("QBound-dim-high", &k, &r);
    +            }
    +          }
    +          break;
    +        case 'c':
    +          qh_option("Qcoplanar-keep", NULL, NULL);
    +          qh KEEPcoplanar= True;
    +          break;
    +        case 'f':
    +          qh_option("Qfurthest-outside", NULL, NULL);
    +          qh BESToutside= True;
    +          break;
    +        case 'g':
    +          qh_option("Qgood-facets-only", NULL, NULL);
    +          qh ONLYgood= True;
    +          break;
    +        case 'i':
    +          qh_option("Qinterior-keep", NULL, NULL);
    +          qh KEEPinside= True;
    +          break;
    +        case 'm':
    +          qh_option("Qmax-outside-only", NULL, NULL);
    +          qh ONLYmax= True;
    +          break;
    +        case 'r':
    +          qh_option("Qrandom-outside", NULL, NULL);
    +          qh RANDOMoutside= True;
    +          break;
    +        case 's':
    +          qh_option("Qsearch-initial-simplex", NULL, NULL);
    +          qh ALLpoints= True;
    +          break;
    +        case 't':
    +          qh_option("Qtriangulate", NULL, NULL);
    +          qh TRIangulate= True;
    +          break;
    +        case 'T':
    +          qh_option("QTestPoints", NULL, NULL);
    +          if (!isdigit(*s))
    +            qh_fprintf(qh ferr, 6039, "qhull input error: missing number of test points for option 'QTn'\n");
    +          else {
    +            qh TESTpoints= qh_strtol(s, &s);
    +            qh_option("QTestPoints", &qh TESTpoints, NULL);
    +          }
    +          break;
    +        case 'u':
    +          qh_option("QupperDelaunay", NULL, NULL);
    +          qh UPPERdelaunay= True;
    +          break;
    +        case 'v':
    +          qh_option("Qvertex-neighbors-convex", NULL, NULL);
    +          qh TESTvneighbors= True;
    +          break;
    +        case 'x':
    +          qh_option("Qxact-merge", NULL, NULL);
    +          qh MERGEexact= True;
    +          break;
    +        case 'z':
    +          qh_option("Qz-infinity-point", NULL, NULL);
    +          qh ATinfinity= True;
    +          break;
    +        case '0':
    +          qh_option("Q0-no-premerge", NULL, NULL);
    +          qh NOpremerge= True;
    +          break;
    +        case '1':
    +          if (!isdigit(*s)) {
    +            qh_option("Q1-no-angle-sort", NULL, NULL);
    +            qh ANGLEmerge= False;
    +            break;
    +          }
    +          switch (*s++) {
    +          case '0':
    +            qh_option("Q10-no-narrow", NULL, NULL);
    +            qh NOnarrow= True;
    +            break;
    +          case '1':
    +            qh_option("Q11-trinormals Qtriangulate", NULL, NULL);
    +            qh TRInormals= True;
    +            qh TRIangulate= True;
    +            break;
    +          case '2':
    +            qh_option("Q12-no-wide-dup", NULL, NULL);
    +            qh NOwide= True;
    +            break;
    +          default:
    +            s--;
    +            qh_fprintf(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("Q2-no-merge-independent", NULL, NULL);
    +          qh MERGEindependent= False;
    +          goto LABELcheckdigit;
    +          break; /* no warnings */
    +        case '3':
    +          qh_option("Q3-no-merge-vertices", NULL, NULL);
    +          qh MERGEvertices= False;
    +        LABELcheckdigit:
    +          if (isdigit(*s))
    +            qh_fprintf(qh ferr, 7017, "qhull warning: can not follow '1', '2', or '3' with a digit.  '%c' skipped.\n",
    +                     *s++);
    +          break;
    +        case '4':
    +          qh_option("Q4-avoid-old-into-new", NULL, NULL);
    +          qh AVOIDold= True;
    +          break;
    +        case '5':
    +          qh_option("Q5-no-check-outer", NULL, NULL);
    +          qh SKIPcheckmax= True;
    +          break;
    +        case '6':
    +          qh_option("Q6-no-concave-merge", NULL, NULL);
    +          qh SKIPconvex= True;
    +          break;
    +        case '7':
    +          qh_option("Q7-no-breadth-first", NULL, NULL);
    +          qh VIRTUALmemory= True;
    +          break;
    +        case '8':
    +          qh_option("Q8-no-near-inside", NULL, NULL);
    +          qh NOnearinside= True;
    +          break;
    +        case '9':
    +          qh_option("Q9-pick-furthest", NULL, NULL);
    +          qh PICKfurthest= True;
    +          break;
    +        case 'G':
    +          i= qh_strtol(s, &t);
    +          if (qh GOODpoint)
    +            qh_fprintf(qh ferr, 7018, "qhull warning: good point already defined for option 'QGn'.  Ignored\n");
    +          else if (s == t)
    +            qh_fprintf(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("QGood-if-dont-see-point", &i, NULL);
    +          }else {
    +            qh GOODpoint= i+1;
    +            qh_option("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("QJoggle", NULL, &qh JOGGLEmax);
    +          }
    +          break;
    +        case 'R':
    +          if (!isdigit(*s) && *s != '-')
    +            qh_fprintf(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("QRotate-id", &i, NULL );
    +            else if (i < -1)
    +              qh_option("QRandom-seed", &i, NULL );
    +          }
    +          break;
    +        case 'V':
    +          i= qh_strtol(s, &t);
    +          if (qh GOODvertex)
    +            qh_fprintf(qh ferr, 7021, "qhull warning: good vertex already defined for option 'QVn'.  Ignored\n");
    +          else if (s == t)
    +            qh_fprintf(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("QV-good-facets-not-point", &i, NULL);
    +          }else {
    +            qh_option("QV-good-facets-point", &i, NULL);
    +            qh GOODvertex= i + 1;
    +          }
    +          s= t;
    +          break;
    +        default:
    +          s--;
    +          qh_fprintf(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("Tannotate-output", NULL, NULL);
    +          qh ANNOTATEoutput= True;
    +          break;
    +        case 'c':
    +          qh_option("Tcheck-frequently", NULL, NULL);
    +          qh CHECKfrequently= True;
    +          break;
    +        case 's':
    +          qh_option("Tstatistics", NULL, NULL);
    +          qh PRINTstatistics= True;
    +          break;
    +        case 'v':
    +          qh_option("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("Tz-stdout", NULL, NULL);
    +            qh USEstdout= True;
    +          }else if (!qh fout)
    +            qh_fprintf(qh ferr, 7024, "qhull warning: output file undefined(stdout).  Option 'Tz' ignored.\n");
    +          else {
    +            qh_option("Tz-stdout", NULL, NULL);
    +            qh USEstdout= True;
    +            qh ferr= qh fout;
    +            qhmem.ferr= qh fout;
    +          }
    +          break;
    +        case 'C':
    +          if (!isdigit(*s))
    +            qh_fprintf(qh ferr, 7025, "qhull warning: missing point id for cone for trace option 'TCn'.  Ignored\n");
    +          else {
    +            i= qh_strtol(s, &s);
    +            qh_option("TCone-stop", &i, NULL);
    +            qh STOPcone= i + 1;
    +          }
    +          break;
    +        case 'F':
    +          if (!isdigit(*s))
    +            qh_fprintf(qh ferr, 7026, "qhull warning: missing frequency count for trace option 'TFn'.  Ignored\n");
    +          else {
    +            qh REPORTfreq= qh_strtol(s, &s);
    +            qh_option("TFacet-log", &qh REPORTfreq, NULL);
    +            qh REPORTfreq2= qh REPORTfreq/2;  /* for tracemerging() */
    +          }
    +          break;
    +        case 'I':
    +          if (!isspace(*s))
    +            qh_fprintf(qh ferr, 7027, "qhull warning: missing space between 'TI' and filename, %s\n", s);
    +          while (isspace(*s))
    +            s++;
    +          t= qh_skipfilename(s);
    +          {
    +            char filename[qh_FILENAMElen];
    +
    +            qh_copyfilename(filename, (int)sizeof(filename), s, (int)(t-s));   /* WARN64 */
    +            s= t;
    +            if (!freopen(filename, "r", stdin)) {
    +              qh_fprintf(qh ferr, 6041, "qhull error: could not open file \"%s\".", filename);
    +              qh_errexit(qh_ERRinput, NULL, NULL);
    +            }else {
    +              qh_option("TInput-file", NULL, NULL);
    +              qh_option(filename, NULL, NULL);
    +            }
    +          }
    +          break;
    +        case 'O':
    +            if (!isspace(*s))
    +                qh_fprintf(qh ferr, 7028, "qhull warning: missing space between 'TO' and filename, %s\n", s);
    +            while (isspace(*s))
    +                s++;
    +            t= qh_skipfilename(s);
    +            {
    +              char filename[qh_FILENAMElen];
    +
    +              qh_copyfilename(filename, (int)sizeof(filename), s, (int)(t-s));  /* WARN64 */
    +              s= t;
    +              if (!qh fout) {
    +                qh_fprintf(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 ferr, 6044, "qhull error: could not open file \"%s\".", filename);
    +                qh_errexit(qh_ERRinput, NULL, NULL);
    +              }else {
    +                qh_option("TOutput-file", NULL, NULL);
    +              qh_option(filename, NULL, NULL);
    +            }
    +          }
    +          break;
    +        case 'P':
    +          if (!isdigit(*s))
    +            qh_fprintf(qh ferr, 7029, "qhull warning: missing point id for trace option 'TPn'.  Ignored\n");
    +          else {
    +            qh TRACEpoint= qh_strtol(s, &s);
    +            qh_option("Trace-point", &qh TRACEpoint, NULL);
    +          }
    +          break;
    +        case 'M':
    +          if (!isdigit(*s))
    +            qh_fprintf(qh ferr, 7030, "qhull warning: missing merge id for trace option 'TMn'.  Ignored\n");
    +          else {
    +            qh TRACEmerge= qh_strtol(s, &s);
    +            qh_option("Trace-merge", &qh TRACEmerge, NULL);
    +          }
    +          break;
    +        case 'R':
    +          if (!isdigit(*s))
    +            qh_fprintf(qh ferr, 7031, "qhull warning: missing rerun count for trace option 'TRn'.  Ignored\n");
    +          else {
    +            qh RERUN= qh_strtol(s, &s);
    +            qh_option("TRerun", &qh RERUN, NULL);
    +          }
    +          break;
    +        case 'V':
    +          i= qh_strtol(s, &t);
    +          if (s == t)
    +            qh_fprintf(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("TV-stop-before-point", &i, NULL);
    +          }else {
    +            qh STOPpoint= i + 1;
    +            qh_option("TV-stop-after-point", &i, NULL);
    +          }
    +          s= t;
    +          break;
    +        case 'W':
    +          if (!isdigit(*s))
    +            qh_fprintf(qh ferr, 7033, "qhull warning: missing max width for trace option 'TWn'.  Ignored\n");
    +          else {
    +            qh TRACEdist= (realT) qh_strtod(s, &s);
    +            qh_option("TWide-trace", NULL, &qh TRACEdist);
    +          }
    +          break;
    +        default:
    +          s--;
    +          qh_fprintf(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 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 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 ferr, 7078, "qhull warning: 'TCn' (stopCone) ignored when used with 'QJn' (joggle)\n");
    +  if (isgeom && !qh FORCEoutput && qh PRINTout[1])
    +    qh_fprintf(qh ferr, 7037, "qhull warning: additional output formats are not compatible with Geomview\n");
    +  /* set derived values in qh_initqhull_globals */
    +} /* initflags */
    +
    +
    +/*---------------------------------
    +
    +  qh_initqhull_buffers()
    +    initialize global memory buffers
    +
    +  notes:
    +    must match qh_freebuffers()
    +*/
    +void qh_initqhull_buffers(void) {
    +  int k;
    +
    +  qh TEMPsize= (qhmem.LASTsize - sizeof(setT))/SETelemsize;
    +  if (qh TEMPsize <= 0 || qh TEMPsize > qhmem.LASTsize)
    +    qh TEMPsize= 8;  /* e.g., if qh_NOmem */
    +  qh other_points= qh_setnew(qh TEMPsize);
    +  qh del_vertices= qh_setnew(qh TEMPsize);
    +  qh coplanarfacetset= qh_setnew(qh TEMPsize);
    +  qh NEARzero= (realT *)qh_memalloc(qh hull_dim * sizeof(realT));
    +  qh lower_threshold= (realT *)qh_memalloc((qh input_dim+1) * sizeof(realT));
    +  qh upper_threshold= (realT *)qh_memalloc((qh input_dim+1) * sizeof(realT));
    +  qh lower_bound= (realT *)qh_memalloc((qh input_dim+1) * sizeof(realT));
    +  qh upper_bound= (realT *)qh_memalloc((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 hull_dim+1) * qh hull_dim * sizeof(coordT));
    +  qh gm_row= (coordT **)qh_memalloc((qh hull_dim+1) * sizeof(coordT *));
    +} /* initqhull_buffers */
    +
    +/*---------------------------------
    +
    +  qh_initqhull_globals( 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(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 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("_pre-merge", NULL, NULL);
    +    }else {
    +      qh MERGEexact= True;
    +      qh_option("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 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("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("_zero-centrum", NULL, NULL);
    +  }
    +  if (qh JOGGLEmax < REALmax/2 && REALepsilon > 2e-8 && qh PRINTprecision)
    +    qh_fprintf(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 ferr, 6045, "qhull input error: merging not installed(qh_NOmerge + 'Qx', 'Cn' or 'An')\n");
    +    qh_errexit(qh_ERRinput, NULL, NULL);
    +  }
    +#endif
    +  if (qh DELAUNAY && qh KEEPcoplanar && !qh KEEPinside) {
    +    qh KEEPinside= True;
    +    qh_option("Qinterior-keep", NULL, NULL);
    +  }
    +  if (qh DELAUNAY && qh HALFspace) {
    +    qh_fprintf(qh ferr, 6046, "qhull input error: can not use Delaunay('d') or Voronoi('v') with halfspace intersection('H')\n");
    +    qh_errexit(qh_ERRinput, NULL, NULL);
    +  }
    +  if (!qh DELAUNAY && (qh UPPERdelaunay || qh ATinfinity)) {
    +    qh_fprintf(qh ferr, 6047, "qhull input error: use upper-Delaunay('Qu') or infinity-point('Qz') with Delaunay('d') or Voronoi('v')\n");
    +    qh_errexit(qh_ERRinput, NULL, NULL);
    +  }
    +  if (qh UPPERdelaunay && qh ATinfinity) {
    +    qh_fprintf(qh ferr, 6048, "qhull input error: can not use infinity-point('Qz') with upper-Delaunay('Qu')\n");
    +    qh_errexit(qh_ERRinput, NULL, NULL);
    +  }
    +  if (qh SCALElast && !qh DELAUNAY && qh PRINTprecision)
    +    qh_fprintf(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 ferr, 6049, "qhull input error: test vertex neighbors('Qv') needs a merge option\n");
    +    qh_errexit(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 ferr, 6050, "qhull error: dimension %d must be > 1\n", qh hull_dim);
    +    qh_errexit(qh_ERRinput, NULL, NULL);
    +  }
    +  for (k=2, factorial=1.0; k < qh hull_dim; k++)
    +    factorial *= k;
    +  qh AREAfactor= 1.0 / factorial;
    +  trace2((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("Q3-no-merge-vertices-dim-high", NULL, NULL);
    +  }
    +  if (qh GOODpoint)
    +    pointsneeded++;
    +#ifdef qh_NOtrace
    +  if (qh IStracing) {
    +    qh_fprintf(qh ferr, 6051, "qhull input error: tracing is not installed(qh_NOtrace in user.h)");
    +    qh_errexit(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("QRandom-seed", &seed, NULL );
    +    }else
    +      qh_option("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_(seed);
    +  randr= 0.0;
    +  for (i=1000; i--; ) {
    +    randi= qh_RANDOMint;
    +    randr += randi;
    +    if (randi > qh_RANDOMmax) {
    +      qh_fprintf(qh ferr, 8036, "\
    +qhull configuration error (qh_RANDOMmax in user.h):\n\
    +   random integer %d > qh_RANDOMmax(%.8g)\n",
    +               randi, qh_RANDOMmax);
    +      qh_errexit(qh_ERRinput, NULL, NULL);
    +    }
    +  }
    +  qh_RANDOMseed_(seed);
    +  randr = randr/1000;
    +  if (randr < qh_RANDOMmax * 0.1
    +  || randr > qh_RANDOMmax * 0.9)
    +    qh_fprintf(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 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_ERRqhull, NULL, NULL);
    +  }
    +  if (numpoints+extra < pointsneeded) {
    +    qh_fprintf(qh ferr, 6214, "qhull input error: not enough points(%d) to construct initial simplex (need %d)\n",
    +            numpoints, pointsneeded);
    +    qh_errexit(qh_ERRinput, NULL, NULL);
    +  }
    +  qh_initqhull_outputflags();
    +} /* initqhull_globals */
    +
    +/*---------------------------------
    +
    +  qh_initqhull_mem(  )
    +    initialize mem.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() to add up to 10 additional sizes for quick allocation
    +      (see numsizes below)
    +
    +  returns:
    +    mem.c already for qh_memalloc/qh_memfree (errors if called beforehand)
    +
    +  notes:
    +    qh_produceoutput() prints memsizes
    +
    +*/
    +void qh_initqhull_mem(void) {
    +  int numsizes;
    +  int i;
    +
    +  numsizes= 8+10;
    +  qh_meminitbuffers(qh IStracing, qh_MEMalign, numsizes,
    +                     qh_MEMbufsize,qh_MEMinitbuf);
    +  qh_memsize((int)sizeof(vertexT));
    +  if (qh MERGING) {
    +    qh_memsize((int)sizeof(ridgeT));
    +    qh_memsize((int)sizeof(mergeT));
    +  }
    +  qh_memsize((int)sizeof(facetT));
    +  i= sizeof(setT) + (qh hull_dim - 1) * SETelemsize;  /* ridge.vertices */
    +  qh_memsize(i);
    +  qh_memsize(qh normal_size);        /* normal */
    +  i += SETelemsize;                 /* facet.vertices, .ridges, .neighbors */
    +  qh_memsize(i);
    +  qh_user_memsizes();
    +  qh_memsetup();
    +} /* initqhull_mem */
    +
    +/*---------------------------------
    +
    +  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(void) {
    +  boolT printgeom= False, printmath= False, printcoplanar= False;
    +  int i;
    +
    +  trace3((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("Pgood", NULL, NULL);
    +    }
    +  }
    +  if (qh PRINTtransparent) {
    +    if (qh hull_dim != 4 || !qh DELAUNAY || qh VORONOI || qh DROPdim >= 0) {
    +      qh_fprintf(qh ferr, 6215, "qhull input error: transparent Delaunay('Gt') needs 3-d Delaunay('d') w/o 'GDn'\n");
    +      qh_errexit(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 ferr, 6053, "qhull input error: option 'Fp' is only used for \nhalfspace intersection('Hn,n,n').\n");
    +      qh_errexit(qh_ERRinput, NULL, NULL);
    +    }else if (qh PRINTout[i] == qh_PRINTtriangles && (qh HALFspace || qh VORONOI)) {
    +      qh_fprintf(qh ferr, 6054, "qhull input error: option 'Ft' is not available for Voronoi vertices or halfspace intersection\n");
    +      qh_errexit(qh_ERRinput, NULL, NULL);
    +    }else if (qh PRINTout[i] == qh_PRINTcentrums && qh VORONOI) {
    +      qh_fprintf(qh ferr, 6055, "qhull input error: option 'FC' is not available for Voronoi vertices('v')\n");
    +      qh_errexit(qh_ERRinput, NULL, NULL);
    +    }else if (qh PRINTout[i] == qh_PRINTvertices) {
    +      if (qh VORONOI)
    +        qh_option("Fvoronoi", NULL, NULL);
    +      else
    +        qh_option("Fvertices", NULL, NULL);
    +    }
    +  }
    +  if (printcoplanar && qh DELAUNAY && qh JOGGLEmax < REALmax/2) {
    +    if (qh PRINTprecision)
    +      qh_fprintf(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 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_ERRinput, NULL, NULL);
    +  }
    +  if (printgeom) {
    +    if (qh hull_dim > 4) {
    +      qh_fprintf(qh ferr, 6057, "qhull input error: Geomview output is only available for 2-d, 3-d and 4-d\n");
    +      qh_errexit(qh_ERRinput, NULL, NULL);
    +    }
    +    if (qh PRINTnoplanes && !(qh PRINTcoplanar + qh PRINTcentrums
    +     + qh PRINTdots + qh PRINTspheres + qh DOintersections + qh PRINTridges)) {
    +      qh_fprintf(qh ferr, 6058, "qhull input error: no output specified for Geomview\n");
    +      qh_errexit(qh_ERRinput, NULL, NULL);
    +    }
    +    if (qh VORONOI && (qh hull_dim > 3 || qh DROPdim >= 0)) {
    +      qh_fprintf(qh ferr, 6059, "qhull input error: Geomview output for Voronoi diagrams only for 2-d\n");
    +      qh_errexit(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 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 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("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 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 */
    +
    +/*---------------------------------
    +
    +  qh_initqhull_start( infile, outfile, errfile )
    +    allocate memory if needed and call qh_initqhull_start2()
    +*/
    +void qh_initqhull_start(FILE *infile, FILE *outfile, FILE *errfile) {
    +
    +#if qh_QHpointer
    +  if (qh_qh) {
    +    qh_fprintf(errfile, 6205, "qhull error (qh_initqhull_start): qh_qh already defined.  Call qh_save_qhull() first\n");
    +    qh_exit(qh_ERRqhull);  /* no error handler */
    +  }
    +  if (!(qh_qh= (qhT *)qh_malloc(sizeof(qhT)))) {
    +    qh_fprintf(errfile, 6060, "qhull error (qh_initqhull_start): insufficient memory\n");
    +    qh_exit(qh_ERRmem);  /* no error handler */
    +  }
    +#endif
    +  qh_initstatistics();
    +  qh_initqhull_start2(infile, outfile, errfile);
    +} /* initqhull_start */
    +
    +/*---------------------------------
    +
    +  qh_initqhull_start2( infile, outfile, errfile )
    +    start initialization of qhull
    +    initialize statistics, stdio, default values for global variables
    +    assumes qh_qh is defined
    +  notes:
    +    report errors elsewhere, error handling and g_qhull_output [Qhull.cpp, QhullQh()] not in initialized
    +  see:
    +    qh_maxmin() determines the precision constants
    +    qh_freeqhull2()
    +*/
    +void qh_initqhull_start2(FILE *infile, FILE *outfile, FILE *errfile) {
    +  time_t timedata;
    +  int seed;
    +
    +  qh_CPUclock; /* start the clock(for qh_clock).  One-shot. */
    +#if qh_QHpointer
    +  memset((char *)qh_qh, 0, sizeof(qhT));   /* every field is 0, FALSE, NULL */
    +#else
    +  memset((char *)&qh_qh, 0, sizeof(qhT));
    +#endif
    +  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 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_(seed);
    +  qh run_id= qh_RANDOMint;
    +  if(!qh run_id)
    +      qh run_id++;  /* guarantee non-zero */
    +  qh_option("run-id", &qh run_id, NULL);
    +  strcat(qh qhull, "qhull");
    +} /* initqhull_start2 */
    +
    +/*---------------------------------
    +
    +  qh_initthresholds( 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(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 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 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 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 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 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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +        if (qh_QHpointer) {
    +            qh_fprintf_stderr(6246, "qh_lib_check: Incorrect qhull library called.  Caller uses a static qhT while library uses a dynamic qhT via qh_QHpointer.  Both caller and library are non-reentrant.\n");
    +            iserror= True;
    +        }
    +    }else if (qhullLibraryType==QHULL_QH_POINTER) { /* 1 */
    +        if (!qh_QHpointer) {
    +            qh_fprintf_stderr(6247, "qh_lib_check: Incorrect qhull library called.  Caller uses a dynamic qhT via qh_QHpointer while library uses a static qhT.  Both caller and library are non-reentrant.\n");
    +            iserror= True;
    +        }
    +    }else if (qhullLibraryType==QHULL_REENTRANT) { /* 2 */
    +        qh_fprintf_stderr(6248, "qh_lib_check: Incorrect qhull library called.  Caller uses reentrant Qhull while library is non-reentrant\n");
    +        iserror= True;
    +    }else{
    +        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) {
    +        if(qh_QHpointer){
    +            qh_fprintf_stderr(6255, "qh_lib_check: Cannot continue.  Library '%s' uses a dynamic qhT via qh_QHpointer (e.g., qhull_p.so)\n", qh_version2);
    +        }else{
    +            qh_fprintf_stderr(6256, "qh_lib_check: Cannot continue.  Library '%s' uses a static qhT (e.g., libqhull.so)\n", qh_version2);
    +        }
    +        qh_exit(qh_ERRqhull);  /* can not use qh_errexit() */
    +    }
    +} /* lib_check */
    +
    +/*---------------------------------
    +
    +  qh_option( 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(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 */
    +
    +#if qh_QHpointer
    +/*---------------------------------
    +
    +  qh_restore_qhull( oldqh )
    +    restores a previously saved qhull
    +    also restores qh_qhstat and qhmem.tempstack
    +    Sets *oldqh to NULL
    +  notes:
    +    errors if current qhull hasn't been saved or freed
    +    uses qhmem for error reporting
    +
    +  NOTE 1998/5/11:
    +    Freeing memory after qh_save_qhull and qh_restore_qhull
    +    is complicated.  The procedures will be redesigned.
    +
    +  see:
    +    qh_save_qhull(), UsingLibQhull
    +*/
    +void qh_restore_qhull(qhT **oldqh) {
    +
    +  if (*oldqh && strcmp((*oldqh)->qhull, "qhull")) {
    +    qh_fprintf(qhmem.ferr, 6061, "qhull internal error (qh_restore_qhull): %p is not a qhull data structure\n",
    +                  *oldqh);
    +    qh_errexit(qh_ERRqhull, NULL, NULL);
    +  }
    +  if (qh_qh) {
    +    qh_fprintf(qhmem.ferr, 6062, "qhull internal error (qh_restore_qhull): did not save or free existing qhull\n");
    +    qh_errexit(qh_ERRqhull, NULL, NULL);
    +  }
    +  if (!*oldqh || !(*oldqh)->old_qhstat) {
    +    qh_fprintf(qhmem.ferr, 6063, "qhull internal error (qh_restore_qhull): did not previously save qhull %p\n",
    +                  *oldqh);
    +    qh_errexit(qh_ERRqhull, NULL, NULL);
    +  }
    +  qh_qh= *oldqh;
    +  *oldqh= NULL;
    +  qh_qhstat= qh old_qhstat;
    +  qhmem.tempstack= qh old_tempstack;
    +  qh old_qhstat= 0;
    +  qh old_tempstack= 0;
    +  trace1((qh ferr, 1007, "qh_restore_qhull: restored qhull from %p\n", *oldqh));
    +} /* restore_qhull */
    +
    +/*---------------------------------
    +
    +  qh_save_qhull(  )
    +    saves qhull for a later qh_restore_qhull
    +    also saves qh_qhstat and qhmem.tempstack
    +
    +  returns:
    +    qh_qh=NULL
    +
    +  notes:
    +    need to initialize qhull or call qh_restore_qhull before continuing
    +
    +  NOTE 1998/5/11:
    +    Freeing memory after qh_save_qhull and qh_restore_qhull
    +    is complicated.  The procedures will be redesigned.
    +
    +  see:
    +    qh_restore_qhull()
    +*/
    +qhT *qh_save_qhull(void) {
    +  qhT *oldqh;
    +
    +  trace1((qhmem.ferr, 1045, "qh_save_qhull: save qhull %p\n", qh_qh));
    +  if (!qh_qh) {
    +    qh_fprintf(qhmem.ferr, 6064, "qhull internal error (qh_save_qhull): qhull not initialized\n");
    +    qh_errexit(qh_ERRqhull, NULL, NULL);
    +  }
    +  qh old_qhstat= qh_qhstat;
    +  qh_qhstat= NULL;
    +  qh old_tempstack= qhmem.tempstack;
    +  qhmem.tempstack= NULL;
    +  oldqh= qh_qh;
    +  qh_qh= NULL;
    +  return oldqh;
    +} /* save_qhull */
    +
    +#endif
    +
    diff --git a/xs/src/qhull/src/libqhull/index.htm b/xs/src/qhull/src/libqhull/index.htm
    new file mode 100644
    index 000000000..62b9d9970
    --- /dev/null
    +++ b/xs/src/qhull/src/libqhull/index.htm
    @@ -0,0 +1,264 @@
    +
    +
    +
    +
    +Qhull functions, macros, and data structures
    +
    +
    +
    +
    +

    Up: Home page for Qhull
    +Up: Qhull manual: Table of Contents
    +Up: Programs +• Options +• Output +• Formats +• Geomview +• Print +• Qhull +• Precision +• Trace +• Functions
    +Up: Qhull code
    +To: Qhull files
    +To: GeomGlobal +• IoMem +• MergePoly +• QhullSet +• StatUser + +


    + + +

    Qhull functions, macros, and data structures

    +
    +

    The following sections provide an overview and index to +Qhull's functions, macros, and data structures. +Each section starts with an introduction. +See also Calling +Qhull from C programs and Calling Qhull from C++ programs.

    + +

    Qhull uses the following conventions:

    +
    + +
      +
    • in code, global variables start with "qh " +
    • in documentation, global variables start with 'qh.' +
    • constants start with an upper case word +
    • important globals include an '_' +
    • functions, macros, and constants start with "qh_"
    • +
    • data types end in "T"
    • +
    • macros with arguments end in "_"
    • +
    • iterators are macros that use local variables
    • +
    • iterators for sets start with "FOREACH"
    • +
    • iterators for lists start with "FORALL"
    • +
    • qhull options are in single quotes (e.g., 'Pdn')
    • +
    • lists are sorted alphabetically
    • +
    • preprocessor directives on left margin for older compilers
    • +
    +
    +

    +When reading the code, please note that the +global data structure, 'qh', is a macro. It +either expands to "qh_qh." or to +"qh_qh->". The later is used for +applications which run concurrent calls to qh_qhull(). +

    +When reading code with an editor, a search for +'"function' +will locate the header of qh_function. A search for '* function' +will locate the tail of qh_function. + +

    A useful starting point is libqhull.h. It defines most +of Qhull data structures and top-level functions. Search for 'PFn' to +determine the corresponding constant in Qhull. Search for 'Fp' to +determine the corresponding qh_PRINT... constant. +Search io.c to learn how the print function is implemented.

    + +

    If your web browser is configured for .c and .h files, the function, macro, and data type links +go to the corresponding source location. To configure your web browser for .c and .h files. +

      +
    • In the Download Preferences or Options panel, add file extensions 'c' and 'h' to mime type 'text/html'. +
    • Opera 12.10 +
        +
      1. In Tools > Preferences > Advanced > Downloads +
      2. Uncheck 'Hide file types opened with Opera' +
      3. Quick find 'html' +
      4. Select 'text/html' > Edit +
      5. Add File extensions 'c,h,' +
      6. Click 'OK' +
      +
    • Internet Explorer -- Mime types are not available from 'Internet Options'. Is there a registry key for these settings? +
    • Firefox -- Mime types are not available from 'Preferences'. Is there an add-on to change the file extensions for a mime type? +
    • Chrome -- Can Chrome be configured? +
    + +

    +Please report documentation and link errors +to qhull-bug@qhull.org. +

    + +

    Copyright © 1997-2015 C.B. Barber

    + +
    + +

    »Qhull files

    +
    + +

    This sections lists the .c and .h files for Qhull. Please +refer to these files for detailed information.

    +
    + +
    +
    Makefile, CMakeLists.txt
    +
    Makefile is preconfigured for gcc. CMakeLists.txt supports multiple +platforms with CMake. +Qhull includes project files for Visual Studio and Qt. +
    + +
     
    +
    libqhull.h
    +
    Include file for the Qhull library (libqhull.so, qhull.dll, libqhullstatic.a). +Data structures are documented under Poly. +Global variables are documented under Global. +Other data structures and variables are documented under +Qhull or Geom.
    + +
     
    +
    Geom, +geom.h, +geom.c, +geom2.c, +random.c, +random.h
    +
    Geometric routines. These routines implement mathematical +functions such as Gaussian elimination and geometric +routines needed for Qhull. Frequently used routines are +in geom.c while infrequent ones are in geom2.c. +
    + +
     
    +
    Global, +global.c, +libqhull.h
    +
    Global routines. Qhull uses a global data structure, qh, +to store globally defined constants, lists, sets, and +variables. +global.c initializes and frees these +structures.
    + +
     
    +
    Io, io.h, +io.c
    +
    Input and output routines. Qhull provides a wide range of +input and output options.
    + +
     
    +
    Mem, +mem.h, +mem.c
    +
    Memory routines. Qhull provides memory allocation and +deallocation. It uses quick-fit allocation.
    + +
     
    +
    Merge, +merge.h, +merge.c
    +
    Merge routines. Qhull handles precision problems by +merged facets or joggled input. These routines merge simplicial facets, +merge non-simplicial facets, merge cycles of facets, and +rename redundant vertices.
    + +
     
    +
    Poly, +poly.h, +poly.c, +poly2.c, +libqhull.h
    +
    Polyhedral routines. Qhull produces a polyhedron as a +list of facets with vertices, neighbors, ridges, and +geometric information. libqhull.h defines the main +data structures. Frequently used routines are in poly.c +while infrequent ones are in poly2.c.
    + +
     
    +
    Qhull, +libqhull.c, +libqhull.h, +qhull_a.h, +unix.c , +qconvex.c , +qdelaun.c , +qhalf.c , +qvoronoi.c
    +
    Top-level routines. The Quickhull algorithm is +implemented by libqhull.c. qhull_a.h +includes all header files.
    + +
     
    +
    Set, +qset.h, +qset.c
    +
    Set routines. Qhull implements its data structures as +sets. A set is an array of pointers that is expanded as +needed. This is a separate package that may be used in +other applications.
    + +
     
    +
    Stat, +stat.h, +stat.c
    +
    Statistical routines. Qhull maintains statistics about +its implementation.
    + +
     
    +
    User, +user.h, +user.c, +user_eg.c, +user_eg2.c, +
    +
    User-defined routines. Qhull allows the user to configure +the code with defined constants and specialized routines. +
    +
    +
    + +
    +

    +
    +

    Up: +Home page for +Qhull
    +Up: Qhull manual: Table of Contents
    +Up: Programs +• Options +• Output +• Formats +• Geomview +• Print +• Qhull +• Precision +• Trace +• Functions
    +Up: Qhull code: Table of Contents
    +To: Qhull files
    +To: Geom • +GlobalIo +• MemMerge +• PolyQhull +• SetStat +• User
    + +

    +
    +

    The +Geometry Center Home Page

    +

    Comments to: qhull@qhull.org +
    +Created: May 2, 1997 --- Last modified: see top

    + + diff --git a/xs/src/qhull/src/libqhull/io.c b/xs/src/qhull/src/libqhull/io.c new file mode 100644 index 000000000..401987ec0 --- /dev/null +++ b/xs/src/qhull/src/libqhull/io.c @@ -0,0 +1,4062 @@ +/*
      ---------------------------------
    +
    +   io.c
    +   Input/Output routines of qhull application
    +
    +   see qh-io.htm and io.h
    +
    +   see user.c for qh_errprint and qh_printfacetlist
    +
    +   unix.c calls qh_readpoints and qh_produce_output
    +
    +   unix.c and user.c are the only callers of io.c functions
    +   This allows the user to avoid loading io.o from qhull.a
    +
    +   Copyright (c) 1993-2015 The Geometry Center.
    +   $Id: //main/2015/qhull/src/libqhull/io.c#5 $$Change: 2064 $
    +   $DateTime: 2016/01/18 12:36:08 $$Author: bbarber $
    +*/
    +
    +#include "qhull_a.h"
    +
    +/*========= -functions in alphabetical order after qh_produce_output()  =====*/
    +
    +/*---------------------------------
    +
    +  qh_produce_output()
    +  qh_produce_output2()
    +    prints out the result of qhull in desired format
    +    qh_produce_output2() does not call qh_prepare_output()
    +    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(void) {
    +    int tempsize= qh_setsize(qhmem.tempstack);
    +
    +    qh_prepare_output();
    +    qh_produce_output2();
    +    if (qh_setsize(qhmem.tempstack) != tempsize) {
    +        qh_fprintf(qh ferr, 6206, "qhull internal error (qh_produce_output): temporary sets not empty(%d)\n",
    +            qh_setsize(qhmem.tempstack));
    +        qh_errexit(qh_ERRqhull, NULL, NULL);
    +    }
    +} /* produce_output */
    +
    +
    +void qh_produce_output2(void) {
    +  int i, tempsize= qh_setsize(qhmem.tempstack), d_1;
    +
    +  if (qh PRINTsummary)
    +    qh_printsummary(qh ferr);
    +  else if (qh PRINTout[0] == qh_PRINTnone)
    +    qh_printsummary(qh fout);
    +  for (i=0; i < qh_PRINTEND; i++)
    +    qh_printfacets(qh fout, qh PRINTout[i], qh facet_list, NULL, !qh_ALL);
    +  qh_allstatistics();
    +  if (qh PRINTprecision && !qh MERGING && (qh JOGGLEmax > REALmax/2 || qh RERUN))
    +    qh_printstats(qh ferr, qhstat precision, NULL);
    +  if (qh VERIFYoutput && (zzval_(Zridge) > 0 || zzval_(Zridgemid) > 0))
    +    qh_printstats(qh ferr, qhstat vridges, NULL);
    +  if (qh PRINTstatistics) {
    +    qh_printstatistics(qh ferr, "");
    +    qh_memstatistics(qh ferr);
    +    d_1= sizeof(setT) + (qh hull_dim - 1) * SETelemsize;
    +    qh_fprintf(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(qhmem.tempstack) != tempsize) {
    +    qh_fprintf(qh ferr, 6065, "qhull internal error (qh_produce_output2): temporary sets not empty(%d)\n",
    +             qh_setsize(qhmem.tempstack));
    +    qh_errexit(qh_ERRqhull, NULL, NULL);
    +  }
    +} /* produce_output2 */
    +
    +/*---------------------------------
    +
    +  qh_dfacet( id )
    +    print facet by id, for debugging
    +
    +*/
    +void qh_dfacet(unsigned id) {
    +  facetT *facet;
    +
    +  FORALLfacets {
    +    if (facet->id == id) {
    +      qh_printfacet(qh fout, facet);
    +      break;
    +    }
    +  }
    +} /* dfacet */
    +
    +
    +/*---------------------------------
    +
    +  qh_dvertex( id )
    +    print vertex by id, for debugging
    +*/
    +void qh_dvertex(unsigned id) {
    +  vertexT *vertex;
    +
    +  FORALLvertices {
    +    if (vertex->id == id) {
    +      qh_printvertex(qh fout, vertex);
    +      break;
    +    }
    +  }
    +} /* dvertex */
    +
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  qh_compare_vertexpoint( p1, p2 )
    +    used by qsort() to order vertices by point id
    +
    +  Not used.  Not available in libqhull_r.h 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(a->point) > qh_pointid(b->point)?1:-1));
    +} /* compare_vertexpoint */
    +
    +/*---------------------------------
    +
    +  qh_copyfilename( dest, size, source, length )
    +    copy filename identified by qh_skipfilename()
    +
    +  notes:
    +    see qh_skipfilename() for syntax
    +*/
    +void qh_copyfilename(char *filename, int size, const char* source, int length) {
    +  char c= *source;
    +
    +  if (length > size + 1) {
    +      qh_fprintf(qh ferr, 6040, "qhull error: filename is more than %d characters, %s\n",  size-1, source);
    +      qh_errexit(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 */
    +
    +/*---------------------------------
    +
    +  qh_countfacets( 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(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(facet)))
    +      facet->visitid= 0;
    +    else {
    +      facet->visitid= ++numfacets;
    +      totneighbors += qh_setsize(facet->neighbors);
    +      if (facet->simplicial) {
    +        numsimplicial++;
    +        if (facet->keepcentrum && facet->tricoplanar)
    +          numtricoplanars++;
    +      }else
    +        numridges += qh_setsize(facet->ridges);
    +      if (facet->coplanarset)
    +        numcoplanars += qh_setsize(facet->coplanarset);
    +    }
    +  }
    +
    +  FOREACHfacet_(facets) {
    +    if ((facet->visible && qh NEWfacets)
    +    || (!printall && qh_skipfacet(facet)))
    +      facet->visitid= 0;
    +    else {
    +      facet->visitid= ++numfacets;
    +      totneighbors += qh_setsize(facet->neighbors);
    +      if (facet->simplicial){
    +        numsimplicial++;
    +        if (facet->keepcentrum && facet->tricoplanar)
    +          numtricoplanars++;
    +      }else
    +        numridges += qh_setsize(facet->ridges);
    +      if (facet->coplanarset)
    +        numcoplanars += qh_setsize(facet->coplanarset);
    +    }
    +  }
    +  qh visit_id += numfacets+1;
    +  *numfacetsp= numfacets;
    +  *numsimplicialp= numsimplicial;
    +  *totneighborsp= totneighbors;
    +  *numridgesp= numridges;
    +  *numcoplanarsp= numcoplanars;
    +  *numtricoplanarsp= numtricoplanars;
    +} /* countfacets */
    +
    +/*---------------------------------
    +
    +  qh_detvnorm( 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.c so that code for 'v Tv' can be removed by removing io.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(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 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(facet->vertices);
    +      qh_setappend(&points, facet->center);
    +    }
    +  }
    +  if (numcenters > dim) {
    +    simplex= qh_settemp(qh TEMPsize);
    +    qh_setappend(&simplex, vertex->point);
    +    if (unbounded)
    +      qh_setappend(&simplex, midpoint);
    +    qh_maxsimplex(dim, points, NULL, 0, &simplex);
    +    qh_setdelnth(simplex, 0);
    +  }else if (numcenters == dim) {
    +    if (unbounded)
    +      qh_setappend(&points, midpoint);
    +    simplex= points;
    +  }else {
    +    qh_fprintf(qh ferr, 6216, "qhull internal error (qh_detvnorm): too few points(%d) to compute separating plane\n", numcenters);
    +    qh_errexit(qh_ERRqhull, NULL, NULL);
    +  }
    +  i= 0;
    +  gmcoord= qh gm_matrix;
    +  point0= SETfirstt_(simplex, pointT);
    +  FOREACHpoint_(simplex) {
    +    if (qh IStracing >= 4)
    +      qh_printmatrix(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(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(vertex->point);
    +    pointidA= qh_pointid(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 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(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 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_(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 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(&simplex);
    +  qh_settempfree(&points);
    +  return normal;
    +} /* detvnorm */
    +
    +/*---------------------------------
    +
    +  qh_detvridge( 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(vertexT *vertex) {
    +  setT *centers= qh_settemp(qh TEMPsize);
    +  setT *tricenters= qh_settemp(qh TEMPsize);
    +  facetT *neighbor, **neighborp;
    +  boolT firstinf= True;
    +
    +  FOREACHneighbor_(vertex) {
    +    if (neighbor->seen) {
    +      if (neighbor->visitid) {
    +        if (!neighbor->tricoplanar || qh_setunique(&tricenters, neighbor->center))
    +          qh_setappend(¢ers, neighbor);
    +      }else if (firstinf) {
    +        firstinf= False;
    +        qh_setappend(¢ers, neighbor);
    +      }
    +    }
    +  }
    +  qsort(SETaddr_(centers, facetT), (size_t)qh_setsize(centers),
    +             sizeof(facetT *), qh_compare_facetvisit);
    +  qh_settempfree(&tricenters);
    +  return centers;
    +} /* detvridge */
    +
    +/*---------------------------------
    +
    +  qh_detvridge3( 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(vertexT *atvertex, vertexT *vertex) {
    +  setT *centers= qh_settemp(qh TEMPsize);
    +  setT *tricenters= qh_settemp(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(&tricenters, facet->center))
    +          qh_setappend(¢ers, facet);
    +      }else if (firstinf) {
    +        firstinf= False;
    +        qh_setappend(¢ers, 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 ferr, 6217, "qhull internal error (qh_detvridge3): neighbors of vertex p%d are not connected at facet %d\n",
    +                 qh_pointid(vertex->point), neighbor->id);
    +        qh_errexit(qh_ERRqhull, neighbor, NULL);
    +      }
    +    }
    +  }
    +  FOREACHneighbor_(atvertex)
    +    neighbor->seen2= True;
    +  qh_settempfree(&tricenters);
    +  return centers;
    +} /* detvridge3 */
    +
    +/*---------------------------------
    +
    +  qh_eachvoronoi( 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(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 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(tricenters, 0);
    +          FOREACHneighborA_(vertex) {
    +            if (neighborA->seen) {
    +              if (neighborA->visitid) {
    +                if (!neighborA->tricoplanar || qh_setunique(&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 ferr, 4017, "qh_eachvoronoi: Voronoi ridge of %d vertices between sites %d and %d\n",
    +                  count, qh_pointid(atvertex->point), qh_pointid(vertex->point)));
    +            if (printvridge && fp) {
    +              if (inorder && qh hull_dim == 3+1) /* 3-d Voronoi diagram */
    +                centers= qh_detvridge3(atvertex, vertex);
    +              else
    +                centers= qh_detvridge(vertex);
    +              (*printvridge)(fp, atvertex, vertex, centers, unbounded);
    +              qh_settempfree(¢ers);
    +            }
    +          }
    +        }
    +      }
    +    }
    +  }
    +  FOREACHneighbor_(atvertex)
    +    neighbor->seen= False;
    +  qh_settempfree(&tricenters);
    +  return totridges;
    +} /* eachvoronoi */
    +
    +
    +/*---------------------------------
    +
    +  qh_eachvoronoi_all( 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(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_ASvoronoi);
    +  qh_vertexneighbors();
    +  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(vertex->point)+1 != qh GOODvertex)
    +      continue;
    +    totridges += qh_eachvoronoi(fp, printvridge, vertex,
    +                   !qh_ALL, innerouter, inorder);
    +  }
    +  return totridges;
    +} /* eachvoronoi_all */
    +
    +/*---------------------------------
    +
    +  qh_facet2point( 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(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(vertex0->point, facet, &dist);
    +  *mindist= dist;
    +  *point0= qh_projectpoint(vertex0->point, facet, dist);
    +  qh_distplane(vertex1->point, facet, &dist);
    +  minimize_(*mindist, dist);
    +  *point1= qh_projectpoint(vertex1->point, facet, dist);
    +} /* facet2point */
    +
    +
    +/*---------------------------------
    +
    +  qh_facetvertices( 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(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 num_vertices);
    +    FORALLvertices {
    +      vertex->visitid= qh vertex_visit;
    +      qh_setappend(&vertices, vertex);
    +    }
    +  }else {
    +    vertices= qh_settemp(qh TEMPsize);
    +    FORALLfacet_(facetlist) {
    +      if (!allfacets && qh_skipfacet(facet))
    +        continue;
    +      FOREACHvertex_(facet->vertices) {
    +        if (vertex->visitid != qh vertex_visit) {
    +          vertex->visitid= qh vertex_visit;
    +          qh_setappend(&vertices, vertex);
    +        }
    +      }
    +    }
    +  }
    +  FOREACHfacet_(facets) {
    +    if (!allfacets && qh_skipfacet(facet))
    +      continue;
    +    FOREACHvertex_(facet->vertices) {
    +      if (vertex->visitid != qh vertex_visit) {
    +        vertex->visitid= qh vertex_visit;
    +        qh_setappend(&vertices, vertex);
    +      }
    +    }
    +  }
    +  return vertices;
    +} /* facetvertices */
    +
    +/*---------------------------------
    +
    +  qh_geomplanes( 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(facetT *facet, realT *outerplane, realT *innerplane) {
    +  realT radius;
    +
    +  if (qh MERGING || qh JOGGLEmax < REALmax/2) {
    +    qh_outerinner(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 */
    +
    +
    +/*---------------------------------
    +
    +  qh_markkeep( 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(facetT *facetlist) {
    +  facetT *facet, **facetp;
    +  setT *facets= qh_settemp(qh num_facets);
    +  int size, count;
    +
    +  trace2((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(&facets, facet);
    +  }
    +  size= qh_setsize(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(&facets);
    +  count= 0;
    +  FORALLfacet_(facetlist) {
    +    if (facet->good)
    +      count++;
    +  }
    +  qh num_good= count;
    +} /* markkeep */
    +
    +
    +/*---------------------------------
    +
    +  qh_markvoronoi( 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(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_ASvoronoi);  /* in case, qh_printvdiagram2 called by user */
    +  qh_vertexneighbors();
    +  vertices= qh_pointvertex();
    +  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(facet)) {
    +      if (!facet->upperdelaunay) {
    +        isLower= True;
    +        break;
    +      }
    +    }
    +  }
    +  FOREACHfacet_(facets) {
    +    if (printall || !qh_skipfacet(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(facet))
    +      facet->visitid= numcenters++;
    +  }
    +  FOREACHfacet_(facets) {
    +    if (printall || !qh_skipfacet(facet))
    +      facet->visitid= numcenters++;
    +  }
    +  *isLowerp= isLower;
    +  *numcentersp= numcenters;
    +  trace2((qh ferr, 2007, "qh_markvoronoi: isLower %d numcenters %d\n", isLower, numcenters));
    +  return vertices;
    +} /* markvoronoi */
    +
    +/*---------------------------------
    +
    +  qh_order_vertexneighbors( 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(vertexT *vertex) {
    +  setT *newset;
    +  facetT *facet, *neighbor, **neighborp;
    +
    +  trace4((qh ferr, 4018, "qh_order_vertexneighbors: order neighbors of v%d for 3-d\n", vertex->id));
    +  newset= qh_settemp(qh_setsize(vertex->neighbors));
    +  facet= (facetT*)qh_setdellast(vertex->neighbors);
    +  qh_setappend(&newset, facet);
    +  while (qh_setsize(vertex->neighbors)) {
    +    FOREACHneighbor_(vertex) {
    +      if (qh_setin(facet->neighbors, neighbor)) {
    +        qh_setdel(vertex->neighbors, neighbor);
    +        qh_setappend(&newset, neighbor);
    +        facet= neighbor;
    +        break;
    +      }
    +    }
    +    if (!neighbor) {
    +      qh_fprintf(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_ERRqhull, facet, NULL);
    +    }
    +  }
    +  qh_setfree(&vertex->neighbors);
    +  qh_settemppop();
    +  vertex->neighbors= newset;
    +} /* order_vertexneighbors */
    +
    +/*---------------------------------
    +
    +  qh_prepare_output( )
    +    prepare for qh_produce_output2() 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(void) {
    +  if (qh VORONOI) {
    +    qh_clearcenters(qh_ASvoronoi);  /* must be before qh_triangulate */
    +    qh_vertexneighbors();
    +  }
    +  if (qh TRIangulate && !qh hasTriangulation) {
    +    qh_triangulate();
    +    if (qh VERIFYoutput && !qh CHECKfrequently)
    +      qh_checkpolygon(qh facet_list);
    +  }
    +  qh_findgood_all(qh facet_list);
    +  if (qh GETarea)
    +    qh_getarea(qh facet_list);
    +  if (qh KEEParea || qh KEEPmerge || qh KEEPminArea < REALmax/2)
    +    qh_markkeep(qh facet_list);
    +  if (qh PRINTstatistics)
    +    qh_collectstatistics();
    +}
    +
    +/*---------------------------------
    +
    +  qh_printafacet( 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(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(facet))
    +    return;
    +  if (facet->visible && qh NEWfacets && format != qh_PRINTfacets)
    +    return;
    +  qh printoutnum++;
    +  switch (format) {
    +  case qh_PRINTarea:
    +    if (facet->isarea) {
    +      qh_fprintf(fp, 9009, qh_REAL_1, facet->f.area);
    +      qh_fprintf(fp, 9010, "\n");
    +    }else
    +      qh_fprintf(fp, 9011, "0\n");
    +    break;
    +  case qh_PRINTcoplanars:
    +    qh_fprintf(fp, 9012, "%d", qh_setsize(facet->coplanarset));
    +    FOREACHpoint_(facet->coplanarset)
    +      qh_fprintf(fp, 9013, " %d", qh_pointid(point));
    +    qh_fprintf(fp, 9014, "\n");
    +    break;
    +  case qh_PRINTcentrums:
    +    qh_printcenter(fp, format, NULL, facet);
    +    break;
    +  case qh_PRINTfacets:
    +    qh_printfacet(fp, facet);
    +    break;
    +  case qh_PRINTfacets_xridge:
    +    qh_printfacetheader(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(color, color);
    +    if (qh PRINTdim != qh hull_dim)
    +      qh_normalize2(color, 3, True, NULL, NULL);
    +    if (qh hull_dim <= 2)
    +      qh_printfacet2geom(fp, facet, color);
    +    else if (qh hull_dim == 3) {
    +      if (facet->simplicial)
    +        qh_printfacet3geom_simplicial(fp, facet, color);
    +      else
    +        qh_printfacet3geom_nonsimplicial(fp, facet, color);
    +    }else {
    +      if (facet->simplicial)
    +        qh_printfacet4geom_simplicial(fp, facet, color);
    +      else
    +        qh_printfacet4geom_nonsimplicial(fp, facet, color);
    +    }
    +    break;
    +  case qh_PRINTids:
    +    qh_fprintf(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(fp, facet, format);
    +    else if (facet->simplicial || qh hull_dim == 2 || format == qh_PRINToff)
    +      qh_printfacetNvertex_simplicial(fp, facet, format);
    +    else
    +      qh_printfacetNvertex_nonsimplicial(fp, facet, qh printoutvar++, format);
    +    break;
    +  case qh_PRINTinner:
    +    qh_outerinner(facet, NULL, &innerplane);
    +    offset= facet->offset - innerplane;
    +    goto LABELprintnorm;
    +    break; /* prevent warning */
    +  case qh_PRINTmerges:
    +    qh_fprintf(fp, 9016, "%d\n", facet->nummerge);
    +    break;
    +  case qh_PRINTnormals:
    +    offset= facet->offset;
    +    goto LABELprintnorm;
    +    break; /* prevent warning */
    +  case qh_PRINTouter:
    +    qh_outerinner(facet, &outerplane, NULL);
    +    offset= facet->offset - outerplane;
    +  LABELprintnorm:
    +    if (!facet->normal) {
    +      qh_fprintf(fp, 9017, "no normal for facet f%d\n", facet->id);
    +      break;
    +    }
    +    if (qh CDDoutput) {
    +      qh_fprintf(fp, 9018, qh_REAL_1, -offset);
    +      for (k=0; k < qh hull_dim; k++)
    +        qh_fprintf(fp, 9019, qh_REAL_1, -facet->normal[k]);
    +    }else {
    +      for (k=0; k < qh hull_dim; k++)
    +        qh_fprintf(fp, 9020, qh_REAL_1, facet->normal[k]);
    +      qh_fprintf(fp, 9021, qh_REAL_1, offset);
    +    }
    +    qh_fprintf(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(fp, facet, format, qh printoutvar++);
    +    else
    +      qh_printfacet3math(fp, facet, format, qh printoutvar++);
    +    break;
    +  case qh_PRINTneighbors:
    +    qh_fprintf(fp, 9023, "%d", qh_setsize(facet->neighbors));
    +    FOREACHneighbor_(facet)
    +      qh_fprintf(fp, 9024, " %d",
    +               neighbor->visitid ? neighbor->visitid - 1: 0 - neighbor->id);
    +    qh_fprintf(fp, 9025, "\n");
    +    break;
    +  case qh_PRINTpointintersect:
    +    if (!qh feasible_point) {
    +      qh_fprintf(qh ferr, 6067, "qhull input error (qh_printafacet): option 'Fp' needs qh feasible_point\n");
    +      qh_errexit( qh_ERRinput, NULL, NULL);
    +    }
    +    if (facet->offset > 0)
    +      goto LABELprintinfinite;
    +    point= coordp= (coordT*)qh_memalloc(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(point, qh normal_size);
    +          goto LABELprintinfinite;
    +        }
    +      }
    +    }
    +    qh_printpoint(fp, NULL, point);
    +    qh_memfree(point, qh normal_size);
    +    break;
    +  LABELprintinfinite:
    +    for (k=qh hull_dim; k--; )
    +      qh_fprintf(fp, 9026, qh_REAL_1, qh_INFINITE);
    +    qh_fprintf(fp, 9027, "\n");
    +    break;
    +  case qh_PRINTpointnearest:
    +    FOREACHpoint_(facet->coplanarset) {
    +      int id, id2;
    +      vertex= qh_nearvertex(facet, point, &dist);
    +      id= qh_pointid(vertex->point);
    +      id2= qh_pointid(point);
    +      qh_fprintf(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(fp, 9029, "1 ");
    +    qh_printcenter(fp, format, NULL, facet);
    +    break;
    +  case qh_PRINTvertices:
    +    qh_fprintf(fp, 9030, "%d", qh_setsize(facet->vertices));
    +    FOREACHvertex_(facet->vertices)
    +      qh_fprintf(fp, 9031, " %d", qh_pointid(vertex->point));
    +    qh_fprintf(fp, 9032, "\n");
    +    break;
    +  default:
    +    break;
    +  }
    +} /* printafacet */
    +
    +/*---------------------------------
    +
    +  qh_printbegin(  )
    +    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(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(facetlist, facets, printall, &numfacets, &numsimplicial,
    +      &totneighbors, &numridges, &numcoplanars, &numtricoplanars);
    +  switch (format) {
    +  case qh_PRINTnone:
    +    break;
    +  case qh_PRINTarea:
    +    qh_fprintf(fp, 9033, "%d\n", numfacets);
    +    break;
    +  case qh_PRINTcoplanars:
    +    qh_fprintf(fp, 9034, "%d\n", numfacets);
    +    break;
    +  case qh_PRINTcentrums:
    +    if (qh CENTERtype == qh_ASnone)
    +      qh_clearcenters(qh_AScentrum);
    +    qh_fprintf(fp, 9035, "%d\n%d\n", qh hull_dim, numfacets);
    +    break;
    +  case qh_PRINTfacets:
    +  case qh_PRINTfacets_xridge:
    +    if (facetlist)
    +      qh_printvertexlist(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 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 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 ferr, 7051, "qhull warning: output for vertices not implemented in 4-d\n");
    +    if (qh PRINTdim == 4 && qh DOintersections && qh PRINTnoplanes)
    +      qh_fprintf(qh ferr, 7052, "qhull warning: 'Gnh' generates no output in 4-d\n");
    +    if (qh PRINTdim == 2) {
    +      qh_fprintf(fp, 9036, "{appearance {linewidth 3} LIST # %s | %s\n",
    +              qh rbox_command, qh qhull_command);
    +    }else if (qh PRINTdim == 3) {
    +      qh_fprintf(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(NULL, facet, &num, printall);
    +      FOREACHfacet_(facets)
    +        qh_printend4geom(NULL, facet, &num, printall);
    +      qh ridgeoutnum= num;
    +      qh printoutvar= 0;  /* counts number of ridges in output */
    +      qh_fprintf(fp, 9038, "LIST # %s | %s\n", qh rbox_command, qh qhull_command);
    +    }
    +
    +    if (qh PRINTdots) {
    +      qh printoutnum++;
    +      num= qh num_points + qh_setsize(qh other_points);
    +      if (qh DELAUNAY && qh ATinfinity)
    +        num--;
    +      if (qh PRINTdim == 4)
    +        qh_fprintf(fp, 9039, "4VECT %d %d 1\n", num, num);
    +      else
    +        qh_fprintf(fp, 9040, "VECT %d %d 1\n", num, num);
    +
    +      for (i=num; i--; ) {
    +        if (i % 20 == 0)
    +          qh_fprintf(fp, 9041, "\n");
    +        qh_fprintf(fp, 9042, "1 ");
    +      }
    +      qh_fprintf(fp, 9043, "# 1 point per line\n1 ");
    +      for (i=num-1; i--; ) { /* num at least 3 for D2 */
    +        if (i % 20 == 0)
    +          qh_fprintf(fp, 9044, "\n");
    +        qh_fprintf(fp, 9045, "0 ");
    +      }
    +      qh_fprintf(fp, 9046, "# 1 color for all\n");
    +      FORALLpoints {
    +        if (!qh DELAUNAY || !qh ATinfinity || qh_pointid(point) != qh num_points-1) {
    +          if (qh PRINTdim == 4)
    +            qh_printpoint(fp, NULL, point);
    +            else
    +              qh_printpoint3(fp, point);
    +        }
    +      }
    +      FOREACHpoint_(qh other_points) {
    +        if (qh PRINTdim == 4)
    +          qh_printpoint(fp, NULL, point);
    +        else
    +          qh_printpoint3(fp, point);
    +      }
    +      qh_fprintf(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(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(facetlist, facets, printall);
    +      if (qh PRINTspheres && qh PRINTdim <= 3)
    +        qh_printspheres(fp, vertices, qh PRINTradius);
    +      if (qh PRINTcoplanar || qh PRINTcentrums) {
    +        qh firstcentrum= True;
    +        if (qh PRINTcoplanar&& !qh PRINTspheres) {
    +          FOREACHvertex_(vertices)
    +            qh_printpointvect2(fp, vertex->point, NULL, qh interior_point, qh PRINTradius);
    +        }
    +        FORALLfacet_(facetlist) {
    +          if (!printall && qh_skipfacet(facet))
    +            continue;
    +          if (!facet->normal)
    +            continue;
    +          if (qh PRINTcentrums && qh PRINTdim <= 3)
    +            qh_printcentrum(fp, facet, qh PRINTcradius);
    +          if (!qh PRINTcoplanar)
    +            continue;
    +          FOREACHpoint_(facet->coplanarset)
    +            qh_printpointvect2(fp, point, facet->normal, NULL, qh PRINTradius);
    +          FOREACHpoint_(facet->outsideset)
    +            qh_printpointvect2(fp, point, facet->normal, NULL, qh PRINTradius);
    +        }
    +        FOREACHfacet_(facets) {
    +          if (!printall && qh_skipfacet(facet))
    +            continue;
    +          if (!facet->normal)
    +            continue;
    +          if (qh PRINTcentrums && qh PRINTdim <= 3)
    +            qh_printcentrum(fp, facet, qh PRINTcradius);
    +          if (!qh PRINTcoplanar)
    +            continue;
    +          FOREACHpoint_(facet->coplanarset)
    +            qh_printpointvect2(fp, point, facet->normal, NULL, qh PRINTradius);
    +          FOREACHpoint_(facet->outsideset)
    +            qh_printpointvect2(fp, point, facet->normal, NULL, qh PRINTradius);
    +        }
    +      }
    +      qh_settempfree(&vertices);
    +    }
    +    qh visit_id++; /* for printing hyperplane intersections */
    +    break;
    +  case qh_PRINTids:
    +    qh_fprintf(fp, 9049, "%d\n", numfacets);
    +    break;
    +  case qh_PRINTincidences:
    +    if (qh VORONOI && qh PRINTprecision)
    +      qh_fprintf(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(fp, 9050, "%d\n", numfacets);
    +    else
    +      qh_fprintf(fp, 9051, "%d\n", numsimplicial+numridges);
    +    break;
    +  case qh_PRINTinner:
    +  case qh_PRINTnormals:
    +  case qh_PRINTouter:
    +    if (qh CDDoutput)
    +      qh_fprintf(fp, 9052, "%s | %s\nbegin\n    %d %d real\n", qh rbox_command,
    +            qh qhull_command, numfacets, qh hull_dim+1);
    +    else
    +      qh_fprintf(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 ferr, 7054, "qhull warning: output is the Delaunay triangulation\n");
    +    if (format == qh_PRINTmaple) {
    +      if (qh hull_dim == 2)
    +        qh_fprintf(fp, 9054, "PLOT(CURVES(\n");
    +      else
    +        qh_fprintf(fp, 9055, "PLOT3D(POLYGONS(\n");
    +    }else
    +      qh_fprintf(fp, 9056, "{\n");
    +    qh printoutvar= 0;   /* counts number of facets for notfirst */
    +    break;
    +  case qh_PRINTmerges:
    +    qh_fprintf(fp, 9057, "%d\n", numfacets);
    +    break;
    +  case qh_PRINTpointintersect:
    +    qh_fprintf(fp, 9058, "%d\n%d\n", qh hull_dim, numfacets);
    +    break;
    +  case qh_PRINTneighbors:
    +    qh_fprintf(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(fp, 9060, "%d\n%d %d %d\n", num,
    +        qh num_points+qh_setsize(qh other_points), numfacets, totneighbors/2);
    +    else { /* qh_PRINTtriangles */
    +      qh printoutvar= qh num_points+qh_setsize(qh other_points); /* first centrum */
    +      if (qh DELAUNAY)
    +        num--;  /* drop last dimension */
    +      qh_fprintf(fp, 9061, "%d\n%d %d %d\n", num, qh printoutvar
    +        + numfacets - numsimplicial, numsimplicial + numridges, totneighbors/2);
    +    }
    +    FORALLpoints
    +      qh_printpointid(qh fout, NULL, num, point, qh_IDunknown);
    +    FOREACHpoint_(qh other_points)
    +      qh_printpointid(qh fout, NULL, num, point, qh_IDunknown);
    +    if (format == qh_PRINTtriangles && qh hull_dim > 2) {
    +      FORALLfacets {
    +        if (!facet->simplicial && facet->visitid)
    +          qh_printcenter(qh fout, format, NULL, facet);
    +      }
    +    }
    +    break;
    +  case qh_PRINTpointnearest:
    +    qh_fprintf(fp, 9062, "%d\n", numcoplanars);
    +    break;
    +  case qh_PRINTpoints:
    +    if (!qh VORONOI)
    +      goto LABELnoformat;
    +    if (qh CDDoutput)
    +      qh_fprintf(fp, 9063, "%s | %s\nbegin\n%d %d real\n", qh rbox_command,
    +           qh qhull_command, numfacets, qh hull_dim);
    +    else
    +      qh_fprintf(fp, 9064, "%d\n%d\n", qh hull_dim-1, numfacets);
    +    break;
    +  case qh_PRINTvertices:
    +    qh_fprintf(fp, 9065, "%d\n", numfacets);
    +    break;
    +  case qh_PRINTsummary:
    +  default:
    +  LABELnoformat:
    +    qh_fprintf(qh ferr, 6068, "qhull internal error (qh_printbegin): can not use this format for dimension %d\n",
    +         qh hull_dim);
    +    qh_errexit(qh_ERRqhull, NULL, NULL);
    +  }
    +} /* printbegin */
    +
    +/*---------------------------------
    +
    +  qh_printcenter( 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(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(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(facet->vertices);
    +      for (k=0; k < num; k++)
    +        qh_fprintf(fp, 9067, qh_REAL_1, facet->center[k]);
    +    }else {
    +      for (k=0; k < num; k++)
    +        qh_fprintf(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(facet);
    +    for (k=0; k < num; k++)
    +      qh_fprintf(fp, 9069, qh_REAL_1, facet->center[k]);
    +  }
    +  if (format == qh_PRINTgeom && num == 2)
    +    qh_fprintf(fp, 9070, " 0\n");
    +  else
    +    qh_fprintf(fp, 9071, "\n");
    +} /* printcenter */
    +
    +/*---------------------------------
    +
    +  qh_printcentrum( 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(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(facet);
    +    centrum= facet->center;
    +  }else {
    +    centrum= qh_getcentrum(facet);
    +    tempcentrum= True;
    +  }
    +  qh_fprintf(fp, 9072, "{appearance {-normal -edge normscale 0} ");
    +  if (qh firstcentrum) {
    +    qh firstcentrum= False;
    +    qh_fprintf(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(fp, 9074, "{INST geom { : centrum } transform { # f%d\n", facet->id);
    +  apex= SETfirstt_(facet->vertices, vertexT);
    +  qh_distplane(apex->point, facet, &dist);
    +  projpt= qh_projectpoint(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(xaxis, xaxis);
    +    qh_projectdim3(normal, normal);
    +    qh_normalize2(normal, qh PRINTdim, True, NULL, NULL);
    +  }
    +  qh_crossproduct(3, xaxis, normal, yaxis);
    +  qh_fprintf(fp, 9075, "%8.4g %8.4g %8.4g 0\n", xaxis[0], xaxis[1], xaxis[2]);
    +  qh_fprintf(fp, 9076, "%8.4g %8.4g %8.4g 0\n", yaxis[0], yaxis[1], yaxis[2]);
    +  qh_fprintf(fp, 9077, "%8.4g %8.4g %8.4g 0\n", normal[0], normal[1], normal[2]);
    +  qh_printpoint3(fp, centrum);
    +  qh_fprintf(fp, 9078, "1 }}}\n");
    +  qh_memfree(projpt, qh normal_size);
    +  qh_printpointvect(fp, centrum, facet->normal, NULL, radius, green);
    +  if (tempcentrum)
    +    qh_memfree(centrum, qh normal_size);
    +} /* printcentrum */
    +
    +/*---------------------------------
    +
    +  qh_printend( fp, format )
    +    prints trailer for all output formats
    +
    +  see:
    +    qh_printbegin() and qh_printafacet()
    +
    +*/
    +void qh_printend(FILE *fp, qh_PRINT format, facetT *facetlist, setT *facets, boolT printall) {
    +  int num;
    +  facetT *facet, **facetp;
    +
    +  if (!qh printoutnum)
    +    qh_fprintf(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(fp, facet,&num, printall);
    +      FOREACHfacet_(facets)
    +        qh_printend4geom(fp, facet, &num, printall);
    +      if (num != qh ridgeoutnum || qh printoutvar != qh ridgeoutnum) {
    +        qh_fprintf(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_ERRqhull, NULL, NULL);
    +      }
    +    }else
    +      qh_fprintf(fp, 9079, "}\n");
    +    break;
    +  case qh_PRINTinner:
    +  case qh_PRINTnormals:
    +  case qh_PRINTouter:
    +    if (qh CDDoutput)
    +      qh_fprintf(fp, 9080, "end\n");
    +    break;
    +  case qh_PRINTmaple:
    +    qh_fprintf(fp, 9081, "));\n");
    +    break;
    +  case qh_PRINTmathematica:
    +    qh_fprintf(fp, 9082, "}\n");
    +    break;
    +  case qh_PRINTpoints:
    +    if (qh CDDoutput)
    +      qh_fprintf(fp, 9083, "end\n");
    +    break;
    +  default:
    +    break;
    +  }
    +} /* printend */
    +
    +/*---------------------------------
    +
    +  qh_printend4geom( 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(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(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(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(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 */
    +
    +/*---------------------------------
    +
    +  qh_printextremes( 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(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 other_points);
    +
    +  points= qh_settemp(allpoints);
    +  qh_setzero(points, 0, allpoints);
    +  vertices= qh_facetvertices(facetlist, facets, printall);
    +  FOREACHvertex_(vertices) {
    +    id= qh_pointid(vertex->point);
    +    if (id >= 0) {
    +      SETelem_(points, id)= vertex->point;
    +      numpoints++;
    +    }
    +  }
    +  qh_settempfree(&vertices);
    +  qh_fprintf(fp, 9086, "%d\n", numpoints);
    +  FOREACHpoint_i_(points) {
    +    if (point)
    +      qh_fprintf(fp, 9087, "%d\n", point_i);
    +  }
    +  qh_settempfree(&points);
    +} /* printextremes */
    +
    +/*---------------------------------
    +
    +  qh_printextremes_2d( 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(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(facetlist, facets, printall, &numfacets, &numsimplicial,
    +      &totneighbors, &numridges, &numcoplanars, &numtricoplanars); /* marks qh visit_id */
    +  vertices= qh_facetvertices(facetlist, facets, printall);
    +  qh_fprintf(fp, 9088, "%d\n", qh_setsize(vertices));
    +  qh_settempfree(&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 ferr, 6218, "Qhull internal error (qh_printextremes_2d): loop in facet list.  facet %d nextfacet %d\n",
    +                 facet->id, nextfacet->id);
    +      qh_errexit2(qh_ERRqhull, facet, nextfacet);
    +    }
    +    if (facet->visitid) {
    +      if (vertexA->visitid != qh vertex_visit) {
    +        vertexA->visitid= qh vertex_visit;
    +        qh_fprintf(fp, 9089, "%d\n", qh_pointid(vertexA->point));
    +      }
    +      if (vertexB->visitid != qh vertex_visit) {
    +        vertexB->visitid= qh vertex_visit;
    +        qh_fprintf(fp, 9090, "%d\n", qh_pointid(vertexB->point));
    +      }
    +    }
    +    facet->visitid= qh visit_id;
    +    facet= nextfacet;
    +  }while (facet && facet != startfacet);
    +} /* printextremes_2d */
    +
    +/*---------------------------------
    +
    +  qh_printextremes_d( 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(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(facetlist, facets, printall);
    +  qh_vertexneighbors();
    +  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(fp, 9091, "%d\n", numpoints);
    +  FOREACHvertex_(vertices) {
    +    if (vertex->seen)
    +      qh_fprintf(fp, 9092, "%d\n", qh_pointid(vertex->point));
    +  }
    +  qh_settempfree(&vertices);
    +} /* printextremes_d */
    +
    +/*---------------------------------
    +
    +  qh_printfacet( fp, facet )
    +    prints all fields of a facet to fp
    +
    +  notes:
    +    ridges printed in neighbor order
    +*/
    +void qh_printfacet(FILE *fp, facetT *facet) {
    +
    +  qh_printfacetheader(fp, facet);
    +  if (facet->ridges)
    +    qh_printfacetridges(fp, facet);
    +} /* printfacet */
    +
    +
    +/*---------------------------------
    +
    +  qh_printfacet2geom( fp, facet, color )
    +    print facet as part of a 2-d VECT for Geomview
    +
    +    notes:
    +      assume precise calculations in io.c with roundoff covered by qh_GEOMepsilon
    +      mindist is calculated within io.c.  maxoutside is calculated elsewhere
    +      so a DISTround error may have occurred.
    +*/
    +void qh_printfacet2geom(FILE *fp, facetT *facet, realT color[3]) {
    +  pointT *point0, *point1;
    +  realT mindist, innerplane, outerplane;
    +  int k;
    +
    +  qh_facet2point(facet, &point0, &point1, &mindist);
    +  qh_geomplanes(facet, &outerplane, &innerplane);
    +  if (qh PRINTouter || (!qh PRINTnoplanes && !qh PRINTinner))
    +    qh_printfacet2geom_points(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(fp, point0, point1, facet, innerplane, color);
    +  }
    +  qh_memfree(point1, qh normal_size);
    +  qh_memfree(point0, qh normal_size);
    +} /* printfacet2geom */
    +
    +/*---------------------------------
    +
    +  qh_printfacet2geom_points( 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(FILE *fp, pointT *point1, pointT *point2,
    +                               facetT *facet, realT offset, realT color[3]) {
    +  pointT *p1= point1, *p2= point2;
    +
    +  qh_fprintf(fp, 9093, "VECT 1 2 1 2 1 # f%d\n", facet->id);
    +  if (offset != 0.0) {
    +    p1= qh_projectpoint(p1, facet, -offset);
    +    p2= qh_projectpoint(p2, facet, -offset);
    +  }
    +  qh_fprintf(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(p1, qh normal_size);
    +    qh_memfree(p2, qh normal_size);
    +  }
    +  qh_fprintf(fp, 9095, "%8.4g %8.4g %8.4g 1.0\n", color[0], color[1], color[2]);
    +} /* printfacet2geom_points */
    +
    +
    +/*---------------------------------
    +
    +  qh_printfacet2math( 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(FILE *fp, facetT *facet, qh_PRINT format, int notfirst) {
    +  pointT *point0, *point1;
    +  realT mindist;
    +  const char *pointfmt;
    +
    +  qh_facet2point(facet, &point0, &point1, &mindist);
    +  if (notfirst)
    +    qh_fprintf(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(fp, 9097, pointfmt, point0[0], point0[1], point1[0], point1[1]);
    +  qh_memfree(point1, qh normal_size);
    +  qh_memfree(point0, qh normal_size);
    +} /* printfacet2math */
    +
    +
    +/*---------------------------------
    +
    +  qh_printfacet3geom_nonsimplicial( 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(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(facet, &outerplane, &innerplane);
    +  vertices= qh_facet3vertex(facet); /* oriented */
    +  cntvertices= qh_setsize(vertices);
    +  projectedpoints= qh_settemp(cntvertices);
    +  FOREACHvertex_(vertices) {
    +    zinc_(Zdistio);
    +    qh_distplane(vertex->point, facet, &dist);
    +    projpt= qh_projectpoint(vertex->point, facet, dist);
    +    qh_setappend(&projectedpoints, projpt);
    +  }
    +  if (qh PRINTouter || (!qh PRINTnoplanes && !qh PRINTinner))
    +    qh_printfacet3geom_points(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(fp, projectedpoints, facet, innerplane, color);
    +  }
    +  FOREACHpoint_(projectedpoints)
    +    qh_memfree(point, qh normal_size);
    +  qh_settempfree(&projectedpoints);
    +  qh_settempfree(&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(fp, facet, neighbor, ridge->vertices, black);
    +        if (qh PRINTridges) {
    +          vertexA= SETfirstt_(ridge->vertices, vertexT);
    +          vertexB= SETsecondt_(ridge->vertices, vertexT);
    +          qh_printline3geom(fp, vertexA->point, vertexB->point, green);
    +        }
    +      }
    +    }
    +  }
    +} /* printfacet3geom_nonsimplicial */
    +
    +/*---------------------------------
    +
    +  qh_printfacet3geom_points( 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(FILE *fp, setT *points, facetT *facet, realT offset, realT color[3]) {
    +  int k, n= qh_setsize(points), i;
    +  pointT *point, **pointp;
    +  setT *printpoints;
    +
    +  qh_fprintf(fp, 9098, "{ OFF %d 1 1 # f%d\n", n, facet->id);
    +  if (offset != 0.0) {
    +    printpoints= qh_settemp(n);
    +    FOREACHpoint_(points)
    +      qh_setappend(&printpoints, qh_projectpoint(point, facet, -offset));
    +  }else
    +    printpoints= points;
    +  FOREACHpoint_(printpoints) {
    +    for (k=0; k < qh hull_dim; k++) {
    +      if (k == qh DROPdim)
    +        qh_fprintf(fp, 9099, "0 ");
    +      else
    +        qh_fprintf(fp, 9100, "%8.4g ", point[k]);
    +    }
    +    if (printpoints != points)
    +      qh_memfree(point, qh normal_size);
    +    qh_fprintf(fp, 9101, "\n");
    +  }
    +  if (printpoints != points)
    +    qh_settempfree(&printpoints);
    +  qh_fprintf(fp, 9102, "%d ", n);
    +  for (i=0; i < n; i++)
    +    qh_fprintf(fp, 9103, "%d ", i);
    +  qh_fprintf(fp, 9104, "%8.4g %8.4g %8.4g 1.0 }\n", color[0], color[1], color[2]);
    +} /* printfacet3geom_points */
    +
    +
    +/*---------------------------------
    +
    +  qh_printfacet3geom_simplicial(  )
    +    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.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(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(facet, &outerplane, &innerplane);
    +  vertices= qh_facet3vertex(facet);
    +  points= qh_settemp(qh TEMPsize);
    +  FOREACHvertex_(vertices)
    +    qh_setappend(&points, vertex->point);
    +  if (qh PRINTouter || (!qh PRINTnoplanes && !qh PRINTinner))
    +    qh_printfacet3geom_points(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(fp, points, facet, innerplane, color);
    +  }
    +  qh_settempfree(&points);
    +  qh_settempfree(&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(facet->vertices, qh hull_dim,
    +                          SETindex_(facet->neighbors, neighbor), 0);
    +        if (qh DOintersections)
    +           qh_printhyperplaneintersection(fp, facet, neighbor, vertices, black);
    +        if (qh PRINTridges) {
    +          vertexA= SETfirstt_(vertices, vertexT);
    +          vertexB= SETsecondt_(vertices, vertexT);
    +          qh_printline3geom(fp, vertexA->point, vertexB->point, green);
    +        }
    +        qh_setfree(&vertices);
    +      }
    +    }
    +  }
    +} /* printfacet3geom_simplicial */
    +
    +/*---------------------------------
    +
    +  qh_printfacet3math( 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(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(fp, 9105, ",\n");
    +  vertices= qh_facet3vertex(facet);
    +  points= qh_settemp(qh_setsize(vertices));
    +  FOREACHvertex_(vertices) {
    +    zinc_(Zdistio);
    +    qh_distplane(vertex->point, facet, &dist);
    +    point= qh_projectpoint(vertex->point, facet, dist);
    +    qh_setappend(&points, point);
    +  }
    +  if (format == qh_PRINTmaple) {
    +    qh_fprintf(fp, 9106, "[");
    +    pointfmt= "[%16.8f, %16.8f, %16.8f]";
    +    endfmt= "]";
    +  }else {
    +    qh_fprintf(fp, 9107, "Polygon[{");
    +    pointfmt= "{%16.8f, %16.8f, %16.8f}";
    +    endfmt= "}]";
    +  }
    +  FOREACHpoint_(points) {
    +    if (firstpoint)
    +      firstpoint= False;
    +    else
    +      qh_fprintf(fp, 9108, ",\n");
    +    qh_fprintf(fp, 9109, pointfmt, point[0], point[1], point[2]);
    +  }
    +  FOREACHpoint_(points)
    +    qh_memfree(point, qh normal_size);
    +  qh_settempfree(&points);
    +  qh_settempfree(&vertices);
    +  qh_fprintf(fp, 9110, "%s", endfmt);
    +} /* printfacet3math */
    +
    +
    +/*---------------------------------
    +
    +  qh_printfacet3vertex( 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(FILE *fp, facetT *facet, qh_PRINT format) {
    +  vertexT *vertex, **vertexp;
    +  setT *vertices;
    +
    +  vertices= qh_facet3vertex(facet);
    +  if (format == qh_PRINToff)
    +    qh_fprintf(fp, 9111, "%d ", qh_setsize(vertices));
    +  FOREACHvertex_(vertices)
    +    qh_fprintf(fp, 9112, "%d ", qh_pointid(vertex->point));
    +  qh_fprintf(fp, 9113, "\n");
    +  qh_settempfree(&vertices);
    +} /* printfacet3vertex */
    +
    +
    +/*---------------------------------
    +
    +  qh_printfacet4geom_nonsimplicial(  )
    +    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(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(fp, facet, neighbor, ridge->vertices, color);
    +    else {
    +      if (qh DROPdim >= 0)
    +        qh_fprintf(fp, 9114, "OFF 3 1 1 # f%d\n", facet->id);
    +      else {
    +        qh printoutvar++;
    +        qh_fprintf(fp, 9115, "# r%d between f%d f%d\n", ridge->id, facet->id, neighbor->id);
    +      }
    +      FOREACHvertex_(ridge->vertices) {
    +        zinc_(Zdistio);
    +        qh_distplane(vertex->point,facet, &dist);
    +        point=qh_projectpoint(vertex->point,facet, dist);
    +        for (k=0; k < qh hull_dim; k++) {
    +          if (k != qh DROPdim)
    +            qh_fprintf(fp, 9116, "%8.4g ", point[k]);
    +        }
    +        qh_fprintf(fp, 9117, "\n");
    +        qh_memfree(point, qh normal_size);
    +      }
    +      if (qh DROPdim >= 0)
    +        qh_fprintf(fp, 9118, "3 0 1 2 %8.4g %8.4g %8.4g\n", color[0], color[1], color[2]);
    +    }
    +  }
    +} /* printfacet4geom_nonsimplicial */
    +
    +
    +/*---------------------------------
    +
    +  qh_printfacet4geom_simplicial( 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(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(facet->vertices, qh hull_dim,
    +                          SETindex_(facet->neighbors, neighbor), 0);
    +    if (qh DOintersections)
    +      qh_printhyperplaneintersection(fp, facet, neighbor, vertices, color);
    +    else {
    +      if (qh DROPdim >= 0)
    +        qh_fprintf(fp, 9119, "OFF 3 1 1 # ridge between f%d f%d\n",
    +                facet->id, neighbor->id);
    +      else {
    +        qh printoutvar++;
    +        qh_fprintf(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(fp, 9121, "%8.4g ", vertex->point[k]);
    +        }
    +        qh_fprintf(fp, 9122, "\n");
    +      }
    +      if (qh DROPdim >= 0)
    +        qh_fprintf(fp, 9123, "3 0 1 2 %8.4g %8.4g %8.4g\n", color[0], color[1], color[2]);
    +    }
    +    qh_setfree(&vertices);
    +  }
    +} /* printfacet4geom_simplicial */
    +
    +
    +/*---------------------------------
    +
    +  qh_printfacetNvertex_nonsimplicial( fp, facet, id, format )
    +    print vertices for an N-d non-simplicial facet
    +    triangulates each ridge to the id
    +*/
    +void qh_printfacetNvertex_nonsimplicial(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(fp, 9124, "%d ", qh hull_dim);
    +    qh_fprintf(fp, 9125, "%d ", id);
    +    if ((ridge->top == facet) ^ qh_ORIENTclock) {
    +      FOREACHvertex_(ridge->vertices)
    +        qh_fprintf(fp, 9126, "%d ", qh_pointid(vertex->point));
    +    }else {
    +      FOREACHvertexreverse12_(ridge->vertices)
    +        qh_fprintf(fp, 9127, "%d ", qh_pointid(vertex->point));
    +    }
    +    qh_fprintf(fp, 9128, "\n");
    +  }
    +} /* printfacetNvertex_nonsimplicial */
    +
    +
    +/*---------------------------------
    +
    +  qh_printfacetNvertex_simplicial( 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(FILE *fp, facetT *facet, qh_PRINT format) {
    +  vertexT *vertex, **vertexp;
    +
    +  if (format == qh_PRINToff || format == qh_PRINTtriangles)
    +    qh_fprintf(fp, 9129, "%d ", qh_setsize(facet->vertices));
    +  if ((facet->toporient ^ qh_ORIENTclock)
    +  || (qh hull_dim > 2 && !facet->simplicial)) {
    +    FOREACHvertex_(facet->vertices)
    +      qh_fprintf(fp, 9130, "%d ", qh_pointid(vertex->point));
    +  }else {
    +    FOREACHvertexreverse12_(facet->vertices)
    +      qh_fprintf(fp, 9131, "%d ", qh_pointid(vertex->point));
    +  }
    +  qh_fprintf(fp, 9132, "\n");
    +} /* printfacetNvertex_simplicial */
    +
    +
    +/*---------------------------------
    +
    +  qh_printfacetheader( fp, facet )
    +    prints header fields of a facet to fp
    +
    +  notes:
    +    for 'f' output and debugging
    +    Same as QhullFacet::printHeader()
    +*/
    +void qh_printfacetheader(FILE *fp, facetT *facet) {
    +  pointT *point, **pointp, *furthest;
    +  facetT *neighbor, **neighborp;
    +  realT dist;
    +
    +  if (facet == qh_MERGEridge) {
    +    qh_fprintf(fp, 9133, " MERGEridge\n");
    +    return;
    +  }else if (facet == qh_DUPLICATEridge) {
    +    qh_fprintf(fp, 9134, " DUPLICATEridge\n");
    +    return;
    +  }else if (!facet) {
    +    qh_fprintf(fp, 9135, " NULLfacet\n");
    +    return;
    +  }
    +  qh old_randomdist= qh RANDOMdist;
    +  qh RANDOMdist= False;
    +  qh_fprintf(fp, 9136, "- f%d\n", facet->id);
    +  qh_fprintf(fp, 9137, "    - flags:");
    +  if (facet->toporient)
    +    qh_fprintf(fp, 9138, " top");
    +  else
    +    qh_fprintf(fp, 9139, " bottom");
    +  if (facet->simplicial)
    +    qh_fprintf(fp, 9140, " simplicial");
    +  if (facet->tricoplanar)
    +    qh_fprintf(fp, 9141, " tricoplanar");
    +  if (facet->upperdelaunay)
    +    qh_fprintf(fp, 9142, " upperDelaunay");
    +  if (facet->visible)
    +    qh_fprintf(fp, 9143, " visible");
    +  if (facet->newfacet)
    +    qh_fprintf(fp, 9144, " new");
    +  if (facet->tested)
    +    qh_fprintf(fp, 9145, " tested");
    +  if (!facet->good)
    +    qh_fprintf(fp, 9146, " notG");
    +  if (facet->seen)
    +    qh_fprintf(fp, 9147, " seen");
    +  if (facet->coplanar)
    +    qh_fprintf(fp, 9148, " coplanar");
    +  if (facet->mergehorizon)
    +    qh_fprintf(fp, 9149, " mergehorizon");
    +  if (facet->keepcentrum)
    +    qh_fprintf(fp, 9150, " keepcentrum");
    +  if (facet->dupridge)
    +    qh_fprintf(fp, 9151, " dupridge");
    +  if (facet->mergeridge && !facet->mergeridge2)
    +    qh_fprintf(fp, 9152, " mergeridge1");
    +  if (facet->mergeridge2)
    +    qh_fprintf(fp, 9153, " mergeridge2");
    +  if (facet->newmerge)
    +    qh_fprintf(fp, 9154, " newmerge");
    +  if (facet->flipped)
    +    qh_fprintf(fp, 9155, " flipped");
    +  if (facet->notfurthest)
    +    qh_fprintf(fp, 9156, " notfurthest");
    +  if (facet->degenerate)
    +    qh_fprintf(fp, 9157, " degenerate");
    +  if (facet->redundant)
    +    qh_fprintf(fp, 9158, " redundant");
    +  qh_fprintf(fp, 9159, "\n");
    +  if (facet->isarea)
    +    qh_fprintf(fp, 9160, "    - area: %2.2g\n", facet->f.area);
    +  else if (qh NEWfacets && facet->visible && facet->f.replace)
    +    qh_fprintf(fp, 9161, "    - replacement: f%d\n", facet->f.replace->id);
    +  else if (facet->newfacet) {
    +    if (facet->f.samecycle && facet->f.samecycle != facet)
    +      qh_fprintf(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(fp, 9163, "    - owner of normal & centrum is facet f%d\n", facet->f.triowner->id);
    +  }else if (facet->f.newcycle)
    +    qh_fprintf(fp, 9164, "    - was horizon to f%d\n", facet->f.newcycle->id);
    +  if (facet->nummerge)
    +    qh_fprintf(fp, 9165, "    - merges: %d\n", facet->nummerge);
    +  qh_printpointid(fp, "    - normal: ", qh hull_dim, facet->normal, qh_IDunknown);
    +  qh_fprintf(fp, 9166, "    - offset: %10.7g\n", facet->offset);
    +  if (qh CENTERtype == qh_ASvoronoi || facet->center)
    +    qh_printcenter(fp, qh_PRINTfacets, "    - center: ", facet);
    +#if qh_MAXoutside
    +  if (facet->maxoutside > qh DISTround)
    +    qh_fprintf(fp, 9167, "    - maxoutside: %10.7g\n", facet->maxoutside);
    +#endif
    +  if (!SETempty_(facet->outsideset)) {
    +    furthest= (pointT*)qh_setlast(facet->outsideset);
    +    if (qh_setsize(facet->outsideset) < 6) {
    +      qh_fprintf(fp, 9168, "    - outside set(furthest p%d):\n", qh_pointid(furthest));
    +      FOREACHpoint_(facet->outsideset)
    +        qh_printpoint(fp, "     ", point);
    +    }else if (qh_setsize(facet->outsideset) < 21) {
    +      qh_printpoints(fp, "    - outside set:", facet->outsideset);
    +    }else {
    +      qh_fprintf(fp, 9169, "    - outside set:  %d points.", qh_setsize(facet->outsideset));
    +      qh_printpoint(fp, "  Furthest", furthest);
    +    }
    +#if !qh_COMPUTEfurthest
    +    qh_fprintf(fp, 9170, "    - furthest distance= %2.2g\n", facet->furthestdist);
    +#endif
    +  }
    +  if (!SETempty_(facet->coplanarset)) {
    +    furthest= (pointT*)qh_setlast(facet->coplanarset);
    +    if (qh_setsize(facet->coplanarset) < 6) {
    +      qh_fprintf(fp, 9171, "    - coplanar set(furthest p%d):\n", qh_pointid(furthest));
    +      FOREACHpoint_(facet->coplanarset)
    +        qh_printpoint(fp, "     ", point);
    +    }else if (qh_setsize(facet->coplanarset) < 21) {
    +      qh_printpoints(fp, "    - coplanar set:", facet->coplanarset);
    +    }else {
    +      qh_fprintf(fp, 9172, "    - coplanar set:  %d points.", qh_setsize(facet->coplanarset));
    +      qh_printpoint(fp, "  Furthest", furthest);
    +    }
    +    zinc_(Zdistio);
    +    qh_distplane(furthest, facet, &dist);
    +    qh_fprintf(fp, 9173, "      furthest distance= %2.2g\n", dist);
    +  }
    +  qh_printvertices(fp, "    - vertices:", facet->vertices);
    +  qh_fprintf(fp, 9174, "    - neighboring facets:");
    +  FOREACHneighbor_(facet) {
    +    if (neighbor == qh_MERGEridge)
    +      qh_fprintf(fp, 9175, " MERGE");
    +    else if (neighbor == qh_DUPLICATEridge)
    +      qh_fprintf(fp, 9176, " DUP");
    +    else
    +      qh_fprintf(fp, 9177, " f%d", neighbor->id);
    +  }
    +  qh_fprintf(fp, 9178, "\n");
    +  qh RANDOMdist= qh old_randomdist;
    +} /* printfacetheader */
    +
    +
    +/*---------------------------------
    +
    +  qh_printfacetridges( 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(FILE *fp, facetT *facet) {
    +  facetT *neighbor, **neighborp;
    +  ridgeT *ridge, **ridgep;
    +  int numridges= 0;
    +
    +
    +  if (facet->visible && qh NEWfacets) {
    +    qh_fprintf(fp, 9179, "    - ridges(ids may be garbage):");
    +    FOREACHridge_(facet->ridges)
    +      qh_fprintf(fp, 9180, " r%d", ridge->id);
    +    qh_fprintf(fp, 9181, "\n");
    +  }else {
    +    qh_fprintf(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(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(fp, ridge);
    +            numridges++;
    +          }
    +        }
    +      }
    +    }
    +    if (numridges != qh_setsize(facet->ridges)) {
    +      qh_fprintf(fp, 9183, "     - all ridges:");
    +      FOREACHridge_(facet->ridges)
    +        qh_fprintf(fp, 9184, " r%d", ridge->id);
    +        qh_fprintf(fp, 9185, "\n");
    +    }
    +    FOREACHridge_(facet->ridges) {
    +      if (!ridge->seen)
    +        qh_printridge(fp, ridge);
    +    }
    +  }
    +} /* printfacetridges */
    +
    +/*---------------------------------
    +
    +  qh_printfacets( 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(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 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(facetlist, facets, printall);
    +    center= qh_getcenter(vertices);
    +    qh_fprintf(fp, 9186, "%d 1\n", qh hull_dim);
    +    qh_printpointid(fp, NULL, qh hull_dim, center, qh_IDunknown);
    +    qh_memfree(center, qh normal_size);
    +    qh_settempfree(&vertices);
    +  }else if (format == qh_PRINTextremes) {
    +    if (qh DELAUNAY)
    +      qh_printextremes_d(fp, facetlist, facets, printall);
    +    else if (qh hull_dim == 2)
    +      qh_printextremes_2d(fp, facetlist, facets, printall);
    +    else
    +      qh_printextremes(fp, facetlist, facets, printall);
    +  }else if (format == qh_PRINToptions)
    +    qh_fprintf(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(fp, facetlist, facets, printall);
    +  else if (format == qh_PRINTqhull)
    +    qh_fprintf(fp, 9188, "%s | %s\n", qh rbox_command, qh qhull_command);
    +  else if (format == qh_PRINTsize) {
    +    qh_fprintf(fp, 9189, "0\n2 ");
    +    qh_fprintf(fp, 9190, qh_REAL_1, qh totarea);
    +    qh_fprintf(fp, 9191, qh_REAL_1, qh totvol);
    +    qh_fprintf(fp, 9192, "\n");
    +  }else if (format == qh_PRINTsummary) {
    +    qh_countfacets(facetlist, facets, printall, &numfacets, &numsimplicial,
    +      &totneighbors, &numridges, &numcoplanars, &numtricoplanars);
    +    vertices= qh_facetvertices(facetlist, facets, printall);
    +    qh_fprintf(fp, 9193, "10 %d %d %d %d %d %d %d %d %d %d\n2 ", qh hull_dim,
    +                qh num_points + qh_setsize(qh other_points),
    +                qh num_vertices, qh num_facets - qh num_visible,
    +                qh_setsize(vertices), numfacets, numcoplanars,
    +                numfacets - numsimplicial, zzval_(Zdelvertextot),
    +                numtricoplanars);
    +    qh_settempfree(&vertices);
    +    qh_outerinner(NULL, &outerplane, &innerplane);
    +    qh_fprintf(fp, 9194, qh_REAL_2n, outerplane, innerplane);
    +  }else if (format == qh_PRINTvneighbors)
    +    qh_printvneighbors(fp, facetlist, facets, printall);
    +  else if (qh VORONOI && format == qh_PRINToff)
    +    qh_printvoronoi(fp, format, facetlist, facets, printall);
    +  else if (qh VORONOI && format == qh_PRINTgeom) {
    +    qh_printbegin(fp, format, facetlist, facets, printall);
    +    qh_printvoronoi(fp, format, facetlist, facets, printall);
    +    qh_printend(fp, format, facetlist, facets, printall);
    +  }else if (qh VORONOI
    +  && (format == qh_PRINTvertices || format == qh_PRINTinner || format == qh_PRINTouter))
    +    qh_printvdiagram(fp, format, facetlist, facets, printall);
    +  else {
    +    qh_printbegin(fp, format, facetlist, facets, printall);
    +    FORALLfacet_(facetlist)
    +      qh_printafacet(fp, format, facet, printall);
    +    FOREACHfacet_(facets)
    +      qh_printafacet(fp, format, facet, printall);
    +    qh_printend(fp, format, facetlist, facets, printall);
    +  }
    +  qh RANDOMdist= qh old_randomdist;
    +} /* printfacets */
    +
    +
    +/*---------------------------------
    +
    +  qh_printhyperplaneintersection( 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(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(facet1->normal, facet2->normal);
    +  denominator= 1 - costheta * costheta;
    +  i= qh_setsize(vertices);
    +  if (qh hull_dim == 3)
    +    qh_fprintf(fp, 9195, "VECT 1 %d 1 %d 1 ", i, i);
    +  else if (qh hull_dim == 4 && qh DROPdim >= 0)
    +    qh_fprintf(fp, 9196, "OFF 3 1 1 ");
    +  else
    +    qh printoutvar++;
    +  qh_fprintf(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(vertex->point, facet1, &dist1);
    +    qh_distplane(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(p, p);
    +      qh_fprintf(fp, 9198, "%8.4g %8.4g %8.4g # ", p[0], p[1], p[2]);
    +    }else
    +      qh_fprintf(fp, 9199, "%8.4g %8.4g %8.4g %8.4g # ", p[0], p[1], p[2], p[3]);
    +    if (nearzero1+nearzero2)
    +      qh_fprintf(fp, 9200, "p%d(coplanar facets)\n", qh_pointid(vertex->point));
    +    else
    +      qh_fprintf(fp, 9201, "projected p%d\n", qh_pointid(vertex->point));
    +  }
    +  if (qh hull_dim == 3)
    +    qh_fprintf(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(fp, 9203, "3 0 1 2 %8.4g %8.4g %8.4g 1.0\n", color[0], color[1], color[2]);
    +} /* printhyperplaneintersection */
    +
    +/*---------------------------------
    +
    +  qh_printline3geom( 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(FILE *fp, pointT *pointA, pointT *pointB, realT color[3]) {
    +  int k;
    +  realT pA[4], pB[4];
    +
    +  qh_projectdim3(pointA, pA);
    +  qh_projectdim3(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(fp, 9204, "VECT 1 2 1 2 1\n");
    +    for (k=0; k < 3; k++)
    +       qh_fprintf(fp, 9205, "%8.4g ", pB[k]);
    +    qh_fprintf(fp, 9206, " # p%d\n", qh_pointid(pointB));
    +  }else
    +    qh_fprintf(fp, 9207, "VECT 1 1 1 1 1\n");
    +  for (k=0; k < 3; k++)
    +    qh_fprintf(fp, 9208, "%8.4g ", pA[k]);
    +  qh_fprintf(fp, 9209, " # p%d\n", qh_pointid(pointA));
    +  qh_fprintf(fp, 9210, "%8.4g %8.4g %8.4g 1\n", color[0], color[1], color[2]);
    +}
    +
    +/*---------------------------------
    +
    +  qh_printneighborhood( fp, format, facetA, facetB, printall )
    +    print neighborhood of one or two facets
    +
    +  notes:
    +    calls qh_findgood_all()
    +    bumps qh.visit_id
    +*/
    +void qh_printneighborhood(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 facet_list);
    +  if (facetA == facetB)
    +    facetB= NULL;
    +  facets= qh_settemp(2*(qh_setsize(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(&facets, facet);
    +    }
    +    FOREACHneighbor_(facet) {
    +      if (neighbor->visitid == qh visit_id)
    +        continue;
    +      neighbor->visitid= qh visit_id;
    +      if (printall || !qh_skipfacet(neighbor))
    +        qh_setappend(&facets, neighbor);
    +    }
    +  }
    +  qh_printfacets(fp, format, NULL, facets, printall);
    +  qh_settempfree(&facets);
    +} /* printneighborhood */
    +
    +/*---------------------------------
    +
    +  qh_printpoint( fp, string, point )
    +  qh_printpointid( 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(FILE *fp, const char *string, pointT *point) {
    +  int id= qh_pointid( point);
    +
    +  qh_printpointid( fp, string, qh hull_dim, point, id);
    +} /* printpoint */
    +
    +void qh_printpointid(FILE *fp, const char *string, int dim, pointT *point, int id) {
    +  int k;
    +  realT r; /*bug fix*/
    +
    +  if (!point)
    +    return;
    +  if (string) {
    +    qh_fprintf(fp, 9211, "%s", string);
    +    if (id != qh_IDunknown && id != qh_IDnone)
    +      qh_fprintf(fp, 9212, " p%d: ", id);
    +  }
    +  for (k=dim; k--; ) {
    +    r= *point++;
    +    if (string)
    +      qh_fprintf(fp, 9213, " %8.4g", r);
    +    else
    +      qh_fprintf(fp, 9214, qh_REAL_1, r);
    +  }
    +  qh_fprintf(fp, 9215, "\n");
    +} /* printpointid */
    +
    +/*---------------------------------
    +
    +  qh_printpoint3( fp, point )
    +    prints 2-d, 3-d, or 4-d point as Geomview 3-d coordinates
    +*/
    +void qh_printpoint3(FILE *fp, pointT *point) {
    +  int k;
    +  realT p[4];
    +
    +  qh_projectdim3(point, p);
    +  for (k=0; k < 3; k++)
    +    qh_fprintf(fp, 9216, "%8.4g ", p[k]);
    +  qh_fprintf(fp, 9217, " # p%d\n", qh_pointid(point));
    +} /* printpoint3 */
    +
    +/*----------------------------------------
    +-printpoints- print pointids for a set of points starting at index
    +   see geom.c
    +*/
    +
    +/*---------------------------------
    +
    +  qh_printpoints_out( 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(FILE *fp, facetT *facetlist, setT *facets, boolT printall) {
    +  int allpoints= qh num_points + qh_setsize(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(allpoints);
    +  qh_setzero(points, 0, allpoints);
    +  vertices= qh_facetvertices(facetlist, facets, printall);
    +  FOREACHvertex_(vertices) {
    +    id= qh_pointid(vertex->point);
    +    if (id >= 0)
    +      SETelem_(points, id)= vertex->point;
    +  }
    +  if (qh KEEPinside || qh KEEPcoplanar || qh KEEPnearinside) {
    +    FORALLfacet_(facetlist) {
    +      if (!printall && qh_skipfacet(facet))
    +        continue;
    +      FOREACHpoint_(facet->coplanarset) {
    +        id= qh_pointid(point);
    +        if (id >= 0)
    +          SETelem_(points, id)= point;
    +      }
    +    }
    +    FOREACHfacet_(facets) {
    +      if (!printall && qh_skipfacet(facet))
    +        continue;
    +      FOREACHpoint_(facet->coplanarset) {
    +        id= qh_pointid(point);
    +        if (id >= 0)
    +          SETelem_(points, id)= point;
    +      }
    +    }
    +  }
    +  qh_settempfree(&vertices);
    +  FOREACHpoint_i_(points) {
    +    if (point)
    +      numpoints++;
    +  }
    +  if (qh CDDoutput)
    +    qh_fprintf(fp, 9218, "%s | %s\nbegin\n%d %d real\n", qh rbox_command,
    +             qh qhull_command, numpoints, qh hull_dim + 1);
    +  else
    +    qh_fprintf(fp, 9219, "%d\n%d\n", qh hull_dim, numpoints);
    +  FOREACHpoint_i_(points) {
    +    if (point) {
    +      if (qh CDDoutput)
    +        qh_fprintf(fp, 9220, "1 ");
    +      qh_printpoint(fp, NULL, point);
    +    }
    +  }
    +  if (qh CDDoutput)
    +    qh_fprintf(fp, 9221, "end\n");
    +  qh_settempfree(&points);
    +} /* printpoints_out */
    +
    +
    +/*---------------------------------
    +
    +  qh_printpointvect( 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(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(diff, qh hull_dim, True, NULL, NULL);
    +  for (k=qh hull_dim; k--; )
    +    pointA[k]= point[k]+diff[k] * radius;
    +  qh_printline3geom(fp, point, pointA, color);
    +} /* printpointvect */
    +
    +/*---------------------------------
    +
    +  qh_printpointvect2( 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(FILE *fp, pointT *point, coordT *normal, pointT *center, realT radius) {
    +  realT red[3]={1, 0, 0}, yellow[3]={1, 1, 0};
    +
    +  qh_printpointvect(fp, point, normal, center, radius, red);
    +  qh_printpointvect(fp, point, normal, center, -radius, yellow);
    +} /* printpointvect2 */
    +
    +/*---------------------------------
    +
    +  qh_printridge( fp, ridge )
    +    prints the information in a ridge
    +
    +  notes:
    +    for qh_printfacetridges()
    +    same as operator<< [QhullRidge.cpp]
    +*/
    +void qh_printridge(FILE *fp, ridgeT *ridge) {
    +
    +  qh_fprintf(fp, 9222, "     - r%d", ridge->id);
    +  if (ridge->tested)
    +    qh_fprintf(fp, 9223, " tested");
    +  if (ridge->nonconvex)
    +    qh_fprintf(fp, 9224, " nonconvex");
    +  qh_fprintf(fp, 9225, "\n");
    +  qh_printvertices(fp, "           vertices:", ridge->vertices);
    +  if (ridge->top && ridge->bottom)
    +    qh_fprintf(fp, 9226, "           between f%d and f%d\n",
    +            ridge->top->id, ridge->bottom->id);
    +} /* printridge */
    +
    +/*---------------------------------
    +
    +  qh_printspheres( fp, vertices, radius )
    +    prints 3-d vertices as OFF spheres
    +
    +  notes:
    +    inflated octahedron from Stuart Levy earth/mksphere2
    +*/
    +void qh_printspheres(FILE *fp, setT *vertices, realT radius) {
    +  vertexT *vertex, **vertexp;
    +
    +  qh printoutnum++;
    +  qh_fprintf(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(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(fp, vertex->point);
    +    qh_fprintf(fp, 9229, "1\n");
    +  }
    +  qh_fprintf(fp, 9230, "}}}\n");
    +} /* printspheres */
    +
    +
    +/*----------------------------------------------
    +-printsummary-
    +                see libqhull.c
    +*/
    +
    +/*---------------------------------
    +
    +  qh_printvdiagram( 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(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 ferr, 6219, "Qhull internal error (qh_printvdiagram): unknown print format %d.\n", format);
    +    qh_errexit(qh_ERRinput, NULL, NULL);
    +  }
    +  vertices= qh_markvoronoi(facetlist, facets, printall, &isLower, &numcenters);
    +  totcount= qh_printvdiagram2(NULL, NULL, vertices, innerouter, False);
    +  qh_fprintf(fp, 9231, "%d\n", totcount);
    +  totcount= qh_printvdiagram2(fp, printvridge, vertices, innerouter, True /* inorder*/);
    +  qh_settempfree(&vertices);
    +#if 0  /* for testing qh_eachvoronoi_all */
    +  qh_fprintf(fp, 9232, "\n");
    +  totcount= qh_eachvoronoi_all(fp, printvridge, qh UPPERdelaunay, innerouter, True /* inorder*/);
    +  qh_fprintf(fp, 9233, "%d\n", totcount);
    +#endif
    +} /* printvdiagram */
    +
    +/*---------------------------------
    +
    +  qh_printvdiagram2( 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(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_(vertices) {
    +    if (vertex) {
    +      if (qh GOODvertex > 0 && qh_pointid(vertex->point)+1 != qh GOODvertex)
    +        continue;
    +      totcount += qh_eachvoronoi(fp, printvridge, vertex, !qh_ALL, innerouter, inorder);
    +    }
    +  }
    +  return totcount;
    +} /* printvdiagram2 */
    +
    +/*---------------------------------
    +
    +  qh_printvertex( fp, vertex )
    +    prints the information in a vertex
    +    Duplicated as operator<< [QhullVertex.cpp]
    +*/
    +void qh_printvertex(FILE *fp, vertexT *vertex) {
    +  pointT *point;
    +  int k, count= 0;
    +  facetT *neighbor, **neighborp;
    +  realT r; /*bug fix*/
    +
    +  if (!vertex) {
    +    qh_fprintf(fp, 9234, "  NULLvertex\n");
    +    return;
    +  }
    +  qh_fprintf(fp, 9235, "- p%d(v%d):", qh_pointid(vertex->point), vertex->id);
    +  point= vertex->point;
    +  if (point) {
    +    for (k=qh hull_dim; k--; ) {
    +      r= *point++;
    +      qh_fprintf(fp, 9236, " %5.2g", r);
    +    }
    +  }
    +  if (vertex->deleted)
    +    qh_fprintf(fp, 9237, " deleted");
    +  if (vertex->delridge)
    +    qh_fprintf(fp, 9238, " ridgedeleted");
    +  qh_fprintf(fp, 9239, "\n");
    +  if (vertex->neighbors) {
    +    qh_fprintf(fp, 9240, "  neighbors:");
    +    FOREACHneighbor_(vertex) {
    +      if (++count % 100 == 0)
    +        qh_fprintf(fp, 9241, "\n     ");
    +      qh_fprintf(fp, 9242, " f%d", neighbor->id);
    +    }
    +    qh_fprintf(fp, 9243, "\n");
    +  }
    +} /* printvertex */
    +
    +
    +/*---------------------------------
    +
    +  qh_printvertexlist( fp, string, facetlist, facets, printall )
    +    prints vertices used by a facetlist or facet set
    +    tests qh_skipfacet() if !printall
    +*/
    +void qh_printvertexlist(FILE *fp, const char* string, facetT *facetlist,
    +                         setT *facets, boolT printall) {
    +  vertexT *vertex, **vertexp;
    +  setT *vertices;
    +
    +  vertices= qh_facetvertices(facetlist, facets, printall);
    +  qh_fprintf(fp, 9244, "%s", string);
    +  FOREACHvertex_(vertices)
    +    qh_printvertex(fp, vertex);
    +  qh_settempfree(&vertices);
    +} /* printvertexlist */
    +
    +
    +/*---------------------------------
    +
    +  qh_printvertices( fp, string, vertices )
    +    prints vertices in a set
    +    duplicated as printVertexSet [QhullVertex.cpp]
    +*/
    +void qh_printvertices(FILE *fp, const char* string, setT *vertices) {
    +  vertexT *vertex, **vertexp;
    +
    +  qh_fprintf(fp, 9245, "%s", string);
    +  FOREACHvertex_(vertices)
    +    qh_fprintf(fp, 9246, " p%d(v%d)", qh_pointid(vertex->point), vertex->id);
    +  qh_fprintf(fp, 9247, "\n");
    +} /* printvertices */
    +
    +/*---------------------------------
    +
    +  qh_printvneighbors( 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(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 other_points);
    +  vertexT *vertex, **vertexp;
    +  int vertex_i, vertex_n;
    +  facetT *facet, **facetp, *neighbor, **neighborp;
    +  pointT *point, **pointp;
    +
    +  qh_countfacets(facetlist, facets, printall, &numfacets, &numsimplicial,
    +      &totneighbors, &numridges, &numcoplanars, &numtricoplanars);  /* sets facet->visitid */
    +  qh_fprintf(fp, 9248, "%d\n", numpoints);
    +  qh_vertexneighbors();
    +  vertices= qh_facetvertices(facetlist, facets, printall);
    +  vertex_points= qh_settemp(numpoints);
    +  coplanar_points= qh_settemp(numpoints);
    +  qh_setzero(vertex_points, 0, numpoints);
    +  qh_setzero(coplanar_points, 0, numpoints);
    +  FOREACHvertex_(vertices)
    +    qh_point_add(vertex_points, vertex->point, vertex);
    +  FORALLfacet_(facetlist) {
    +    FOREACHpoint_(facet->coplanarset)
    +      qh_point_add(coplanar_points, point, facet);
    +  }
    +  FOREACHfacet_(facets) {
    +    FOREACHpoint_(facet->coplanarset)
    +      qh_point_add(coplanar_points, point, facet);
    +  }
    +  FOREACHvertex_i_(vertex_points) {
    +    if (vertex) {
    +      numneighbors= qh_setsize(vertex->neighbors);
    +      qh_fprintf(fp, 9249, "%d", numneighbors);
    +      if (qh hull_dim == 3)
    +        qh_order_vertexneighbors(vertex);
    +      else if (qh hull_dim >= 4)
    +        qsort(SETaddr_(vertex->neighbors, facetT), (size_t)numneighbors,
    +             sizeof(facetT *), qh_compare_facetvisit);
    +      FOREACHneighbor_(vertex)
    +        qh_fprintf(fp, 9250, " %d",
    +                 neighbor->visitid ? neighbor->visitid - 1 : 0 - neighbor->id);
    +      qh_fprintf(fp, 9251, "\n");
    +    }else if ((facet= SETelemt_(coplanar_points, vertex_i, facetT)))
    +      qh_fprintf(fp, 9252, "1 %d\n",
    +                  facet->visitid ? facet->visitid - 1 : 0 - facet->id);
    +    else
    +      qh_fprintf(fp, 9253, "0\n");
    +  }
    +  qh_settempfree(&coplanar_points);
    +  qh_settempfree(&vertex_points);
    +  qh_settempfree(&vertices);
    +} /* printvneighbors */
    +
    +/*---------------------------------
    +
    +  qh_printvoronoi( 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(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(facetlist, facets, printall, &isLower, &numcenters);
    +  FOREACHvertex_i_(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(fp, 9254, "{appearance {+edge -face} OFF %d %d 1 # Voronoi centers and cells\n",
    +                numcenters, numvertices);
    +  else
    +    qh_fprintf(fp, 9255, "%d\n%d %d 1\n", qh hull_dim-1, numcenters, qh_setsize(vertices));
    +  if (format == qh_PRINTgeom) {
    +    for (k=qh hull_dim-1; k--; )
    +      qh_fprintf(fp, 9256, qh_REAL_1, 0.0);
    +    qh_fprintf(fp, 9257, " 0 # infinity not used\n");
    +  }else {
    +    for (k=qh hull_dim-1; k--; )
    +      qh_fprintf(fp, 9258, qh_REAL_1, qh_INFINITE);
    +    qh_fprintf(fp, 9259, "\n");
    +  }
    +  FORALLfacet_(facetlist) {
    +    if (facet->visitid && facet->visitid < numfacets) {
    +      if (format == qh_PRINTgeom)
    +        qh_fprintf(fp, 9260, "# %d f%d\n", vid++, facet->id);
    +      qh_printcenter(fp, format, NULL, facet);
    +    }
    +  }
    +  FOREACHfacet_(facets) {
    +    if (facet->visitid && facet->visitid < numfacets) {
    +      if (format == qh_PRINTgeom)
    +        qh_fprintf(fp, 9261, "# %d f%d\n", vid++, facet->id);
    +      qh_printcenter(fp, format, NULL, facet);
    +    }
    +  }
    +  FOREACHvertex_i_(vertices) {
    +    numneighbors= 0;
    +    numinf=0;
    +    if (vertex) {
    +      if (qh hull_dim == 3)
    +        qh_order_vertexneighbors(vertex);
    +      else if (qh hull_dim >= 4)
    +        qsort(SETaddr_(vertex->neighbors, facetT),
    +             (size_t)qh_setsize(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(fp, 9262, "%d", numneighbors);
    +        FOREACHneighbor_(vertex) {
    +          if (neighbor->visitid && neighbor->visitid < numfacets)
    +            qh_fprintf(fp, 9263, " %d", neighbor->visitid);
    +        }
    +        qh_fprintf(fp, 9264, " # p%d(v%d)\n", vertex_i, vertex->id);
    +      }else
    +        qh_fprintf(fp, 9265, " # p%d is coplanar or isolated\n", vertex_i);
    +    }else {
    +      if (numinf)
    +        numneighbors++;
    +      qh_fprintf(fp, 9266, "%d", numneighbors);
    +      if (vertex) {
    +        FOREACHneighbor_(vertex) {
    +          if (neighbor->visitid == 0) {
    +            if (numinf) {
    +              numinf= 0;
    +              qh_fprintf(fp, 9267, " %d", neighbor->visitid);
    +            }
    +          }else if (neighbor->visitid < numfacets)
    +            qh_fprintf(fp, 9268, " %d", neighbor->visitid);
    +        }
    +      }
    +      qh_fprintf(fp, 9269, "\n");
    +    }
    +  }
    +  if (format == qh_PRINTgeom)
    +    qh_fprintf(fp, 9270, "}\n");
    +  qh_settempfree(&vertices);
    +} /* printvoronoi */
    +
    +/*---------------------------------
    +
    +  qh_printvnorm( 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(FILE *fp, vertexT *vertex, vertexT *vertexA, setT *centers, boolT unbounded) {
    +  pointT *normal;
    +  realT offset;
    +  int k;
    +  QHULL_UNUSED(unbounded);
    +
    +  normal= qh_detvnorm(vertex, vertexA, centers, &offset);
    +  qh_fprintf(fp, 9271, "%d %d %d ",
    +      2+qh hull_dim, qh_pointid(vertex->point), qh_pointid(vertexA->point));
    +  for (k=0; k< qh hull_dim-1; k++)
    +    qh_fprintf(fp, 9272, qh_REAL_1, normal[k]);
    +  qh_fprintf(fp, 9273, qh_REAL_1, offset);
    +  qh_fprintf(fp, 9274, "\n");
    +} /* printvnorm */
    +
    +/*---------------------------------
    +
    +  qh_printvridge( 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(FILE *fp, vertexT *vertex, vertexT *vertexA, setT *centers, boolT unbounded) {
    +  facetT *facet, **facetp;
    +  QHULL_UNUSED(unbounded);
    +
    +  qh_fprintf(fp, 9275, "%d %d %d", qh_setsize(centers)+2,
    +       qh_pointid(vertex->point), qh_pointid(vertexA->point));
    +  FOREACHfacet_(centers)
    +    qh_fprintf(fp, 9276, " %d", facet->visitid);
    +  qh_fprintf(fp, 9277, "\n");
    +} /* printvridge */
    +
    +/*---------------------------------
    +
    +  qh_projectdim3( 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(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 */
    +
    +/*---------------------------------
    +
    +  qh_readfeasible( 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(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 ferr, 6070, "qhull input error: feasible point(dim 1 coords) is only valid for halfspace intersection\n");
    +    qh_errexit(qh_ERRinput, NULL, NULL);
    +  }
    +  if (qh feasible_string)
    +    qh_fprintf(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 ferr, 6071, "qhull error: insufficient memory for feasible point\n");
    +    qh_errexit(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 ferr, 6072, "qhull input error: coordinates for feasible point do not finish out the line: %s\n",
    +               s);
    +          qh_errexit(qh_ERRinput, NULL, NULL);
    +        }
    +        return linecount;
    +      }
    +    }
    +  }
    +  qh_fprintf(qh ferr, 6073, "qhull input error: only %d coordinates.  Could not read %d-d feasible point.\n",
    +           tokcount, dim);
    +  qh_errexit(qh_ERRinput, NULL, NULL);
    +  return 0;
    +} /* readfeasible */
    +
    +/*---------------------------------
    +
    +  qh_readpoints( 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(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(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 ferr, 6074, "qhull input error: missing \"begin\" for cdd-formated input\n");
    +      qh_errexit(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(diminput, s); /* checks if ok */
    +          dimfeasible= diminput;
    +          diminput= numinput= 0;
    +        }else
    +          break;
    +      }
    +    }
    +  }
    +  if (!s) {
    +    qh_fprintf(qh ferr, 6075, "qhull input error: short input file.  Did not find dimension and number of points\n");
    +    qh_errexit(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 ferr, 6220,"qhull input error: dimension %d(first number) should be at least 2\n",
    +            diminput);
    +    qh_errexit(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 ferr, 6221,"qhull input error: dimension %d(first number, includes offset) should be at least 3 for halfspaces\n",
    +            diminput);
    +      qh_errexit(qh_ERRinput, NULL, NULL);
    +    }
    +    if (dimfeasible) {
    +      if (dimfeasible != *dimension) {
    +        qh_fprintf(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_ERRinput, NULL, NULL);
    +      }
    +    }else
    +      qh_setfeasible(*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 ferr, 6076, "qhull error: insufficient memory to read %d points\n",
    +            numinput);
    +    qh_errexit(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 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 ferr, 6077, "qhull input error: for cdd format, point at line %d does not start with '1'\n",
    +                   linecount);
    +            qh_errexit(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(*dimension, coords, &coords, normalp, offsetp, qh feasible_point)) {
    +            qh_fprintf(qh ferr, 8048, "The halfspace was on line %d\n", linecount);
    +            if (wasbegin)
    +              qh_fprintf(qh ferr, 8049, "The input appears to be in cdd format.  If so, you should use option 'Fd'\n");
    +            qh_errexit(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 ferr, 6078, "qhull input error: line %d contained more than %d characters\n",
    +              linecount, (int) (s - qh line));   /* WARN64 */
    +      qh_errexit(qh_ERRinput, NULL, NULL);
    +    }
    +    isfirst= False;
    +  }
    +  if (tokcount != maxcount) {
    +    newnum= fmin_(numinput, tokcount/diminput);
    +    qh_fprintf(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 ferr, 8051, ", line %d is the first comment", firsttext);
    +    if (firstshort)
    +      qh_fprintf(qh ferr, 8052, ", line %d is the first short\nline", firstshort);
    +    if (firstlong)
    +      qh_fprintf(qh ferr, 8053, ", line %d is the first long line", firstlong);
    +    qh_fprintf(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 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 ferr, 1008,"qh_readpoints: read in %d %d-dimensional points\n",
    +          numinput, diminput));
    +  return(points);
    +} /* readpoints */
    +
    +
    +/*---------------------------------
    +
    +  qh_setfeasible( 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(int dim) {
    +  int tokcount= 0;
    +  char *s;
    +  coordT *coords, value;
    +
    +  if (!(s= qh feasible_string)) {
    +    qh_fprintf(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_ERRinput, NULL, NULL);
    +  }
    +  if (!(qh feasible_point= (pointT*)qh_malloc(dim * sizeof(coordT)))) {
    +    qh_fprintf(qh ferr, 6079, "qhull error: insufficient memory for 'Hn,n,n'\n");
    +    qh_errexit(qh_ERRmem, NULL, NULL);
    +  }
    +  coords= qh feasible_point;
    +  while (*s) {
    +    value= qh_strtod(s, &s);
    +    if (++tokcount > dim) {
    +      qh_fprintf(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 */
    +
    +/*---------------------------------
    +
    +  qh_skipfacet( 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(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(facet->normal, NULL));
    +} /* skipfacet */
    +
    +/*---------------------------------
    +
    +  qh_skipfilename( 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(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 ferr, 6204, "qhull input error: filename expected, none found.\n");
    +    qh_errexit(qh_ERRinput, NULL, NULL);
    +  }
    +  if (c == '\'' || c == '"') {
    +    while (*s !=c || s[-1] == '\\') {
    +      if (!*s) {
    +        qh_fprintf(qh ferr, 6203, "qhull input error: missing quote after filename -- %s\n", filename);
    +        qh_errexit(qh_ERRinput, NULL, NULL);
    +      }
    +      s++;
    +    }
    +    s++;
    +  }
    +  else while (*s && !isspace(*s))
    +      s++;
    +  return s;
    +} /* skipfilename */
    +
    diff --git a/xs/src/qhull/src/libqhull/io.h b/xs/src/qhull/src/libqhull/io.h
    new file mode 100644
    index 000000000..eca0369d3
    --- /dev/null
    +++ b/xs/src/qhull/src/libqhull/io.h
    @@ -0,0 +1,159 @@
    +/*
      ---------------------------------
    +
    +   io.h
    +   declarations of Input/Output functions
    +
    +   see README, libqhull.h and io.c
    +
    +   Copyright (c) 1993-2015 The Geometry Center.
    +   $Id: //main/2015/qhull/src/libqhull/io.h#1 $$Change: 1981 $
    +   $DateTime: 2015/09/28 20:26:32 $$Author: bbarber $
    +*/
    +
    +#ifndef qhDEFio
    +#define qhDEFio 1
    +
    +#include "libqhull.h"
    +
    +/*============ constants and flags ==================*/
    +
    +/*----------------------------------
    +
    +  qh_MAXfirst
    +    maximum length of first two lines of stdin
    +*/
    +#define qh_MAXfirst  200
    +
    +/*----------------------------------
    +
    +  qh_MINradius
    +    min radius for Gp and Gv, fraction of maxcoord
    +*/
    +#define qh_MINradius 0.02
    +
    +/*----------------------------------
    +
    +  qh_GEOMepsilon
    +    adjust outer planes for 'lines closer' and geomview roundoff.
    +    This prevents bleed through.
    +*/
    +#define qh_GEOMepsilon 2e-3
    +
    +/*----------------------------------
    +
    +  qh_WHITESPACE
    +    possible values of white space
    +*/
    +#define qh_WHITESPACE " \n\t\v\r\f"
    +
    +
    +/*----------------------------------
    +
    +  qh_RIDGE
    +    to select which ridges to print in qh_eachvoronoi
    +*/
    +typedef enum
    +{
    +    qh_RIDGEall = 0, qh_RIDGEinner, qh_RIDGEouter
    +}
    +qh_RIDGE;
    +
    +/*----------------------------------
    +
    +  printvridgeT
    +    prints results of qh_printvdiagram
    +
    +  see:
    +    qh_printvridge for an example
    +*/
    +typedef void (*printvridgeT)(FILE *fp, vertexT *vertex, vertexT *vertexA, setT *centers, boolT unbounded);
    +
    +/*============== -prototypes in alphabetical order =========*/
    +
    +void    qh_dfacet(unsigned id);
    +void    qh_dvertex(unsigned id);
    +int     qh_compare_facetarea(const void *p1, const void *p2);
    +int     qh_compare_facetmerge(const void *p1, const void *p2);
    +int     qh_compare_facetvisit(const void *p1, const void *p2);
    +int     qh_compare_vertexpoint(const void *p1, const void *p2); /* not used, not in libqhull_r.h */
    +void    qh_copyfilename(char *filename, int size, const char* source, int length);
    +void    qh_countfacets(facetT *facetlist, setT *facets, boolT printall,
    +              int *numfacetsp, int *numsimplicialp, int *totneighborsp,
    +              int *numridgesp, int *numcoplanarsp, int *numnumtricoplanarsp);
    +pointT *qh_detvnorm(vertexT *vertex, vertexT *vertexA, setT *centers, realT *offsetp);
    +setT   *qh_detvridge(vertexT *vertex);
    +setT   *qh_detvridge3(vertexT *atvertex, vertexT *vertex);
    +int     qh_eachvoronoi(FILE *fp, printvridgeT printvridge, vertexT *atvertex, boolT visitall, qh_RIDGE innerouter, boolT inorder);
    +int     qh_eachvoronoi_all(FILE *fp, printvridgeT printvridge, boolT isUpper, qh_RIDGE innerouter, boolT inorder);
    +void    qh_facet2point(facetT *facet, pointT **point0, pointT **point1, realT *mindist);
    +setT   *qh_facetvertices(facetT *facetlist, setT *facets, boolT allfacets);
    +void    qh_geomplanes(facetT *facet, realT *outerplane, realT *innerplane);
    +void    qh_markkeep(facetT *facetlist);
    +setT   *qh_markvoronoi(facetT *facetlist, setT *facets, boolT printall, boolT *isLowerp, int *numcentersp);
    +void    qh_order_vertexneighbors(vertexT *vertex);
    +void    qh_prepare_output(void);
    +void    qh_printafacet(FILE *fp, qh_PRINT format, facetT *facet, boolT printall);
    +void    qh_printbegin(FILE *fp, qh_PRINT format, facetT *facetlist, setT *facets, boolT printall);
    +void    qh_printcenter(FILE *fp, qh_PRINT format, const char *string, facetT *facet);
    +void    qh_printcentrum(FILE *fp, facetT *facet, realT radius);
    +void    qh_printend(FILE *fp, qh_PRINT format, facetT *facetlist, setT *facets, boolT printall);
    +void    qh_printend4geom(FILE *fp, facetT *facet, int *num, boolT printall);
    +void    qh_printextremes(FILE *fp, facetT *facetlist, setT *facets, boolT printall);
    +void    qh_printextremes_2d(FILE *fp, facetT *facetlist, setT *facets, boolT printall);
    +void    qh_printextremes_d(FILE *fp, facetT *facetlist, setT *facets, boolT printall);
    +void    qh_printfacet(FILE *fp, facetT *facet);
    +void    qh_printfacet2math(FILE *fp, facetT *facet, qh_PRINT format, int notfirst);
    +void    qh_printfacet2geom(FILE *fp, facetT *facet, realT color[3]);
    +void    qh_printfacet2geom_points(FILE *fp, pointT *point1, pointT *point2,
    +                               facetT *facet, realT offset, realT color[3]);
    +void    qh_printfacet3math(FILE *fp, facetT *facet, qh_PRINT format, int notfirst);
    +void    qh_printfacet3geom_nonsimplicial(FILE *fp, facetT *facet, realT color[3]);
    +void    qh_printfacet3geom_points(FILE *fp, setT *points, facetT *facet, realT offset, realT color[3]);
    +void    qh_printfacet3geom_simplicial(FILE *fp, facetT *facet, realT color[3]);
    +void    qh_printfacet3vertex(FILE *fp, facetT *facet, qh_PRINT format);
    +void    qh_printfacet4geom_nonsimplicial(FILE *fp, facetT *facet, realT color[3]);
    +void    qh_printfacet4geom_simplicial(FILE *fp, facetT *facet, realT color[3]);
    +void    qh_printfacetNvertex_nonsimplicial(FILE *fp, facetT *facet, int id, qh_PRINT format);
    +void    qh_printfacetNvertex_simplicial(FILE *fp, facetT *facet, qh_PRINT format);
    +void    qh_printfacetheader(FILE *fp, facetT *facet);
    +void    qh_printfacetridges(FILE *fp, facetT *facet);
    +void    qh_printfacets(FILE *fp, qh_PRINT format, facetT *facetlist, setT *facets, boolT printall);
    +void    qh_printhyperplaneintersection(FILE *fp, facetT *facet1, facetT *facet2,
    +                   setT *vertices, realT color[3]);
    +void    qh_printneighborhood(FILE *fp, qh_PRINT format, facetT *facetA, facetT *facetB, boolT printall);
    +void    qh_printline3geom(FILE *fp, pointT *pointA, pointT *pointB, realT color[3]);
    +void    qh_printpoint(FILE *fp, const char *string, pointT *point);
    +void    qh_printpointid(FILE *fp, const char *string, int dim, pointT *point, int id);
    +void    qh_printpoint3(FILE *fp, pointT *point);
    +void    qh_printpoints_out(FILE *fp, facetT *facetlist, setT *facets, boolT printall);
    +void    qh_printpointvect(FILE *fp, pointT *point, coordT *normal, pointT *center, realT radius, realT color[3]);
    +void    qh_printpointvect2(FILE *fp, pointT *point, coordT *normal, pointT *center, realT radius);
    +void    qh_printridge(FILE *fp, ridgeT *ridge);
    +void    qh_printspheres(FILE *fp, setT *vertices, realT radius);
    +void    qh_printvdiagram(FILE *fp, qh_PRINT format, facetT *facetlist, setT *facets, boolT printall);
    +int     qh_printvdiagram2(FILE *fp, printvridgeT printvridge, setT *vertices, qh_RIDGE innerouter, boolT inorder);
    +void    qh_printvertex(FILE *fp, vertexT *vertex);
    +void    qh_printvertexlist(FILE *fp, const char* string, facetT *facetlist,
    +                         setT *facets, boolT printall);
    +void    qh_printvertices(FILE *fp, const char* string, setT *vertices);
    +void    qh_printvneighbors(FILE *fp, facetT* facetlist, setT *facets, boolT printall);
    +void    qh_printvoronoi(FILE *fp, qh_PRINT format, facetT *facetlist, setT *facets, boolT printall);
    +void    qh_printvnorm(FILE *fp, vertexT *vertex, vertexT *vertexA, setT *centers, boolT unbounded);
    +void    qh_printvridge(FILE *fp, vertexT *vertex, vertexT *vertexA, setT *centers, boolT unbounded);
    +void    qh_produce_output(void);
    +void    qh_produce_output2(void);
    +void    qh_projectdim3(pointT *source, pointT *destination);
    +int     qh_readfeasible(int dim, const char *curline);
    +coordT *qh_readpoints(int *numpoints, int *dimension, boolT *ismalloc);
    +void    qh_setfeasible(int dim);
    +boolT   qh_skipfacet(facetT *facet);
    +char   *qh_skipfilename(char *filename);
    +
    +#endif /* qhDEFio */
    diff --git a/xs/src/qhull/src/libqhull/libqhull.c b/xs/src/qhull/src/libqhull/libqhull.c
    new file mode 100644
    index 000000000..7696a8a9f
    --- /dev/null
    +++ b/xs/src/qhull/src/libqhull/libqhull.c
    @@ -0,0 +1,1403 @@
    +/*
      ---------------------------------
    +
    +   libqhull.c
    +   Quickhull algorithm for convex hulls
    +
    +   qhull() and top-level routines
    +
    +   see qh-qhull.htm, libqhull.h, unix.c
    +
    +   see qhull_a.h for internal functions
    +
    +   Copyright (c) 1993-2015 The Geometry Center.
    +   $Id: //main/2015/qhull/src/libqhull/libqhull.c#3 $$Change: 2047 $
    +   $DateTime: 2016/01/04 22:03:18 $$Author: bbarber $
    +*/
    +
    +#include "qhull_a.h"
    +
    +/*============= functions in alphabetic order after qhull() =======*/
    +
    +/*---------------------------------
    +
    +  qh_qhull()
    +    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(void) {
    +  int numoutside;
    +
    +  qh hulltime= qh_CPUclock;
    +  if (qh RERUN || qh JOGGLEmax < REALmax/2)
    +    qh_build_withrestart();
    +  else {
    +    qh_initbuild();
    +    qh_buildhull();
    +  }
    +  if (!qh STOPpoint && !qh STOPcone) {
    +    if (qh ZEROall_ok && !qh TESTvneighbors && qh MERGEexact)
    +      qh_checkzero( qh_ALL);
    +    if (qh ZEROall_ok && !qh TESTvneighbors && !qh WAScoplanar) {
    +      trace2((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("First post-merge", qh premerge_centrum, qh premerge_cos,
    +             (qh POSTmerge ? False : qh TESTvneighbors));
    +      else if (!qh POSTmerge && qh TESTvneighbors)
    +        qh_postmerge("For testing vertex neighbors", qh premerge_centrum,
    +             qh premerge_cos, True);
    +      if (qh POSTmerge)
    +        qh_postmerge("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.visible_list*/ !qh_ALL, &numoutside);
    +        qh findbestnew= False;
    +        qh_deletevisible(/*qh.visible_list*/);
    +        qh_resetlists(False, qh_RESETvisible /*qh.visible_list newvertex_list newfacet_list */);
    +      }
    +    }
    +    if (qh DOcheckmax){
    +      if (qh REPORTfreq) {
    +        qh_buildtracing(NULL, NULL);
    +        qh_fprintf(qh ferr, 8115, "\nTesting all coplanar points.\n");
    +      }
    +      qh_check_maxout();
    +    }
    +    if (qh KEEPnearinside && !qh maxoutdone)
    +      qh_nearcoplanar();
    +  }
    +  if (qh_setsize(qhmem.tempstack) != 0) {
    +    qh_fprintf(qh ferr, 6164, "qhull internal error (qh_qhull): temporary sets not empty(%d)\n",
    +             qh_setsize(qhmem.tempstack));
    +    qh_errexit(qh_ERRqhull, NULL, NULL);
    +  }
    +  qh hulltime= qh_CPUclock - qh hulltime;
    +  qh QHULLfinished= True;
    +  trace1((qh ferr, 1036, "Qhull: algorithm completed\n"));
    +} /* qhull */
    +
    +/*---------------------------------
    +
    +  qh_addpoint( 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(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(furthest) == qh_IDunknown)
    +    qh_setappend(&qh other_points, furthest);
    +  if (!facet) {
    +    qh_fprintf(qh ferr, 6213, "qhull internal error (qh_addpoint): NULL facet.  Need to call qh_findbestfacet first\n");
    +    qh_errexit(qh_ERRqhull, NULL, NULL);
    +  }
    +  if (checkdist) {
    +    facet= qh_findbest(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(furthest, facet, &dist);
    +      return True;
    +    }
    +  }
    +  qh_buildtracing(furthest, facet);
    +  if (qh STOPpoint < 0 && qh furthest_id == -qh STOPpoint-1) {
    +    facet->notfurthest= True;
    +    return False;
    +  }
    +  qh_findhorizon(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(False, qh_RESETvisible /*qh.visible_list newvertex_list newfacet_list */);
    +    return True;
    +  }
    +  zzinc_(Zprocessed);
    +  firstnew= qh facet_id;
    +  vertex= qh_makenewfacets(furthest /*visible_list, attaches if !ONLYgood */);
    +  qh_makenewplanes(/* 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 newfacet_list, goodhorizon) && !qh GOODclosest) {
    +    FORALLnew_facets
    +      qh_delfacet(newfacet);
    +    qh_delvertex(vertex);
    +    qh_resetlists(True, qh_RESETvisible /*qh.visible_list newvertex_list newfacet_list */);
    +    zinc_(Znotgoodnew);
    +    facet->notfurthest= True;
    +    return True;
    +  }
    +  if (qh ONLYgood)
    +    qh_attachnewfacets(/*visible_list*/);
    +  qh_matchnewfacets();
    +  qh_updatevertices();
    +  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(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.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.visible_list*/);
    +  zmax_(Zmaxvertex, qh num_vertices);
    +  qh NEWfacets= False;
    +  if (qh IStracing >= 4) {
    +    if (qh num_facets < 2000)
    +      qh_printlists();
    +    qh_printfacetlist(qh newfacet_list, NULL, True);
    +    qh_checkpolygon(qh facet_list);
    +  }else if (qh CHECKfrequently) {
    +    if (qh num_facets < 50)
    +      qh_checkpolygon(qh facet_list);
    +    else
    +      qh_checkpolygon(qh newfacet_list);
    +  }
    +  if (qh STOPpoint > 0 && qh furthest_id == qh STOPpoint-1)
    +    return False;
    +  qh_resetlists(True, qh_RESETvisible /*qh.visible_list newvertex_list newfacet_list */);
    +  /* qh_triangulate(); to test qh.TRInormals */
    +  trace2((qh ferr, 2056, "qh_addpoint: added p%d new facets %d new balance %2.2g point balance %2.2g\n",
    +    qh_pointid(furthest), numnew, newbalance, pbalance));
    +  return True;
    +} /* addpoint */
    +
    +/*---------------------------------
    +
    +  qh_build_withrestart()
    +    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()
    +*/
    +void qh_build_withrestart(void) {
    +  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 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_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(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("_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;
    +      }
    +      qhmem.IStracing= qh IStracing;
    +    }
    +    if (qh JOGGLEmax < REALmax/2)
    +      qh_joggleinput();
    +    qh_initbuild();
    +    qh_buildhull();
    +    if (qh JOGGLEmax < REALmax/2 && !qh MERGING)
    +      qh_checkconvex(qh facet_list, qh_ALGORITHMfault);
    +  }
    +  qh ALLOWrestart= False;
    +} /* qh_build_withrestart */
    +
    +/*---------------------------------
    +
    +  qh_buildhull()
    +    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(void) {
    +  facetT *facet;
    +  pointT *furthest;
    +  vertexT *vertex;
    +  int id;
    +
    +  trace1((qh ferr, 1037, "qh_buildhull: start build hull\n"));
    +  FORALLfacets {
    +    if (facet->visible || facet->newfacet) {
    +      qh_fprintf(qh ferr, 6165, "qhull internal error (qh_buildhull): visible or new facet f%d in facet list\n",
    +                   facet->id);
    +      qh_errexit(qh_ERRqhull, facet, NULL);
    +    }
    +  }
    +  FORALLvertices {
    +    if (vertex->newlist) {
    +      qh_fprintf(qh ferr, 6166, "qhull internal error (qh_buildhull): new vertex f%d in vertex list\n",
    +                   vertex->id);
    +      qh_errprint("ERRONEOUS", NULL, NULL, NULL, vertex);
    +      qh_errexit(qh_ERRqhull, NULL, NULL);
    +    }
    +    id= qh_pointid(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 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(&facet))) {
    +    qh num_outside--;  /* if ONLYmax, furthest may not be outside */
    +    if (!qh_addpoint(furthest, facet, qh ONLYmax))
    +      break;
    +  }
    +  if (qh NARROWhull) /* move points from outsideset to coplanarset */
    +    qh_outcoplanar( /* facet_list */ );
    +  if (qh num_outside && !furthest) {
    +    qh_fprintf(qh ferr, 6167, "qhull internal error (qh_buildhull): %d outside points were never processed.\n", qh num_outside);
    +    qh_errexit(qh_ERRqhull, NULL, NULL);
    +  }
    +  trace1((qh ferr, 1039, "qh_buildhull: completed the hull construction\n"));
    +} /* buildhull */
    +
    +
    +/*---------------------------------
    +
    +  qh_buildtracing( 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(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 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(furthest);
    +  if (qh TRACEpoint == furthestid) {
    +    qh IStracing= qh TRACElevel;
    +    qhmem.IStracing= qh TRACElevel;
    +  }else if (qh TRACEpoint != qh_IDunknown && qh TRACEdist < REALmax/2) {
    +    qh IStracing= 0;
    +    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(furthest, facet, &dist);
    +    qh_fprintf(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(furthest, facet, &dist);
    +    qh_fprintf(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 */
    +
    +/*---------------------------------
    +
    +  qh_errexit2( 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(int exitcode, facetT *facet, facetT *otherfacet) {
    +
    +  qh_errprint("ERRONEOUS", facet, otherfacet, NULL, NULL);
    +  qh_errexit(exitcode, NULL, NULL);
    +} /* errexit2 */
    +
    +
    +/*---------------------------------
    +
    +  qh_findhorizon( 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(pointT *point, facetT *facet, int *goodvisible, int *goodhorizon) {
    +  facetT *neighbor, **neighborp, *visible;
    +  int numhorizon= 0, coplanar= 0;
    +  realT dist;
    +
    +  trace1((qh ferr, 1040,"qh_findhorizon: find horizon for point p%d facet f%d\n",qh_pointid(point),facet->id));
    +  *goodvisible= *goodhorizon= 0;
    +  zinc_(Ztotvisible);
    +  qh_removefacet(facet);  /* visible_list at end of qh facet_list */
    +  qh_appendfacet(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("visible", facet, NULL, NULL, NULL);
    +  qh visit_id++;
    +  FORALLvisible_facets {
    +    if (visible->tricoplanar && !qh TRInormals) {
    +      qh_fprintf(qh ferr, 6230, "Qhull internal error (qh_findhorizon): does not work for tricoplanar facets.  Use option 'Q11'\n");
    +      qh_errexit(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(point, neighbor, &dist);
    +      if (dist > qh MINvisible) {
    +        zinc_(Ztotvisible);
    +        qh_removefacet(neighbor);  /* append to end of qh visible_list */
    +        qh_appendfacet(neighbor);
    +        neighbor->visible= True;
    +        neighbor->f.replace= NULL;
    +        qh num_visible++;
    +        if (neighbor->good)
    +          (*goodvisible)++;
    +        if (qh IStracing >=4)
    +          qh_errprint("visible", neighbor, NULL, NULL, NULL);
    +      }else {
    +        if (dist > - qh MAXcoplanar) {
    +          neighbor->coplanar= True;
    +          zzinc_(Zcoplanarhorizon);
    +          qh_precision("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 ferr, 2057, "qh_findhorizon: point p%d is coplanar to horizon f%d, dist=%2.7g < qh MINvisible(%2.7g)\n",
    +              qh_pointid(point), neighbor->id, dist, qh MINvisible));
    +        }else
    +          neighbor->coplanar= False;
    +        zinc_(Ztothorizon);
    +        numhorizon++;
    +        if (neighbor->good)
    +          (*goodhorizon)++;
    +        if (qh IStracing >=4)
    +          qh_errprint("horizon", neighbor, NULL, NULL, NULL);
    +      }
    +    }
    +  }
    +  if (!numhorizon) {
    +    qh_precision("empty horizon");
    +    qh_fprintf(qh ferr, 6168, "qhull precision error (qh_findhorizon): empty horizon\n\
    +QhullPoint p%d was above all facets.\n", qh_pointid(point));
    +    qh_printfacetlist(qh facet_list, NULL, True);
    +    qh_errexit(qh_ERRprec, NULL, NULL);
    +  }
    +  trace1((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();
    +} /* findhorizon */
    +
    +/*---------------------------------
    +
    +  qh_nextfurthest( 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(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(&facet->outsideset);
    +      qh facet_next= facet->next;
    +      continue;
    +    }
    +    if (qh NARROWhull) {
    +      if (facet->notfurthest)
    +        qh_furthestout(facet);
    +      furthest= (pointT*)qh_setlast(facet->outsideset);
    +#if qh_COMPUTEfurthest
    +      qh_distplane(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.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( 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(&facet->outsideset);
    +          else if (size > idx) {
    +            *visible= facet;
    +            return((pointT*)qh_setdelnth(facet->outsideset, idx));
    +          }else
    +            idx -= size;
    +        }
    +      }
    +      qh_fprintf(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_ERRqhull, NULL, NULL);
    +    }else { /* VIRTUALmemory */
    +      facet= qh facet_tail->previous;
    +      if (!(furthest= (pointT*)qh_setdellast(facet->outsideset))) {
    +        if (facet->outsideset)
    +          qh_setfree(&facet->outsideset);
    +        qh_removefacet(facet);
    +        qh_prependfacet(facet, &qh facet_list);
    +        continue;
    +      }
    +      *visible= facet;
    +      return furthest;
    +    }
    +  }
    +  return NULL;
    +} /* nextfurthest */
    +
    +/*---------------------------------
    +
    +  qh_partitionall( 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(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 ferr, 1042, "qh_partitionall: partition all points into outside sets\n"));
    +  pointset= qh_settemp(numpoints);
    +  qh num_outside= 0;
    +  pointp= SETaddr_(pointset, pointT);
    +  for (i=numpoints, point= points; i--; point += qh hull_dim)
    +    *(pointp++)= point;
    +  qh_settruncate(pointset, numpoints);
    +  FOREACHvertex_(vertices) {
    +    if ((id= qh_pointid(vertex->point)) >= 0)
    +      SETelem_(pointset, id)= NULL;
    +  }
    +  id= qh_pointid(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 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(size);
    +      bestpoint= NULL;
    +      point_end= 0;
    +      FOREACHpoint_i_(pointset) {
    +        if (point) {
    +          zzinc_(Zpartitionall);
    +          qh_distplane(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(&facet->outsideset, bestpoint);
    +              bestpoint= point;
    +              bestdist= dist;
    +            }else
    +              qh_setappend(&facet->outsideset, point);
    +          }
    +        }
    +      }
    +      if (bestpoint) {
    +        qh_setappend(&facet->outsideset, bestpoint);
    +#if !qh_COMPUTEfurthest
    +        facet->furthestdist= bestdist;
    +#endif
    +      }else
    +        qh_setfree(&facet->outsideset);
    +      qh_settruncate(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_(pointset) {
    +      if (point)
    +        qh_partitionpoint(point, qh facet_list);
    +    }
    +    qh findbestnew= False;
    +  }
    +  zzadd_(Zpartitionall, zzval_(Zpartition));
    +  zzval_(Zpartition)= 0;
    +  qh_settempfree(&pointset);
    +  if (qh IStracing >= 4)
    +    qh_printfacetlist(qh facet_list, NULL, True);
    +} /* partitionall */
    +
    +
    +/*---------------------------------
    +
    +  qh_partitioncoplanar( 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(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(point, facet, &bestdist, qh_ALL, &isoutside, &numpart);
    +    else
    +      bestfacet= qh_findbest(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 ferr, 4062, "qh_partitioncoplanar: point p%d is more than near-inside facet f%d dist %2.2g findbestnew %d\n",
    +                  qh_pointid(point), bestfacet->id, bestdist, qh findbestnew));
    +          return;
    +        }
    +      }else if (bestdist < -qh MAXcoplanar) {
    +          trace4((qh ferr, 4063, "qh_partitioncoplanar: point p%d is inside facet f%d dist %2.2g findbestnew %d\n",
    +                  qh_pointid(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(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 ferr, 2058, "qh_partitioncoplanar: repartition point p%d from f%d.  It is above flipped facet f%d dist %2.2g\n",
    +                qh_pointid(point), facet->id, bestfacet->id, bestdist));
    +        oldfindbest= qh findbestnew;
    +        qh findbestnew= False;
    +        qh_partitionpoint(point, bestfacet);
    +        qh findbestnew= oldfindbest;
    +        return;
    +      }
    +    }
    +    qh max_outside= bestdist;
    +    if (bestdist > qh TRACEdist) {
    +      qh_fprintf(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(point), facet->id, bestdist, bestfacet->id, qh furthest_id);
    +      qh_errprint("DISTANT", facet, bestfacet, NULL, NULL);
    +    }
    +  }
    +  if (qh KEEPcoplanar + qh KEEPinside + qh KEEPnearinside) {
    +    oldfurthest= (pointT*)qh_setlast(bestfacet->coplanarset);
    +    if (oldfurthest) {
    +      zinc_(Zcomputefurthest);
    +      qh_distplane(oldfurthest, bestfacet, &dist2);
    +    }
    +    if (!oldfurthest || dist2 < bestdist)
    +      qh_setappend(&bestfacet->coplanarset, point);
    +    else
    +      qh_setappend2ndlast(&bestfacet->coplanarset, point);
    +  }
    +  trace4((qh ferr, 4064, "qh_partitioncoplanar: point p%d is coplanar with facet f%d(or inside) dist %2.2g\n",
    +          qh_pointid(point), bestfacet->id, bestdist));
    +} /* partitioncoplanar */
    +
    +/*---------------------------------
    +
    +  qh_partitionpoint( 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(pointT *point, facetT *facet) {
    +  realT bestdist;
    +  boolT isoutside;
    +  facetT *bestfacet;
    +  int numpart;
    +#if qh_COMPUTEfurthest
    +  realT dist;
    +#endif
    +
    +  if (qh findbestnew)
    +    bestfacet= qh_findbestnew(point, facet, &bestdist, qh BESToutside, &isoutside, &numpart);
    +  else
    +    bestfacet= qh_findbest(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("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(&(bestfacet->outsideset), point);
    +      if (!bestfacet->newfacet) {
    +        qh_removefacet(bestfacet);  /* make sure it's after qh facet_next */
    +        qh_appendfacet(bestfacet);
    +      }
    +#if !qh_COMPUTEfurthest
    +      bestfacet->furthestdist= bestdist;
    +#endif
    +    }else {
    +#if qh_COMPUTEfurthest
    +      zinc_(Zcomputefurthest);
    +      qh_distplane(oldfurthest, bestfacet, &dist);
    +      if (dist < bestdist)
    +        qh_setappend(&(bestfacet->outsideset), point);
    +      else
    +        qh_setappend2ndlast(&(bestfacet->outsideset), point);
    +#else
    +      if (bestfacet->furthestdist < bestdist) {
    +        qh_setappend(&(bestfacet->outsideset), point);
    +        bestfacet->furthestdist= bestdist;
    +      }else
    +        qh_setappend2ndlast(&(bestfacet->outsideset), point);
    +#endif
    +    }
    +    qh num_outside++;
    +    trace4((qh ferr, 4065, "qh_partitionpoint: point p%d is outside facet f%d new? %d (or narrowhull)\n",
    +          qh_pointid(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("nearly incident point");
    +    if ((qh KEEPcoplanar + qh KEEPnearinside) || bestdist > qh max_outside)
    +      qh_partitioncoplanar(point, bestfacet, &bestdist);
    +    else {
    +      trace4((qh ferr, 4066, "qh_partitionpoint: point p%d is coplanar to facet f%d (dropped)\n",
    +          qh_pointid(point), bestfacet->id));
    +    }
    +  }else if (qh KEEPnearinside && bestdist > -qh NEARinside) {
    +    zinc_(Zpartnear);
    +    qh_partitioncoplanar(point, bestfacet, &bestdist);
    +  }else {
    +    zinc_(Zpartinside);
    +    trace4((qh ferr, 4067, "qh_partitionpoint: point p%d is inside all facets, closest to f%d dist %2.2g\n",
    +          qh_pointid(point), bestfacet->id, bestdist));
    +    if (qh KEEPinside)
    +      qh_partitioncoplanar(point, bestfacet, &bestdist);
    +  }
    +} /* partitionpoint */
    +
    +/*---------------------------------
    +
    +  qh_partitionvisible( 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(/*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(visible);
    +    }
    +    if (!newfacet)
    +      newfacet= qh newfacet_list;
    +    if (newfacet == qh facet_tail) {
    +      qh_fprintf(qh ferr, 6170, "qhull precision error (qh_partitionvisible): all new facets deleted as\n        degenerate facets. Can not continue.\n");
    +      qh_errexit(qh_ERRprec, NULL, NULL);
    +    }
    +    if (visible->outsideset) {
    +      size= qh_setsize(visible->outsideset);
    +      *numoutside += size;
    +      qh num_outside -= size;
    +      FOREACHpoint_(visible->outsideset)
    +        qh_partitionpoint(point, newfacet);
    +    }
    +    if (visible->coplanarset && (qh KEEPcoplanar + qh KEEPinside + qh KEEPnearinside)) {
    +      size= qh_setsize(visible->coplanarset);
    +      coplanar += size;
    +      FOREACHpoint_(visible->coplanarset) {
    +        if (allpoints) /* not used */
    +          qh_partitionpoint(point, newfacet);
    +        else
    +          qh_partitioncoplanar(point, newfacet, NULL);
    +      }
    +    }
    +  }
    +  FOREACHvertex_(qh del_vertices) {
    +    if (vertex->point) {
    +      if (allpoints) /* not used */
    +        qh_partitionpoint(vertex->point, qh newfacet_list);
    +      else
    +        qh_partitioncoplanar(vertex->point, qh newfacet_list, NULL);
    +    }
    +  }
    +  trace1((qh ferr, 1043,"qh_partitionvisible: partitioned %d points from outsidesets and %d points from coplanarsets\n", *numoutside, coplanar));
    +} /* partitionvisible */
    +
    +
    +
    +/*---------------------------------
    +
    +  qh_precision( reason )
    +    restart on precision errors if not merging and if 'QJn'
    +*/
    +void qh_precision(const char *reason) {
    +
    +  if (qh ALLOWrestart && !qh PREmerge && !qh MERGEexact) {
    +    if (qh JOGGLEmax < REALmax/2) {
    +      trace0((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 */
    +
    +/*---------------------------------
    +
    +  qh_printsummary( fp )
    +    prints summary to fp
    +
    +  notes:
    +    not in io.c so that user_eg.c can prevent io.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(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 other_points);
    +  numvertices= qh num_vertices - qh_setsize(qh del_vertices);
    +  id= qh_pointid(qh GOODpointp);
    +  FORALLfacets {
    +    if (facet->coplanarset)
    +      numcoplanars += qh_setsize( facet->coplanarset);
    +    if (facet->good) {
    +      if (facet->simplicial) {
    +        if (facet->keepcentrum && facet->tricoplanar)
    +          numtricoplanars++;
    +      }else if (qh_setsize(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(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(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(fp, 9290, "\n\
    +Voronoi diagram by the convex hull of %d points in %d-d:\n\n", size, qh hull_dim);
    +    qh_fprintf(fp, 9291, "  Number of Voronoi regions%s: %d\n",
    +              qh ATinfinity ? " and at-infinity" : "", numvertices);
    +    if (numdel)
    +      qh_fprintf(fp, 9292, "  Total number of deleted points due to merging: %d\n", numdel);
    +    if (numcoplanars - numdel > 0)
    +      qh_fprintf(fp, 9293, "  Number of nearly incident points: %d\n", numcoplanars - numdel);
    +    else if (size - numvertices - numdel > 0)
    +      qh_fprintf(fp, 9294, "  Total number of nearly incident points: %d\n", size - numvertices - numdel);
    +    qh_fprintf(fp, 9295, "  Number of%s Voronoi vertices: %d\n",
    +              goodused ? " 'good'" : "", qh num_good);
    +    if (nonsimplicial)
    +      qh_fprintf(fp, 9296, "  Number of%s non-simplicial Voronoi vertices: %d\n",
    +              goodused ? " 'good'" : "", nonsimplicial);
    +  }else if (qh DELAUNAY) {
    +    if (qh UPPERdelaunay)
    +      qh_fprintf(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(fp, 9298, "\n\
    +Delaunay triangulation by the convex hull of %d points in %d-d:\n\n", size, qh hull_dim);
    +    qh_fprintf(fp, 9299, "  Number of input sites%s: %d\n",
    +              qh ATinfinity ? " and at-infinity" : "", numvertices);
    +    if (numdel)
    +      qh_fprintf(fp, 9300, "  Total number of deleted points due to merging: %d\n", numdel);
    +    if (numcoplanars - numdel > 0)
    +      qh_fprintf(fp, 9301, "  Number of nearly incident points: %d\n", numcoplanars - numdel);
    +    else if (size - numvertices - numdel > 0)
    +      qh_fprintf(fp, 9302, "  Total number of nearly incident points: %d\n", size - numvertices - numdel);
    +    qh_fprintf(fp, 9303, "  Number of%s Delaunay regions: %d\n",
    +              goodused ? " 'good'" : "", qh num_good);
    +    if (nonsimplicial)
    +      qh_fprintf(fp, 9304, "  Number of%s non-simplicial Delaunay regions: %d\n",
    +              goodused ? " 'good'" : "", nonsimplicial);
    +  }else if (qh HALFspace) {
    +    qh_fprintf(fp, 9305, "\n\
    +Halfspace intersection by the convex hull of %d points in %d-d:\n\n", size, qh hull_dim);
    +    qh_fprintf(fp, 9306, "  Number of halfspaces: %d\n", size);
    +    qh_fprintf(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(fp, 9308, "  Number of %s halfspaces: %d\n", s, numcoplanars);
    +    }
    +    qh_fprintf(fp, 9309, "  Number of intersection points: %d\n", qh num_facets - qh num_visible);
    +    if (goodused)
    +      qh_fprintf(fp, 9310, "  Number of 'good' intersection points: %d\n", qh num_good);
    +    if (nonsimplicial)
    +      qh_fprintf(fp, 9311, "  Number of%s non-simplicial intersection points: %d\n",
    +              goodused ? " 'good'" : "", nonsimplicial);
    +  }else {
    +    qh_fprintf(fp, 9312, "\n\
    +Convex hull of %d points in %d-d:\n\n", size, qh hull_dim);
    +    qh_fprintf(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(fp, 9314, "  Number of %s points: %d\n", s, numcoplanars);
    +    }
    +    qh_fprintf(fp, 9315, "  Number of facets: %d\n", qh num_facets - qh num_visible);
    +    if (goodused)
    +      qh_fprintf(fp, 9316, "  Number of 'good' facets: %d\n", qh num_good);
    +    if (nonsimplicial)
    +      qh_fprintf(fp, 9317, "  Number of%s non-simplicial facets: %d\n",
    +              goodused ? " 'good'" : "", nonsimplicial);
    +  }
    +  if (numtricoplanars)
    +      qh_fprintf(fp, 9318, "  Number of triangulated facets: %d\n", numtricoplanars);
    +  qh_fprintf(fp, 9319, "\nStatistics for: %s | %s",
    +                      qh rbox_command, qh qhull_command);
    +  if (qh ROTATErandom != INT_MIN)
    +    qh_fprintf(fp, 9320, " QR%d\n\n", qh ROTATErandom);
    +  else
    +    qh_fprintf(fp, 9321, "\n\n");
    +  qh_fprintf(fp, 9322, "  Number of points processed: %d\n", zzval_(Zprocessed));
    +  qh_fprintf(fp, 9323, "  Number of hyperplanes created: %d\n", zzval_(Zsetplane));
    +  if (qh DELAUNAY)
    +    qh_fprintf(fp, 9324, "  Number of facets in hull: %d\n", qh num_facets - qh num_visible);
    +  qh_fprintf(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(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(fp, 9327, "  new facet standard deviation: %2.2g\n", stddev);
    +  qh_fprintf(fp, 9328, "  average partition balance: %2.2g\n",
    +          wval_(Wpbalance)/zval_(Zpbalance));
    +  stddev= qh_stddev(zval_(Zpbalance), wval_(Wpbalance),
    +                                 wval_(Wpbalance2), &ave);
    +  qh_fprintf(fp, 9329, "  partition standard deviation: %2.2g\n", stddev);
    +  }
    +#endif
    +  if (nummerged) {
    +    qh_fprintf(fp, 9330,"  Number of distance tests for merging: %d\n",zzval_(Zbestdist)+
    +          zzval_(Zcentrumtests)+zzval_(Zdistconvex)+zzval_(Zdistcheck)+
    +          zzval_(Zdistzero));
    +    qh_fprintf(fp, 9331,"  Number of distance tests for checking: %d\n",zzval_(Zcheckpart));
    +    qh_fprintf(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(fp, 9333, "  CPU seconds to compute hull (after input): %2.4g\n", cpu);
    +  }
    +  if (qh RERUN) {
    +    if (!qh PREmerge && !qh MERGEexact)
    +      qh_fprintf(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(fp, 9335, "  After %d retries, input joggled by: %2.2g\n",
    +         zzval_(Zretry), qh JOGGLEmax);
    +    else
    +      qh_fprintf(fp, 9336, "  Input joggled by: %2.2g\n", qh JOGGLEmax);
    +  }
    +  if (qh totarea != 0.0)
    +    qh_fprintf(fp, 9337, "  %s facet area:   %2.8g\n",
    +            zzval_(Ztotmerge) ? "Approximate" : "Total", qh totarea);
    +  if (qh totvol != 0.0)
    +    qh_fprintf(fp, 9338, "  %s volume:       %2.8g\n",
    +            zzval_(Ztotmerge) ? "Approximate" : "Total", qh totvol);
    +  if (qh MERGING) {
    +    qh_outerinner(NULL, &outerplane, &innerplane);
    +    if (outerplane > 2 * qh DISTround) {
    +      qh_fprintf(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(fp, 9340, " (%.1fx)\n", ratio);
    +      else
    +        qh_fprintf(fp, 9341, "\n");
    +    }
    +    if (innerplane < -2 * qh DISTround) {
    +      qh_fprintf(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(fp, 9343, " (%.1fx)\n", ratio);
    +      else
    +        qh_fprintf(fp, 9344, "\n");
    +    }
    +  }
    +  qh_fprintf(fp, 9345, "\n");
    +} /* printsummary */
    +
    +
    diff --git a/xs/src/qhull/src/libqhull/libqhull.h b/xs/src/qhull/src/libqhull/libqhull.h
    new file mode 100644
    index 000000000..677085808
    --- /dev/null
    +++ b/xs/src/qhull/src/libqhull/libqhull.h
    @@ -0,0 +1,1140 @@
    +/*
      ---------------------------------
    +
    +   libqhull.h
    +   user-level header file for using qhull.a library
    +
    +   see qh-qhull.htm, qhull_a.h
    +
    +   Copyright (c) 1993-2015 The Geometry Center.
    +   $Id: //main/2015/qhull/src/libqhull/libqhull.h#7 $$Change: 2066 $
    +   $DateTime: 2016/01/18 19:29:17 $$Author: bbarber $
    +
    +   NOTE: access to qh_qh is via the 'qh' macro.  This allows
    +   qh_qh to be either a pointer or a structure.  An example
    +   of using qh is "qh.DROPdim" which accesses the DROPdim
    +   field of qh_qh.  Similarly, access to qh_qhstat is via
    +   the 'qhstat' macro.
    +
    +   includes function prototypes for libqhull.c, geom.c, global.c, io.c, user.c
    +
    +   use mem.h for mem.c
    +   use qset.h for qset.c
    +
    +   see unix.c for an example of using libqhull.h
    +
    +   recompile qhull if you change this file
    +*/
    +
    +#ifndef qhDEFlibqhull
    +#define qhDEFlibqhull 1
    +
    +/*=========================== -included files ==============*/
    +
    +/* user_r.h first for QHULL_CRTDBG */
    +#include "user.h"      /* user definable constants (e.g., qh_QHpointer) */
    +
    +#include "mem.h"   /* Needed qhT in libqhull_r.h.  Here for compatibility */
    +#include "qset.h"   /* Needed for QHULL_LIB_CHECK */
    +/* include stat_r.h after defining boolT.  Needed for qhT in libqhull_r.h.  Here for compatibility and statT */
    +
    +#include 
    +#include 
    +#include 
    +#include 
    +
    +#if __MWERKS__ && __POWERPC__
    +#include  
    +#include  
    +#include        
    +#endif
    +
    +#ifndef __STDC__
    +#ifndef __cplusplus
    +#if     !_MSC_VER
    +#error  Neither __STDC__ nor __cplusplus is defined.  Please use strict ANSI C or C++ to compile
    +#error  Qhull.  You may need to turn off compiler extensions in your project configuration.  If
    +#error  your compiler is a standard C compiler, you can delete this warning from libqhull.h
    +#endif
    +#endif
    +#endif
    +
    +/*============ constants and basic types ====================*/
    +
    +extern const char qh_version[]; /* defined in global.c */
    +extern const char qh_version2[]; /* defined in global.c */
    +
    +/*----------------------------------
    +
    +  coordT
    +    coordinates and coefficients are stored as realT (i.e., double)
    +
    +  notes:
    +    Qhull works well if realT is 'float'.  If so joggle (QJ) is not effective.
    +
    +    Could use 'float' for data and 'double' for calculations (realT vs. coordT)
    +      This requires many type casts, and adjusted error bounds.
    +      Also C compilers may do expressions in double anyway.
    +*/
    +#define coordT realT
    +
    +/*----------------------------------
    +
    +  pointT
    +    a point is an array of coordinates, usually qh.hull_dim
    +    qh_pointid returns
    +      qh_IDnone if point==0 or qh is undefined
    +      qh_IDinterior for qh.interior_point
    +      qh_IDunknown if point is neither in qh.first_point... nor qh.other_points
    +
    +  notes:
    +    qh.STOPcone and qh.STOPpoint assume that qh_IDunknown==-1 (other negative numbers indicate points)
    +    qh_IDunknown is also returned by getid_() for unknown facet, ridge, or vertex
    +*/
    +#define pointT coordT
    +typedef enum
    +{
    +    qh_IDnone = -3, qh_IDinterior = -2, qh_IDunknown = -1
    +}
    +qh_pointT;
    +
    +/*----------------------------------
    +
    +  flagT
    +    Boolean flag as a bit
    +*/
    +#define flagT unsigned int
    +
    +/*----------------------------------
    +
    +  boolT
    +    boolean value, either True or False
    +
    +  notes:
    +    needed for portability
    +    Use qh_False/qh_True as synonyms
    +*/
    +#define boolT unsigned int
    +#ifdef False
    +#undef False
    +#endif
    +#ifdef True
    +#undef True
    +#endif
    +#define False 0
    +#define True 1
    +#define qh_False 0
    +#define qh_True 1
    +
    +#include "stat.h"  /* after define of boolT */
    +
    +/*----------------------------------
    +
    +  qh_CENTER
    +    to distinguish facet->center
    +*/
    +typedef enum
    +{
    +    qh_ASnone = 0,   /* If not MERGING and not VORONOI */
    +    qh_ASvoronoi,    /* Set by qh_clearcenters on qh_prepare_output, or if not MERGING and VORONOI */
    +    qh_AScentrum     /* If MERGING (assumed during merging) */
    +}
    +qh_CENTER;
    +
    +/*----------------------------------
    +
    +  qh_PRINT
    +    output formats for printing (qh.PRINTout).
    +    'Fa' 'FV' 'Fc' 'FC'
    +
    +
    +   notes:
    +   some of these names are similar to qhT names.  The similar names are only
    +   used in switch statements in qh_printbegin() etc.
    +*/
    +typedef enum {qh_PRINTnone= 0,
    +  qh_PRINTarea, qh_PRINTaverage,           /* 'Fa' 'FV' 'Fc' 'FC' */
    +  qh_PRINTcoplanars, qh_PRINTcentrums,
    +  qh_PRINTfacets, qh_PRINTfacets_xridge,   /* 'f' 'FF' 'G' 'FI' 'Fi' 'Fn' */
    +  qh_PRINTgeom, qh_PRINTids, qh_PRINTinner, qh_PRINTneighbors,
    +  qh_PRINTnormals, qh_PRINTouter, qh_PRINTmaple, /* 'n' 'Fo' 'i' 'm' 'Fm' 'FM', 'o' */
    +  qh_PRINTincidences, qh_PRINTmathematica, qh_PRINTmerges, qh_PRINToff,
    +  qh_PRINToptions, qh_PRINTpointintersect, /* 'FO' 'Fp' 'FP' 'p' 'FQ' 'FS' */
    +  qh_PRINTpointnearest, qh_PRINTpoints, qh_PRINTqhull, qh_PRINTsize,
    +  qh_PRINTsummary, qh_PRINTtriangles,      /* 'Fs' 'Ft' 'Fv' 'FN' 'Fx' */
    +  qh_PRINTvertices, qh_PRINTvneighbors, qh_PRINTextremes,
    +  qh_PRINTEND} qh_PRINT;
    +
    +/*----------------------------------
    +
    +  qh_ALL
    +    argument flag for selecting everything
    +*/
    +#define qh_ALL      True
    +#define qh_NOupper  True     /* argument for qh_findbest */
    +#define qh_IScheckmax  True     /* argument for qh_findbesthorizon */
    +#define qh_ISnewfacets  True     /* argument for qh_findbest */
    +#define qh_RESETvisible  True     /* argument for qh_resetlists */
    +
    +/*----------------------------------
    +
    +  qh_ERR
    +    Qhull exit codes, for indicating errors
    +    See: MSG_ERROR and MSG_WARNING [user.h]
    +*/
    +#define qh_ERRnone  0    /* no error occurred during qhull */
    +#define qh_ERRinput 1    /* input inconsistency */
    +#define qh_ERRsingular 2 /* singular input data */
    +#define qh_ERRprec  3    /* precision error */
    +#define qh_ERRmem   4    /* insufficient memory, matches mem.h */
    +#define qh_ERRqhull 5    /* internal error detected, matches mem.h */
    +
    +/*----------------------------------
    +
    +qh_FILEstderr
    +Fake stderr to distinguish error output from normal output
    +For C++ interface.  Must redefine qh_fprintf_qhull
    +*/
    +#define qh_FILEstderr ((FILE*)1)
    +
    +/* ============ -structures- ====================
    +   each of the following structures is defined by a typedef
    +   all realT and coordT fields occur at the beginning of a structure
    +        (otherwise space may be wasted due to alignment)
    +   define all flags together and pack into 32-bit number
    +   DEFsetT is likewise defined in
    +   mem.h and qset.h
    +*/
    +
    +typedef struct vertexT vertexT;
    +typedef struct ridgeT ridgeT;
    +typedef struct facetT facetT;
    +#ifndef DEFsetT
    +#define DEFsetT 1
    +typedef struct setT setT;          /* defined in qset.h */
    +#endif
    +
    +/*----------------------------------
    +
    +  facetT
    +    defines a facet
    +
    +  notes:
    +   qhull() generates the hull as a list of facets.
    +
    +  topological information:
    +    f.previous,next     doubly-linked list of facets
    +    f.vertices          set of vertices
    +    f.ridges            set of ridges
    +    f.neighbors         set of neighbors
    +    f.toporient         True if facet has top-orientation (else bottom)
    +
    +  geometric information:
    +    f.offset,normal     hyperplane equation
    +    f.maxoutside        offset to outer plane -- all points inside
    +    f.center            centrum for testing convexity
    +    f.simplicial        True if facet is simplicial
    +    f.flipped           True if facet does not include qh.interior_point
    +
    +  for constructing hull:
    +    f.visible           True if facet on list of visible facets (will be deleted)
    +    f.newfacet          True if facet on list of newly created facets
    +    f.coplanarset       set of points coplanar with this facet
    +                        (includes near-inside points for later testing)
    +    f.outsideset        set of points outside of this facet
    +    f.furthestdist      distance to furthest point of outside set
    +    f.visitid           marks visited facets during a loop
    +    f.replace           replacement facet for to-be-deleted, visible facets
    +    f.samecycle,newcycle cycle of facets for merging into horizon facet
    +
    +  see below for other flags and fields
    +*/
    +struct facetT {
    +#if !qh_COMPUTEfurthest
    +  coordT   furthestdist;/* distance to furthest point of outsideset */
    +#endif
    +#if qh_MAXoutside
    +  coordT   maxoutside;  /* max computed distance of point to facet
    +                        Before QHULLfinished this is an approximation
    +                        since maxdist not always set for mergefacet
    +                        Actual outer plane is +DISTround and
    +                        computed outer plane is +2*DISTround */
    +#endif
    +  coordT   offset;      /* exact offset of hyperplane from origin */
    +  coordT  *normal;      /* normal of hyperplane, hull_dim coefficients */
    +                        /*   if tricoplanar, shared with a neighbor */
    +  union {               /* in order of testing */
    +   realT   area;        /* area of facet, only in io.c if  ->isarea */
    +   facetT *replace;     /*  replacement facet if ->visible and NEWfacets
    +                             is NULL only if qh_mergedegen_redundant or interior */
    +   facetT *samecycle;   /*  cycle of facets from the same visible/horizon intersection,
    +                             if ->newfacet */
    +   facetT *newcycle;    /*  in horizon facet, current samecycle of new facets */
    +   facetT *trivisible;  /* visible facet for ->tricoplanar facets during qh_triangulate() */
    +   facetT *triowner;    /* owner facet for ->tricoplanar, !isarea facets w/ ->keepcentrum */
    +  }f;
    +  coordT  *center;      /* set according to qh.CENTERtype */
    +                        /*   qh_ASnone:    no center (not MERGING) */
    +                        /*   qh_AScentrum: centrum for testing convexity (qh_getcentrum) */
    +                        /*                 assumed qh_AScentrum while merging */
    +                        /*   qh_ASvoronoi: Voronoi center (qh_facetcenter) */
    +                        /* after constructing the hull, it may be changed (qh_clearcenter) */
    +                        /* if tricoplanar and !keepcentrum, shared with a neighbor */
    +  facetT  *previous;    /* previous facet in the facet_list */
    +  facetT  *next;        /* next facet in the facet_list */
    +  setT    *vertices;    /* vertices for this facet, inverse sorted by ID
    +                           if simplicial, 1st vertex was apex/furthest */
    +  setT    *ridges;      /* explicit ridges for nonsimplicial facets.
    +                           for simplicial facets, neighbors define the ridges */
    +  setT    *neighbors;   /* neighbors of the facet.  If simplicial, the kth
    +                           neighbor is opposite the kth vertex, and the first
    +                           neighbor is the horizon facet for the first vertex*/
    +  setT    *outsideset;  /* set of points outside this facet
    +                           if non-empty, last point is furthest
    +                           if NARROWhull, includes coplanars for partitioning*/
    +  setT    *coplanarset; /* set of points coplanar with this facet
    +                           > qh.min_vertex and <= facet->max_outside
    +                           a point is assigned to the furthest facet
    +                           if non-empty, last point is furthest away */
    +  unsigned visitid;     /* visit_id, for visiting all neighbors,
    +                           all uses are independent */
    +  unsigned id;          /* unique identifier from qh.facet_id */
    +  unsigned nummerge:9;  /* number of merges */
    +#define qh_MAXnummerge 511 /*     2^9-1, 32 flags total, see "flags:" in io.c */
    +  flagT    tricoplanar:1; /* True if TRIangulate and simplicial and coplanar with a neighbor */
    +                          /*   all tricoplanars share the same apex */
    +                          /*   all tricoplanars share the same ->center, ->normal, ->offset, ->maxoutside */
    +                          /*     ->keepcentrum is true for the owner.  It has the ->coplanareset */
    +                          /*   if ->degenerate, does not span facet (one logical ridge) */
    +                          /*   during qh_triangulate, f.trivisible points to original facet */
    +  flagT    newfacet:1;  /* True if facet on qh.newfacet_list (new or merged) */
    +  flagT    visible:1;   /* True if visible facet (will be deleted) */
    +  flagT    toporient:1; /* True if created with top orientation
    +                           after merging, use ridge orientation */
    +  flagT    simplicial:1;/* True if simplicial facet, ->ridges may be implicit */
    +  flagT    seen:1;      /* used to perform operations only once, like visitid */
    +  flagT    seen2:1;     /* used to perform operations only once, like visitid */
    +  flagT    flipped:1;   /* True if facet is flipped */
    +  flagT    upperdelaunay:1; /* True if facet is upper envelope of Delaunay triangulation */
    +  flagT    notfurthest:1; /* True if last point of outsideset is not furthest*/
    +
    +/*-------- flags primarily for output ---------*/
    +  flagT    good:1;      /* True if a facet marked good for output */
    +  flagT    isarea:1;    /* True if facet->f.area is defined */
    +
    +/*-------- flags for merging ------------------*/
    +  flagT    dupridge:1;  /* True if duplicate ridge in facet */
    +  flagT    mergeridge:1; /* True if facet or neighbor contains a qh_MERGEridge
    +                            ->normal defined (also defined for mergeridge2) */
    +  flagT    mergeridge2:1; /* True if neighbor contains a qh_MERGEridge (mark_dupridges */
    +  flagT    coplanar:1;  /* True if horizon facet is coplanar at last use */
    +  flagT     mergehorizon:1; /* True if will merge into horizon (->coplanar) */
    +  flagT     cycledone:1;/* True if mergecycle_all already done */
    +  flagT    tested:1;    /* True if facet convexity has been tested (false after merge */
    +  flagT    keepcentrum:1; /* True if keep old centrum after a merge, or marks owner for ->tricoplanar */
    +  flagT    newmerge:1;  /* True if facet is newly merged for reducevertices */
    +  flagT    degenerate:1; /* True if facet is degenerate (degen_mergeset or ->tricoplanar) */
    +  flagT    redundant:1;  /* True if facet is redundant (degen_mergeset) */
    +};
    +
    +
    +/*----------------------------------
    +
    +  ridgeT
    +    defines a ridge
    +
    +  notes:
    +  a ridge is hull_dim-1 simplex between two neighboring facets.  If the
    +  facets are non-simplicial, there may be more than one ridge between
    +  two facets.  E.G. a 4-d hypercube has two triangles between each pair
    +  of neighboring facets.
    +
    +  topological information:
    +    vertices            a set of vertices
    +    top,bottom          neighboring facets with orientation
    +
    +  geometric information:
    +    tested              True if ridge is clearly convex
    +    nonconvex           True if ridge is non-convex
    +*/
    +struct ridgeT {
    +  setT    *vertices;    /* vertices belonging to this ridge, inverse sorted by ID
    +                           NULL if a degen ridge (matchsame) */
    +  facetT  *top;         /* top facet this ridge is part of */
    +  facetT  *bottom;      /* bottom facet this ridge is part of */
    +  unsigned id;          /* unique identifier.  Same size as vertex_id and ridge_id */
    +  flagT    seen:1;      /* used to perform operations only once */
    +  flagT    tested:1;    /* True when ridge is tested for convexity */
    +  flagT    nonconvex:1; /* True if getmergeset detected a non-convex neighbor
    +                           only one ridge between neighbors may have nonconvex */
    +};
    +
    +/*----------------------------------
    +
    +  vertexT
    +     defines a vertex
    +
    +  topological information:
    +    next,previous       doubly-linked list of all vertices
    +    neighbors           set of adjacent facets (only if qh.VERTEXneighbors)
    +
    +  geometric information:
    +    point               array of DIM3 coordinates
    +*/
    +struct vertexT {
    +  vertexT *next;        /* next vertex in vertex_list */
    +  vertexT *previous;    /* previous vertex in vertex_list */
    +  pointT  *point;       /* hull_dim coordinates (coordT) */
    +  setT    *neighbors;   /* neighboring facets of vertex, qh_vertexneighbors()
    +                           inits in io.c or after first merge */
    +  unsigned id;          /* unique identifier.  Same size as qh.vertex_id and qh.ridge_id */
    +  unsigned visitid;     /* for use with qh.vertex_visit, size must match */
    +  flagT    seen:1;      /* used to perform operations only once */
    +  flagT    seen2:1;     /* another seen flag */
    +  flagT    delridge:1;  /* vertex was part of a deleted ridge */
    +  flagT    deleted:1;   /* true if vertex on qh.del_vertices */
    +  flagT    newlist:1;   /* true if vertex on qh.newvertex_list */
    +};
    +
    +/*======= -global variables -qh ============================*/
    +
    +/*----------------------------------
    +
    +  qh
    +   all global variables for qhull are in qh, qhmem, and qhstat
    +
    +  notes:
    +   qhmem is defined in mem.h, qhstat is defined in stat.h, qhrbox is defined in rboxpoints.h
    +   Access to qh_qh is via the "qh" macro.  See qh_QHpointer in user.h
    +
    +   All global variables for qhull are in qh, qhmem, and qhstat
    +   qh must be unique for each instance of qhull
    +   qhstat may be shared between qhull instances.
    +   qhmem may be shared across multiple instances of Qhull.
    +   Rbox uses global variables rbox_inuse and rbox, but does not persist data across calls.
    +
    +   Qhull is not multi-threaded.  Global state could be stored in thread-local storage.
    +
    +   QHULL_LIB_CHECK checks that a program and the corresponding
    +   qhull library were built with the same type of header files.
    +*/
    +
    +typedef struct qhT qhT;
    +
    +#define QHULL_NON_REENTRANT 0
    +#define QHULL_QH_POINTER 1
    +#define QHULL_REENTRANT 2
    +
    +#if qh_QHpointer_dllimport
    +#define qh qh_qh->
    +__declspec(dllimport) extern qhT *qh_qh;     /* allocated in global.c */
    +#define QHULL_LIB_TYPE QHULL_QH_POINTER
    +
    +#elif qh_QHpointer
    +#define qh qh_qh->
    +extern qhT *qh_qh;     /* allocated in global.c */
    +#define QHULL_LIB_TYPE QHULL_QH_POINTER
    +
    +#elif qh_dllimport
    +#define qh qh_qh.
    +__declspec(dllimport) extern qhT qh_qh;      /* allocated in global.c */
    +#define QHULL_LIB_TYPE QHULL_NON_REENTRANT
    +
    +#else
    +#define qh qh_qh.
    +extern qhT qh_qh;
    +#define QHULL_LIB_TYPE QHULL_NON_REENTRANT
    +#endif
    +
    +#define QHULL_LIB_CHECK qh_lib_check(QHULL_LIB_TYPE, sizeof(qhT), sizeof(vertexT), sizeof(ridgeT), sizeof(facetT), sizeof(setT), sizeof(qhmemT));
    +#define QHULL_LIB_CHECK_RBOX qh_lib_check(QHULL_LIB_TYPE, sizeof(qhT), sizeof(vertexT), sizeof(ridgeT), sizeof(facetT), 0, 0);
    +
    +struct qhT {
    +
    +/*----------------------------------
    +
    +  qh constants
    +    configuration flags and constants for Qhull
    +
    +  notes:
    +    The user configures Qhull by defining flags.  They are
    +    copied into qh by qh_setflags().  qh-quick.htm#options defines the flags.
    +*/
    +  boolT ALLpoints;        /* true 'Qs' if search all points for initial simplex */
    +  boolT ANGLEmerge;       /* true 'Qa' if sort potential merges by angle */
    +  boolT APPROXhull;       /* true 'Wn' if MINoutside set */
    +  realT   MINoutside;     /*   'Wn' min. distance for an outside point */
    +  boolT ANNOTATEoutput;   /* true 'Ta' if annotate output with message codes */
    +  boolT ATinfinity;       /* true 'Qz' if point num_points-1 is "at-infinity"
    +                             for improving precision in Delaunay triangulations */
    +  boolT AVOIDold;         /* true 'Q4' if avoid old->new merges */
    +  boolT BESToutside;      /* true 'Qf' if partition points into best outsideset */
    +  boolT CDDinput;         /* true 'Pc' if input uses CDD format (1.0/offset first) */
    +  boolT CDDoutput;        /* true 'PC' if print normals in CDD format (offset first) */
    +  boolT CHECKfrequently;  /* true 'Tc' if checking frequently */
    +  realT premerge_cos;     /*   'A-n'   cos_max when pre merging */
    +  realT postmerge_cos;    /*   'An'    cos_max when post merging */
    +  boolT DELAUNAY;         /* true 'd' if computing DELAUNAY triangulation */
    +  boolT DOintersections;  /* true 'Gh' if print hyperplane intersections */
    +  int   DROPdim;          /* drops dim 'GDn' for 4-d -> 3-d output */
    +  boolT FORCEoutput;      /* true 'Po' if forcing output despite degeneracies */
    +  int   GOODpoint;        /* 1+n for 'QGn', good facet if visible/not(-) from point n*/
    +  pointT *GOODpointp;     /*   the actual point */
    +  boolT GOODthreshold;    /* true if qh.lower_threshold/upper_threshold defined
    +                             false if qh.SPLITthreshold */
    +  int   GOODvertex;       /* 1+n, good facet if vertex for point n */
    +  pointT *GOODvertexp;     /*   the actual point */
    +  boolT HALFspace;        /* true 'Hn,n,n' if halfspace intersection */
    +  boolT ISqhullQh;        /* Set by Qhull.cpp on initialization */
    +  int   IStracing;        /* trace execution, 0=none, 1=least, 4=most, -1=events */
    +  int   KEEParea;         /* 'PAn' number of largest facets to keep */
    +  boolT KEEPcoplanar;     /* true 'Qc' if keeping nearest facet for coplanar points */
    +  boolT KEEPinside;       /* true 'Qi' if keeping nearest facet for inside points
    +                              set automatically if 'd Qc' */
    +  int   KEEPmerge;        /* 'PMn' number of facets to keep with most merges */
    +  realT KEEPminArea;      /* 'PFn' minimum facet area to keep */
    +  realT MAXcoplanar;      /* 'Un' max distance below a facet to be coplanar*/
    +  boolT MERGEexact;       /* true 'Qx' if exact merges (coplanar, degen, dupridge, flipped) */
    +  boolT MERGEindependent; /* true 'Q2' if merging independent sets */
    +  boolT MERGING;          /* true if exact-, pre- or post-merging, with angle and centrum tests */
    +  realT   premerge_centrum;  /*   'C-n' centrum_radius when pre merging.  Default is round-off */
    +  realT   postmerge_centrum; /*   'Cn' centrum_radius when post merging.  Default is round-off */
    +  boolT MERGEvertices;    /* true 'Q3' if merging redundant vertices */
    +  realT MINvisible;       /* 'Vn' min. distance for a facet to be visible */
    +  boolT NOnarrow;         /* true 'Q10' if no special processing for narrow distributions */
    +  boolT NOnearinside;     /* true 'Q8' if ignore near-inside points when partitioning */
    +  boolT NOpremerge;       /* true 'Q0' if no defaults for C-0 or Qx */
    +  boolT NOwide;           /* true 'Q12' if no error on wide merge due to duplicate ridge */
    +  boolT ONLYgood;         /* true 'Qg' if process points with good visible or horizon facets */
    +  boolT ONLYmax;          /* true 'Qm' if only process points that increase max_outside */
    +  boolT PICKfurthest;     /* true 'Q9' if process furthest of furthest points*/
    +  boolT POSTmerge;        /* true if merging after buildhull (Cn or An) */
    +  boolT PREmerge;         /* true if merging during buildhull (C-n or A-n) */
    +                        /* NOTE: some of these names are similar to qh_PRINT names */
    +  boolT PRINTcentrums;    /* true 'Gc' if printing centrums */
    +  boolT PRINTcoplanar;    /* true 'Gp' if printing coplanar points */
    +  int   PRINTdim;         /* print dimension for Geomview output */
    +  boolT PRINTdots;        /* true 'Ga' if printing all points as dots */
    +  boolT PRINTgood;        /* true 'Pg' if printing good facets */
    +  boolT PRINTinner;       /* true 'Gi' if printing inner planes */
    +  boolT PRINTneighbors;   /* true 'PG' if printing neighbors of good facets */
    +  boolT PRINTnoplanes;    /* true 'Gn' if printing no planes */
    +  boolT PRINToptions1st;  /* true 'FO' if printing options to stderr */
    +  boolT PRINTouter;       /* true 'Go' if printing outer planes */
    +  boolT PRINTprecision;   /* false 'Pp' if not reporting precision problems */
    +  qh_PRINT PRINTout[qh_PRINTEND]; /* list of output formats to print */
    +  boolT PRINTridges;      /* true 'Gr' if print ridges */
    +  boolT PRINTspheres;     /* true 'Gv' if print vertices as spheres */
    +  boolT PRINTstatistics;  /* true 'Ts' if printing statistics to stderr */
    +  boolT PRINTsummary;     /* true 's' if printing summary to stderr */
    +  boolT PRINTtransparent; /* true 'Gt' if print transparent outer ridges */
    +  boolT PROJECTdelaunay;  /* true if DELAUNAY, no readpoints() and
    +                             need projectinput() for Delaunay in qh_init_B */
    +  int   PROJECTinput;     /* number of projected dimensions 'bn:0Bn:0' */
    +  boolT QUICKhelp;        /* true if quick help message for degen input */
    +  boolT RANDOMdist;       /* true if randomly change distplane and setfacetplane */
    +  realT RANDOMfactor;     /*    maximum random perturbation */
    +  realT RANDOMa;          /*    qh_randomfactor is randr * RANDOMa + RANDOMb */
    +  realT RANDOMb;
    +  boolT RANDOMoutside;    /* true if select a random outside point */
    +  int   REPORTfreq;       /* buildtracing reports every n facets */
    +  int   REPORTfreq2;      /* tracemerging reports every REPORTfreq/2 facets */
    +  int   RERUN;            /* 'TRn' rerun qhull n times (qh.build_cnt) */
    +  int   ROTATErandom;     /* 'QRn' seed, 0 time, >= rotate input */
    +  boolT SCALEinput;       /* true 'Qbk' if scaling input */
    +  boolT SCALElast;        /* true 'Qbb' if scale last coord to max prev coord */
    +  boolT SETroundoff;      /* true 'E' if qh.DISTround is predefined */
    +  boolT SKIPcheckmax;     /* true 'Q5' if skip qh_check_maxout */
    +  boolT SKIPconvex;       /* true 'Q6' if skip convexity testing during pre-merge */
    +  boolT SPLITthresholds;  /* true if upper_/lower_threshold defines a region
    +                               used only for printing (!for qh.ONLYgood) */
    +  int   STOPcone;         /* 'TCn' 1+n for stopping after cone for point n */
    +                          /*       also used by qh_build_withresart for err exit*/
    +  int   STOPpoint;        /* 'TVn' 'TV-n' 1+n for stopping after/before(-)
    +                                        adding point n */
    +  int   TESTpoints;       /* 'QTn' num of test points after qh.num_points.  Test points always coplanar. */
    +  boolT TESTvneighbors;   /*  true 'Qv' if test vertex neighbors at end */
    +  int   TRACElevel;       /* 'Tn' conditional IStracing level */
    +  int   TRACElastrun;     /*  qh.TRACElevel applies to last qh.RERUN */
    +  int   TRACEpoint;       /* 'TPn' start tracing when point n is a vertex */
    +  realT TRACEdist;        /* 'TWn' start tracing when merge distance too big */
    +  int   TRACEmerge;       /* 'TMn' start tracing before this merge */
    +  boolT TRIangulate;      /* true 'Qt' if triangulate non-simplicial facets */
    +  boolT TRInormals;       /* true 'Q11' if triangulate duplicates ->normal and ->center (sets Qt) */
    +  boolT UPPERdelaunay;    /* true 'Qu' if computing furthest-site Delaunay */
    +  boolT USEstdout;        /* true 'Tz' if using stdout instead of stderr */
    +  boolT VERIFYoutput;     /* true 'Tv' if verify output at end of qhull */
    +  boolT VIRTUALmemory;    /* true 'Q7' if depth-first processing in buildhull */
    +  boolT VORONOI;          /* true 'v' if computing Voronoi diagram */
    +
    +  /*--------input constants ---------*/
    +  realT AREAfactor;       /* 1/(hull_dim-1)! for converting det's to area */
    +  boolT DOcheckmax;       /* true if calling qh_check_maxout (qh_initqhull_globals) */
    +  char  *feasible_string;  /* feasible point 'Hn,n,n' for halfspace intersection */
    +  coordT *feasible_point;  /*    as coordinates, both malloc'd */
    +  boolT GETarea;          /* true 'Fa', 'FA', 'FS', 'PAn', 'PFn' if compute facet area/Voronoi volume in io.c */
    +  boolT KEEPnearinside;   /* true if near-inside points in coplanarset */
    +  int   hull_dim;         /* dimension of hull, set by initbuffers */
    +  int   input_dim;        /* dimension of input, set by initbuffers */
    +  int   num_points;       /* number of input points */
    +  pointT *first_point;    /* array of input points, see POINTSmalloc */
    +  boolT POINTSmalloc;     /*   true if qh.first_point/num_points allocated */
    +  pointT *input_points;   /* copy of original qh.first_point for input points for qh_joggleinput */
    +  boolT input_malloc;     /* true if qh.input_points malloc'd */
    +  char  qhull_command[256];/* command line that invoked this program */
    +  int   qhull_commandsiz2; /*    size of qhull_command at qh_clear_outputflags */
    +  char  rbox_command[256]; /* command line that produced the input points */
    +  char  qhull_options[512];/* descriptive list of options */
    +  int   qhull_optionlen;  /*    length of last line */
    +  int   qhull_optionsiz;  /*    size of qhull_options at qh_build_withrestart */
    +  int   qhull_optionsiz2; /*    size of qhull_options at qh_clear_outputflags */
    +  int   run_id;           /* non-zero, random identifier for this instance of qhull */
    +  boolT VERTEXneighbors;  /* true if maintaining vertex neighbors */
    +  boolT ZEROcentrum;      /* true if 'C-0' or 'C-0 Qx'.  sets ZEROall_ok */
    +  realT *upper_threshold; /* don't print if facet->normal[k]>=upper_threshold[k]
    +                             must set either GOODthreshold or SPLITthreshold
    +                             if Delaunay, default is 0.0 for upper envelope */
    +  realT *lower_threshold; /* don't print if facet->normal[k] <=lower_threshold[k] */
    +  realT *upper_bound;     /* scale point[k] to new upper bound */
    +  realT *lower_bound;     /* scale point[k] to new lower bound
    +                             project if both upper_ and lower_bound == 0 */
    +
    +/*----------------------------------
    +
    +  qh precision constants
    +    precision constants for Qhull
    +
    +  notes:
    +    qh_detroundoff() computes the maximum roundoff error for distance
    +    and other computations.  It also sets default values for the
    +    qh constants above.
    +*/
    +  realT ANGLEround;       /* max round off error for angles */
    +  realT centrum_radius;   /* max centrum radius for convexity (roundoff added) */
    +  realT cos_max;          /* max cosine for convexity (roundoff added) */
    +  realT DISTround;        /* max round off error for distances, 'E' overrides qh_distround() */
    +  realT MAXabs_coord;     /* max absolute coordinate */
    +  realT MAXlastcoord;     /* max last coordinate for qh_scalelast */
    +  realT MAXsumcoord;      /* max sum of coordinates */
    +  realT MAXwidth;         /* max rectilinear width of point coordinates */
    +  realT MINdenom_1;       /* min. abs. value for 1/x */
    +  realT MINdenom;         /*    use divzero if denominator < MINdenom */
    +  realT MINdenom_1_2;     /* min. abs. val for 1/x that allows normalization */
    +  realT MINdenom_2;       /*    use divzero if denominator < MINdenom_2 */
    +  realT MINlastcoord;     /* min. last coordinate for qh_scalelast */
    +  boolT NARROWhull;       /* set in qh_initialhull if angle < qh_MAXnarrow */
    +  realT *NEARzero;        /* hull_dim array for near zero in gausselim */
    +  realT NEARinside;       /* keep points for qh_check_maxout if close to facet */
    +  realT ONEmerge;         /* max distance for merging simplicial facets */
    +  realT outside_err;      /* application's epsilon for coplanar points
    +                             qh_check_bestdist() qh_check_points() reports error if point outside */
    +  realT WIDEfacet;        /* size of wide facet for skipping ridge in
    +                             area computation and locking centrum */
    +
    +/*----------------------------------
    +
    +  qh internal constants
    +    internal constants for Qhull
    +*/
    +  char qhull[sizeof("qhull")]; /* "qhull" for checking ownership while debugging */
    +  jmp_buf errexit;        /* exit label for qh_errexit, defined by setjmp() and NOerrexit */
    +  char jmpXtra[40];       /* extra bytes in case jmp_buf is defined wrong by compiler */
    +  jmp_buf restartexit;    /* restart label for qh_errexit, defined by setjmp() and ALLOWrestart */
    +  char jmpXtra2[40];      /* extra bytes in case jmp_buf is defined wrong by compiler*/
    +  FILE *fin;              /* pointer to input file, init by qh_initqhull_start2 */
    +  FILE *fout;             /* pointer to output file */
    +  FILE *ferr;             /* pointer to error file */
    +  pointT *interior_point; /* center point of the initial simplex*/
    +  int normal_size;     /* size in bytes for facet normals and point coords*/
    +  int center_size;     /* size in bytes for Voronoi centers */
    +  int   TEMPsize;         /* size for small, temporary sets (in quick mem) */
    +
    +/*----------------------------------
    +
    +  qh facet and vertex lists
    +    defines lists of facets, new facets, visible facets, vertices, and
    +    new vertices.  Includes counts, next ids, and trace ids.
    +  see:
    +    qh_resetlists()
    +*/
    +  facetT *facet_list;     /* first facet */
    +  facetT  *facet_tail;     /* end of facet_list (dummy facet) */
    +  facetT *facet_next;     /* next facet for buildhull()
    +                             previous facets do not have outside sets
    +                             NARROWhull: previous facets may have coplanar outside sets for qh_outcoplanar */
    +  facetT *newfacet_list;  /* list of new facets to end of facet_list */
    +  facetT *visible_list;   /* list of visible facets preceding newfacet_list,
    +                             facet->visible set */
    +  int       num_visible;  /* current number of visible facets */
    +  unsigned tracefacet_id;  /* set at init, then can print whenever */
    +  facetT *tracefacet;     /*   set in newfacet/mergefacet, undone in delfacet*/
    +  unsigned tracevertex_id;  /* set at buildtracing, can print whenever */
    +  vertexT *tracevertex;     /*   set in newvertex, undone in delvertex*/
    +  vertexT *vertex_list;     /* list of all vertices, to vertex_tail */
    +  vertexT  *vertex_tail;    /*      end of vertex_list (dummy vertex) */
    +  vertexT *newvertex_list; /* list of vertices in newfacet_list, to vertex_tail
    +                             all vertices have 'newlist' set */
    +  int   num_facets;       /* number of facets in facet_list
    +                             includes visible faces (num_visible) */
    +  int   num_vertices;     /* number of vertices in facet_list */
    +  int   num_outside;      /* number of points in outsidesets (for tracing and RANDOMoutside)
    +                               includes coplanar outsideset points for NARROWhull/qh_outcoplanar() */
    +  int   num_good;         /* number of good facets (after findgood_all) */
    +  unsigned facet_id;      /* ID of next, new facet from newfacet() */
    +  unsigned ridge_id;      /* ID of next, new ridge from newridge() */
    +  unsigned vertex_id;     /* ID of next, new vertex from newvertex() */
    +
    +/*----------------------------------
    +
    +  qh global variables
    +    defines minimum and maximum distances, next visit ids, several flags,
    +    and other global variables.
    +    initialize in qh_initbuild or qh_maxmin if used in qh_buildhull
    +*/
    +  unsigned long hulltime; /* ignore time to set up input and randomize */
    +                          /*   use unsigned to avoid wrap-around errors */
    +  boolT ALLOWrestart;     /* true if qh_precision can use qh.restartexit */
    +  int   build_cnt;        /* number of calls to qh_initbuild */
    +  qh_CENTER CENTERtype;   /* current type of facet->center, qh_CENTER */
    +  int   furthest_id;      /* pointid of furthest point, for tracing */
    +  facetT *GOODclosest;    /* closest facet to GOODthreshold in qh_findgood */
    +  boolT hasAreaVolume;    /* true if totarea, totvol was defined by qh_getarea */
    +  boolT hasTriangulation; /* true if triangulation created by qh_triangulate */
    +  realT JOGGLEmax;        /* set 'QJn' if randomly joggle input */
    +  boolT maxoutdone;       /* set qh_check_maxout(), cleared by qh_addpoint() */
    +  realT max_outside;      /* maximum distance from a point to a facet,
    +                               before roundoff, not simplicial vertices
    +                               actual outer plane is +DISTround and
    +                               computed outer plane is +2*DISTround */
    +  realT max_vertex;       /* maximum distance (>0) from vertex to a facet,
    +                               before roundoff, due to a merge */
    +  realT min_vertex;       /* minimum distance (<0) from vertex to a facet,
    +                               before roundoff, due to a merge
    +                               if qh.JOGGLEmax, qh_makenewplanes sets it
    +                               recomputed if qh.DOcheckmax, default -qh.DISTround */
    +  boolT NEWfacets;        /* true while visible facets invalid due to new or merge
    +                              from makecone/attachnewfacets to deletevisible */
    +  boolT findbestnew;      /* true if partitioning calls qh_findbestnew */
    +  boolT findbest_notsharp; /* true if new facets are at least 90 degrees */
    +  boolT NOerrexit;        /* true if qh.errexit is not available, cleared after setjmp */
    +  realT PRINTcradius;     /* radius for printing centrums */
    +  realT PRINTradius;      /* radius for printing vertex spheres and points */
    +  boolT POSTmerging;      /* true when post merging */
    +  int   printoutvar;      /* temporary variable for qh_printbegin, etc. */
    +  int   printoutnum;      /* number of facets printed */
    +  boolT QHULLfinished;    /* True after qhull() is finished */
    +  realT totarea;          /* 'FA': total facet area computed by qh_getarea, hasAreaVolume */
    +  realT totvol;           /* 'FA': total volume computed by qh_getarea, hasAreaVolume */
    +  unsigned int visit_id;  /* unique ID for searching neighborhoods, */
    +  unsigned int vertex_visit; /* unique ID for searching vertices, reset with qh_buildtracing */
    +  boolT ZEROall_ok;       /* True if qh_checkzero always succeeds */
    +  boolT WAScoplanar;      /* True if qh_partitioncoplanar (qh_check_maxout) */
    +
    +/*----------------------------------
    +
    +  qh global sets
    +    defines sets for merging, initial simplex, hashing, extra input points,
    +    and deleted vertices
    +*/
    +  setT *facet_mergeset;   /* temporary set of merges to be done */
    +  setT *degen_mergeset;   /* temporary set of degenerate and redundant merges */
    +  setT *hash_table;       /* hash table for matching ridges in qh_matchfacets
    +                             size is setsize() */
    +  setT *other_points;     /* additional points */
    +  setT *del_vertices;     /* vertices to partition and delete with visible
    +                             facets.  Have deleted set for checkfacet */
    +
    +/*----------------------------------
    +
    +  qh global buffers
    +    defines buffers for maxtrix operations, input, and error messages
    +*/
    +  coordT *gm_matrix;      /* (dim+1)Xdim matrix for geom.c */
    +  coordT **gm_row;        /* array of gm_matrix rows */
    +  char* line;             /* malloc'd input line of maxline+1 chars */
    +  int maxline;
    +  coordT *half_space;     /* malloc'd input array for halfspace (qh normal_size+coordT) */
    +  coordT *temp_malloc;    /* malloc'd input array for points */
    +
    +/*----------------------------------
    +
    +  qh static variables
    +    defines static variables for individual functions
    +
    +  notes:
    +    do not use 'static' within a function.  Multiple instances of qhull
    +    may exist.
    +
    +    do not assume zero initialization, 'QPn' may cause a restart
    +*/
    +  boolT ERREXITcalled;    /* true during qh_errexit (prevents duplicate calls */
    +  boolT firstcentrum;     /* for qh_printcentrum */
    +  boolT old_randomdist;   /* save RANDOMdist flag during io, tracing, or statistics */
    +  setT *coplanarfacetset;  /* set of coplanar facets for searching qh_findbesthorizon() */
    +  realT last_low;         /* qh_scalelast parameters for qh_setdelaunay */
    +  realT last_high;
    +  realT last_newhigh;
    +  unsigned lastreport;    /* for qh_buildtracing */
    +  int mergereport;        /* for qh_tracemerging */
    +  qhstatT *old_qhstat;    /* for saving qh_qhstat in save_qhull() and UsingLibQhull.  Free with qh_free() */
    +  setT *old_tempstack;    /* for saving qhmem.tempstack in save_qhull */
    +  int   ridgeoutnum;      /* number of ridges for 4OFF output (qh_printbegin,etc) */
    +};
    +
    +/*=========== -macros- =========================*/
    +
    +/*----------------------------------
    +
    +  otherfacet_(ridge, facet)
    +    return neighboring facet for a ridge in facet
    +*/
    +#define otherfacet_(ridge, facet) \
    +                        (((ridge)->top == (facet)) ? (ridge)->bottom : (ridge)->top)
    +
    +/*----------------------------------
    +
    +  getid_(p)
    +    return int ID for facet, ridge, or vertex
    +    return qh_IDunknown(-1) if NULL
    +*/
    +#define getid_(p)       ((p) ? (int)((p)->id) : qh_IDunknown)
    +
    +/*============== FORALL macros ===================*/
    +
    +/*----------------------------------
    +
    +  FORALLfacets { ... }
    +    assign 'facet' to each facet in qh.facet_list
    +
    +  notes:
    +    uses 'facetT *facet;'
    +    assumes last facet is a sentinel
    +
    +  see:
    +    FORALLfacet_( facetlist )
    +*/
    +#define FORALLfacets for (facet=qh facet_list;facet && facet->next;facet=facet->next)
    +
    +/*----------------------------------
    +
    +  FORALLpoints { ... }
    +    assign 'point' to each point in qh.first_point, qh.num_points
    +
    +  declare:
    +    coordT *point, *pointtemp;
    +*/
    +#define FORALLpoints FORALLpoint_(qh first_point, qh num_points)
    +
    +/*----------------------------------
    +
    +  FORALLpoint_( points, num) { ... }
    +    assign 'point' to each point in points array of num points
    +
    +  declare:
    +    coordT *point, *pointtemp;
    +*/
    +#define FORALLpoint_(points, num) for (point= (points), \
    +      pointtemp= (points)+qh hull_dim*(num); point < pointtemp; point += qh hull_dim)
    +
    +/*----------------------------------
    +
    +  FORALLvertices { ... }
    +    assign 'vertex' to each vertex in qh.vertex_list
    +
    +  declare:
    +    vertexT *vertex;
    +
    +  notes:
    +    assumes qh.vertex_list terminated with a sentinel
    +*/
    +#define FORALLvertices for (vertex=qh vertex_list;vertex && vertex->next;vertex= vertex->next)
    +
    +/*----------------------------------
    +
    +  FOREACHfacet_( facets ) { ... }
    +    assign 'facet' to each facet in facets
    +
    +  declare:
    +    facetT *facet, **facetp;
    +
    +  see:
    +    FOREACHsetelement_
    +*/
    +#define FOREACHfacet_(facets)    FOREACHsetelement_(facetT, facets, facet)
    +
    +/*----------------------------------
    +
    +  FOREACHneighbor_( facet ) { ... }
    +    assign 'neighbor' to each neighbor in facet->neighbors
    +
    +  FOREACHneighbor_( vertex ) { ... }
    +    assign 'neighbor' to each neighbor in vertex->neighbors
    +
    +  declare:
    +    facetT *neighbor, **neighborp;
    +
    +  see:
    +    FOREACHsetelement_
    +*/
    +#define FOREACHneighbor_(facet)  FOREACHsetelement_(facetT, facet->neighbors, neighbor)
    +
    +/*----------------------------------
    +
    +  FOREACHpoint_( points ) { ... }
    +    assign 'point' to each point in points set
    +
    +  declare:
    +    pointT *point, **pointp;
    +
    +  see:
    +    FOREACHsetelement_
    +*/
    +#define FOREACHpoint_(points)    FOREACHsetelement_(pointT, points, point)
    +
    +/*----------------------------------
    +
    +  FOREACHridge_( ridges ) { ... }
    +    assign 'ridge' to each ridge in ridges set
    +
    +  declare:
    +    ridgeT *ridge, **ridgep;
    +
    +  see:
    +    FOREACHsetelement_
    +*/
    +#define FOREACHridge_(ridges)    FOREACHsetelement_(ridgeT, ridges, ridge)
    +
    +/*----------------------------------
    +
    +  FOREACHvertex_( vertices ) { ... }
    +    assign 'vertex' to each vertex in vertices set
    +
    +  declare:
    +    vertexT *vertex, **vertexp;
    +
    +  see:
    +    FOREACHsetelement_
    +*/
    +#define FOREACHvertex_(vertices) FOREACHsetelement_(vertexT, vertices,vertex)
    +
    +/*----------------------------------
    +
    +  FOREACHfacet_i_( facets ) { ... }
    +    assign 'facet' and 'facet_i' for each facet in facets set
    +
    +  declare:
    +    facetT *facet;
    +    int     facet_n, facet_i;
    +
    +  see:
    +    FOREACHsetelement_i_
    +*/
    +#define FOREACHfacet_i_(facets)    FOREACHsetelement_i_(facetT, facets, facet)
    +
    +/*----------------------------------
    +
    +  FOREACHneighbor_i_( facet ) { ... }
    +    assign 'neighbor' and 'neighbor_i' for each neighbor in facet->neighbors
    +
    +  FOREACHneighbor_i_( vertex ) { ... }
    +    assign 'neighbor' and 'neighbor_i' for each neighbor in vertex->neighbors
    +
    +  declare:
    +    facetT *neighbor;
    +    int     neighbor_n, neighbor_i;
    +
    +  see:
    +    FOREACHsetelement_i_
    +*/
    +#define FOREACHneighbor_i_(facet)  FOREACHsetelement_i_(facetT, facet->neighbors, neighbor)
    +
    +/*----------------------------------
    +
    +  FOREACHpoint_i_( points ) { ... }
    +    assign 'point' and 'point_i' for each point in points set
    +
    +  declare:
    +    pointT *point;
    +    int     point_n, point_i;
    +
    +  see:
    +    FOREACHsetelement_i_
    +*/
    +#define FOREACHpoint_i_(points)    FOREACHsetelement_i_(pointT, points, point)
    +
    +/*----------------------------------
    +
    +  FOREACHridge_i_( ridges ) { ... }
    +    assign 'ridge' and 'ridge_i' for each ridge in ridges set
    +
    +  declare:
    +    ridgeT *ridge;
    +    int     ridge_n, ridge_i;
    +
    +  see:
    +    FOREACHsetelement_i_
    +*/
    +#define FOREACHridge_i_(ridges)    FOREACHsetelement_i_(ridgeT, ridges, ridge)
    +
    +/*----------------------------------
    +
    +  FOREACHvertex_i_( vertices ) { ... }
    +    assign 'vertex' and 'vertex_i' for each vertex in vertices set
    +
    +  declare:
    +    vertexT *vertex;
    +    int     vertex_n, vertex_i;
    +
    +  see:
    +    FOREACHsetelement_i_
    +*/
    +#define FOREACHvertex_i_(vertices) FOREACHsetelement_i_(vertexT, vertices,vertex)
    +
    +/********* -libqhull.c prototypes (duplicated from qhull_a.h) **********************/
    +
    +void    qh_qhull(void);
    +boolT   qh_addpoint(pointT *furthest, facetT *facet, boolT checkdist);
    +void    qh_printsummary(FILE *fp);
    +
    +/********* -user.c prototypes (alphabetical) **********************/
    +
    +void    qh_errexit(int exitcode, facetT *facet, ridgeT *ridge);
    +void    qh_errprint(const char* string, facetT *atfacet, facetT *otherfacet, ridgeT *atridge, vertexT *atvertex);
    +int     qh_new_qhull(int dim, int numpoints, coordT *points, boolT ismalloc,
    +                char *qhull_cmd, FILE *outfile, FILE *errfile);
    +void    qh_printfacetlist(facetT *facetlist, setT *facets, boolT printall);
    +void    qh_printhelp_degenerate(FILE *fp);
    +void    qh_printhelp_narrowhull(FILE *fp, realT minangle);
    +void    qh_printhelp_singular(FILE *fp);
    +void    qh_user_memsizes(void);
    +
    +/********* -usermem.c prototypes (alphabetical) **********************/
    +void    qh_exit(int exitcode);
    +void    qh_fprintf_stderr(int msgcode, const char *fmt, ... );
    +void    qh_free(void *mem);
    +void   *qh_malloc(size_t size);
    +
    +/********* -userprintf.c and userprintf_rbox.c prototypes **********************/
    +void    qh_fprintf(FILE *fp, int msgcode, const char *fmt, ... );
    +void    qh_fprintf_rbox(FILE *fp, int msgcode, const char *fmt, ... );
    +
    +/***** -geom.c/geom2.c/random.c prototypes (duplicated from geom.h, random.h) ****************/
    +
    +facetT *qh_findbest(pointT *point, facetT *startfacet,
    +                     boolT bestoutside, boolT newfacets, boolT noupper,
    +                     realT *dist, boolT *isoutside, int *numpart);
    +facetT *qh_findbestnew(pointT *point, facetT *startfacet,
    +                     realT *dist, boolT bestoutside, boolT *isoutside, int *numpart);
    +boolT   qh_gram_schmidt(int dim, realT **rows);
    +void    qh_outerinner(facetT *facet, realT *outerplane, realT *innerplane);
    +void    qh_printsummary(FILE *fp);
    +void    qh_projectinput(void);
    +void    qh_randommatrix(realT *buffer, int dim, realT **row);
    +void    qh_rotateinput(realT **rows);
    +void    qh_scaleinput(void);
    +void    qh_setdelaunay(int dim, int count, pointT *points);
    +coordT  *qh_sethalfspace_all(int dim, int count, coordT *halfspaces, pointT *feasible);
    +
    +/***** -global.c prototypes (alphabetical) ***********************/
    +
    +unsigned long qh_clock(void);
    +void    qh_checkflags(char *command, char *hiddenflags);
    +void    qh_clear_outputflags(void);
    +void    qh_freebuffers(void);
    +void    qh_freeqhull(boolT allmem);
    +void    qh_freeqhull2(boolT allmem);
    +void    qh_init_A(FILE *infile, FILE *outfile, FILE *errfile, int argc, char *argv[]);
    +void    qh_init_B(coordT *points, int numpoints, int dim, boolT ismalloc);
    +void    qh_init_qhull_command(int argc, char *argv[]);
    +void    qh_initbuffers(coordT *points, int numpoints, int dim, boolT ismalloc);
    +void    qh_initflags(char *command);
    +void    qh_initqhull_buffers(void);
    +void    qh_initqhull_globals(coordT *points, int numpoints, int dim, boolT ismalloc);
    +void    qh_initqhull_mem(void);
    +void    qh_initqhull_outputflags(void);
    +void    qh_initqhull_start(FILE *infile, FILE *outfile, FILE *errfile);
    +void    qh_initqhull_start2(FILE *infile, FILE *outfile, FILE *errfile);
    +void    qh_initthresholds(char *command);
    +void    qh_lib_check(int qhullLibraryType, int qhTsize, int vertexTsize, int ridgeTsize, int facetTsize, int setTsize, int qhmemTsize);
    +void    qh_option(const char *option, int *i, realT *r);
    +#if qh_QHpointer
    +void    qh_restore_qhull(qhT **oldqh);
    +qhT    *qh_save_qhull(void);
    +#endif
    +
    +/***** -io.c prototypes (duplicated from io.h) ***********************/
    +
    +void    qh_dfacet(unsigned id);
    +void    qh_dvertex(unsigned id);
    +void    qh_printneighborhood(FILE *fp, qh_PRINT format, facetT *facetA, facetT *facetB, boolT printall);
    +void    qh_produce_output(void);
    +coordT *qh_readpoints(int *numpoints, int *dimension, boolT *ismalloc);
    +
    +
    +/********* -mem.c prototypes (duplicated from mem.h) **********************/
    +
    +void qh_meminit(FILE *ferr);
    +void qh_memfreeshort(int *curlong, int *totlong);
    +
    +/********* -poly.c/poly2.c prototypes (duplicated from poly.h) **********************/
    +
    +void    qh_check_output(void);
    +void    qh_check_points(void);
    +setT   *qh_facetvertices(facetT *facetlist, setT *facets, boolT allfacets);
    +facetT *qh_findbestfacet(pointT *point, boolT bestoutside,
    +           realT *bestdist, boolT *isoutside);
    +vertexT *qh_nearvertex(facetT *facet, pointT *point, realT *bestdistp);
    +pointT *qh_point(int id);
    +setT   *qh_pointfacet(void /*qh.facet_list*/);
    +int     qh_pointid(pointT *point);
    +setT   *qh_pointvertex(void /*qh.facet_list*/);
    +void    qh_setvoronoi_all(void);
    +void    qh_triangulate(void /*qh.facet_list*/);
    +
    +/********* -rboxlib.c prototypes **********************/
    +int     qh_rboxpoints(FILE* fout, FILE* ferr, char* rbox_command);
    +void    qh_errexit_rbox(int exitcode);
    +
    +/********* -stat.c prototypes (duplicated from stat.h) **********************/
    +
    +void    qh_collectstatistics(void);
    +void    qh_printallstatistics(FILE *fp, const char *string);
    +
    +#endif /* qhDEFlibqhull */
    diff --git a/xs/src/qhull/src/libqhull/libqhull.pro b/xs/src/qhull/src/libqhull/libqhull.pro
    new file mode 100644
    index 000000000..18005da59
    --- /dev/null
    +++ b/xs/src/qhull/src/libqhull/libqhull.pro
    @@ -0,0 +1,67 @@
    +# -------------------------------------------------
    +# libqhull.pro -- Qt project for Qhull shared library
    +# -------------------------------------------------
    +
    +include(../qhull-warn.pri)
    +
    +DESTDIR = ../../lib
    +DLLDESTDIR = ../../bin
    +TEMPLATE = lib
    +CONFIG += shared warn_on
    +CONFIG -= qt
    +
    +build_pass:CONFIG(debug, debug|release):{
    +    TARGET = qhull_d
    +    OBJECTS_DIR = Debug
    +}else:build_pass:CONFIG(release, debug|release):{
    +    TARGET = qhull
    +    OBJECTS_DIR = Release
    +}
    +win32-msvc* : QMAKE_LFLAGS += /INCREMENTAL:NO
    +
    +win32-msvc* : DEF_FILE += ../../src/libqhull/qhull-exports.def
    +
    +# Order object files by frequency of execution.  Small files at end.
    +
    +# libqhull/libqhull.pro and ../qhull-libqhull-src.pri have the same SOURCES and HEADERS
    +SOURCES += ../libqhull/global.c
    +SOURCES += ../libqhull/stat.c
    +SOURCES += ../libqhull/geom2.c
    +SOURCES += ../libqhull/poly2.c
    +SOURCES += ../libqhull/merge.c
    +SOURCES += ../libqhull/libqhull.c
    +SOURCES += ../libqhull/geom.c
    +SOURCES += ../libqhull/poly.c
    +SOURCES += ../libqhull/qset.c
    +SOURCES += ../libqhull/mem.c
    +SOURCES += ../libqhull/random.c
    +SOURCES += ../libqhull/usermem.c
    +SOURCES += ../libqhull/userprintf.c
    +SOURCES += ../libqhull/io.c
    +SOURCES += ../libqhull/user.c
    +SOURCES += ../libqhull/rboxlib.c
    +SOURCES += ../libqhull/userprintf_rbox.c
    +
    +HEADERS += ../libqhull/geom.h
    +HEADERS += ../libqhull/io.h
    +HEADERS += ../libqhull/libqhull.h
    +HEADERS += ../libqhull/mem.h
    +HEADERS += ../libqhull/merge.h
    +HEADERS += ../libqhull/poly.h
    +HEADERS += ../libqhull/random.h
    +HEADERS += ../libqhull/qhull_a.h
    +HEADERS += ../libqhull/qset.h
    +HEADERS += ../libqhull/stat.h
    +HEADERS += ../libqhull/user.h
    +
    +OTHER_FILES += Mborland
    +OTHER_FILES += qh-geom.htm
    +OTHER_FILES += qh-globa.htm
    +OTHER_FILES += qh-io.htm
    +OTHER_FILES += qh-mem.htm
    +OTHER_FILES += qh-merge.htm
    +OTHER_FILES += qh-poly.htm
    +OTHER_FILES += qh-qhull.htm
    +OTHER_FILES += qh-set.htm
    +OTHER_FILES += qh-stat.htm
    +OTHER_FILES += qh-user.htm
    diff --git a/xs/src/qhull/src/libqhull/mem.c b/xs/src/qhull/src/libqhull/mem.c
    new file mode 100644
    index 000000000..db72bb4e1
    --- /dev/null
    +++ b/xs/src/qhull/src/libqhull/mem.c
    @@ -0,0 +1,576 @@
    +/*
      ---------------------------------
    +
    +  mem.c
    +    memory management routines for qhull
    +
    +  This is a standalone program.
    +
    +  To initialize memory:
    +
    +    qh_meminit(stderr);
    +    qh_meminitbuffers(qh IStracing, qh_MEMalign, 7, qh_MEMbufsize,qh_MEMinitbuf);
    +    qh_memsize((int)sizeof(facetT));
    +    qh_memsize((int)sizeof(facetT));
    +    ...
    +    qh_memsetup();
    +
    +  To free up all memory buffers:
    +    qh_memfreeshort(&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.htm and mem.h
    +    global.c (qh_initbuffers) for an example of using mem.c
    +
    +  Copyright (c) 1993-2015 The Geometry Center.
    +  $Id: //main/2015/qhull/src/libqhull/mem.c#7 $$Change: 2065 $
    +  $DateTime: 2016/01/18 13:51:04 $$Author: bbarber $
    +*/
    +
    +#include "user.h"  /* for QHULL_CRTDBG */
    +#include "mem.h"
    +#include 
    +#include 
    +#include 
    +
    +#ifndef qhDEFlibqhull
    +typedef struct ridgeT ridgeT;
    +typedef struct facetT facetT;
    +#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
    +void    qh_errexit(int exitcode, facetT *, ridgeT *);
    +void    qh_exit(int exitcode);
    +void    qh_fprintf(FILE *fp, int msgcode, const char *fmt, ... );
    +void    qh_fprintf_stderr(int msgcode, const char *fmt, ... );
    +void    qh_free(void *mem);
    +void   *qh_malloc(size_t size);
    +#endif
    +
    +/*============ -global data structure ==============
    +    see mem.h for definition
    +*/
    +
    +qhmemT qhmem= {0,0,0,0,0,0,0,0,0,0,0,
    +               0,0,0,0,0,0,0,0,0,0,0,
    +               0,0,0,0,0,0,0};     /* remove "= {0}" if this causes a compiler error */
    +
    +#ifndef qh_NOmem
    +
    +/*============= internal functions ==============*/
    +
    +static int qh_intcompare(const void *i, const void *j);
    +
    +/*========== functions in alphabetical order ======== */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +
    +/*----------------------------------
    +
    +  qh_memalloc( 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 < qhmem.LASTsize
    +      if qhmem.freelists[size] non-empty
    +        return first object on freelist
    +      else
    +        round up request to size of 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(int insize) {
    +  void **freelistp, *newbuffer;
    +  int idx, size, n;
    +  int outsize, bufsize;
    +  void *object;
    +
    +  if (insize<0) {
    +      qh_fprintf(qhmem.ferr, 6235, "qhull error (qh_memalloc): negative request size (%d).  Did int overflow due to high-D?\n", insize); /* WARN64 */
    +      qh_errexit(qhmem_ERRmem, NULL, NULL);
    +  }
    +  if (insize>=0 && insize <= qhmem.LASTsize) {
    +    idx= qhmem.indextable[insize];
    +    outsize= qhmem.sizetable[idx];
    +    qhmem.totshort += outsize;
    +    freelistp= qhmem.freelists+idx;
    +    if ((object= *freelistp)) {
    +      qhmem.cntquick++;
    +      qhmem.totfree -= outsize;
    +      *freelistp= *((void **)*freelistp);  /* replace freelist with next object */
    +#ifdef qh_TRACEshort
    +      n= qhmem.cntshort+qhmem.cntquick+qhmem.freeshort;
    +      if (qhmem.IStracing >= 5)
    +          qh_fprintf(qhmem.ferr, 8141, "qh_mem %p n %8d alloc quick: %d bytes (tot %d cnt %d)\n", object, n, outsize, qhmem.totshort, qhmem.cntshort+qhmem.cntquick-qhmem.freeshort);
    +#endif
    +      return(object);
    +    }else {
    +      qhmem.cntshort++;
    +      if (outsize > qhmem.freesize) {
    +        qhmem.totdropped += qhmem.freesize;
    +        if (!qhmem.curbuffer)
    +          bufsize= qhmem.BUFinit;
    +        else
    +          bufsize= qhmem.BUFsize;
    +        if (!(newbuffer= qh_malloc((size_t)bufsize))) {
    +          qh_fprintf(qhmem.ferr, 6080, "qhull error (qh_memalloc): insufficient memory to allocate short memory buffer (%d bytes)\n", bufsize);
    +          qh_errexit(qhmem_ERRmem, NULL, NULL);
    +        }
    +        *((void **)newbuffer)= qhmem.curbuffer;  /* prepend newbuffer to curbuffer
    +                                                    list.  newbuffer!=0 by QH6080 */
    +        qhmem.curbuffer= newbuffer;
    +        size= (sizeof(void **) + qhmem.ALIGNmask) & ~qhmem.ALIGNmask;
    +        qhmem.freemem= (void *)((char *)newbuffer+size);
    +        qhmem.freesize= bufsize - size;
    +        qhmem.totbuffer += bufsize - size; /* easier to check */
    +        /* Periodically test totbuffer.  It matches at beginning and exit of every call */
    +        n = qhmem.totshort + qhmem.totfree + qhmem.totdropped + qhmem.freesize - outsize;
    +        if (qhmem.totbuffer != n) {
    +            qh_fprintf(qhmem.ferr, 6212, "qh_memalloc internal error: short totbuffer %d != totshort+totfree... %d\n", qhmem.totbuffer, n);
    +            qh_errexit(qhmem_ERRmem, NULL, NULL);
    +        }
    +      }
    +      object= qhmem.freemem;
    +      qhmem.freemem= (void *)((char *)qhmem.freemem + outsize);
    +      qhmem.freesize -= outsize;
    +      qhmem.totunused += outsize - insize;
    +#ifdef qh_TRACEshort
    +      n= qhmem.cntshort+qhmem.cntquick+qhmem.freeshort;
    +      if (qhmem.IStracing >= 5)
    +          qh_fprintf(qhmem.ferr, 8140, "qh_mem %p n %8d alloc short: %d bytes (tot %d cnt %d)\n", object, n, outsize, qhmem.totshort, qhmem.cntshort+qhmem.cntquick-qhmem.freeshort);
    +#endif
    +      return object;
    +    }
    +  }else {                     /* long allocation */
    +    if (!qhmem.indextable) {
    +      qh_fprintf(qhmem.ferr, 6081, "qhull internal error (qh_memalloc): qhmem has not been initialized.\n");
    +      qh_errexit(qhmem_ERRqhull, NULL, NULL);
    +    }
    +    outsize= insize;
    +    qhmem.cntlong++;
    +    qhmem.totlong += outsize;
    +    if (qhmem.maxlong < qhmem.totlong)
    +      qhmem.maxlong= qhmem.totlong;
    +    if (!(object= qh_malloc((size_t)outsize))) {
    +      qh_fprintf(qhmem.ferr, 6082, "qhull error (qh_memalloc): insufficient memory to allocate %d bytes\n", outsize);
    +      qh_errexit(qhmem_ERRmem, NULL, NULL);
    +    }
    +    if (qhmem.IStracing >= 5)
    +      qh_fprintf(qhmem.ferr, 8057, "qh_mem %p n %8d alloc long: %d bytes (tot %d cnt %d)\n", object, qhmem.cntlong+qhmem.freelong, outsize, qhmem.totlong, qhmem.cntlong-qhmem.freelong);
    +  }
    +  return(object);
    +} /* memalloc */
    +
    +
    +/*----------------------------------
    +
    +  qh_memcheck( )
    +*/
    +void qh_memcheck(void) {
    +  int i, count, totfree= 0;
    +  void *object;
    +
    +  if (qhmem.ferr == 0 || qhmem.IStracing < 0 || qhmem.IStracing > 10 || (((qhmem.ALIGNmask+1) & qhmem.ALIGNmask) != 0)) {
    +    qh_fprintf_stderr(6244, "qh_memcheck error: either qhmem is overwritten or qhmem is not initialized.  Call qh_meminit() or qh_new_qhull() before calling qh_mem routines.  ferr 0x%x IsTracing %d ALIGNmask 0x%x", qhmem.ferr, qhmem.IStracing, qhmem.ALIGNmask);
    +    qh_exit(qhmem_ERRqhull);  /* can not use qh_errexit() */
    +  }
    +  if (qhmem.IStracing != 0)
    +    qh_fprintf(qhmem.ferr, 8143, "qh_memcheck: check size of freelists on qhmem\nqh_memcheck: A segmentation fault indicates an overwrite of qhmem\n");
    +  for (i=0; i < qhmem.TABLEsize; i++) {
    +    count=0;
    +    for (object= qhmem.freelists[i]; object; object= *((void **)object))
    +      count++;
    +    totfree += qhmem.sizetable[i] * count;
    +  }
    +  if (totfree != qhmem.totfree) {
    +    qh_fprintf(qhmem.ferr, 6211, "Qhull internal error (qh_memcheck): totfree %d not equal to freelist total %d\n", qhmem.totfree, totfree);
    +    qh_errexit(qhmem_ERRqhull, NULL, NULL);
    +  }
    +  if (qhmem.IStracing != 0)
    +    qh_fprintf(qhmem.ferr, 8144, "qh_memcheck: total size of freelists totfree is the same as qhmem.totfree\n", totfree);
    +} /* memcheck */
    +
    +/*----------------------------------
    +
    +  qh_memfree( 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 <= qhmem.LASTsize
    +      append object to corresponding freelist
    +    else
    +      call qh_free(object)
    +*/
    +void qh_memfree(void *object, int insize) {
    +  void **freelistp;
    +  int idx, outsize;
    +
    +  if (!object)
    +    return;
    +  if (insize <= qhmem.LASTsize) {
    +    qhmem.freeshort++;
    +    idx= qhmem.indextable[insize];
    +    outsize= qhmem.sizetable[idx];
    +    qhmem.totfree += outsize;
    +    qhmem.totshort -= outsize;
    +    freelistp= qhmem.freelists + idx;
    +    *((void **)object)= *freelistp;
    +    *freelistp= object;
    +#ifdef qh_TRACEshort
    +    idx= qhmem.cntshort+qhmem.cntquick+qhmem.freeshort;
    +    if (qhmem.IStracing >= 5)
    +        qh_fprintf(qhmem.ferr, 8142, "qh_mem %p n %8d free short: %d bytes (tot %d cnt %d)\n", object, idx, outsize, qhmem.totshort, qhmem.cntshort+qhmem.cntquick-qhmem.freeshort);
    +#endif
    +  }else {
    +    qhmem.freelong++;
    +    qhmem.totlong -= insize;
    +    if (qhmem.IStracing >= 5)
    +      qh_fprintf(qhmem.ferr, 8058, "qh_mem %p n %8d free long: %d bytes (tot %d cnt %d)\n", object, qhmem.cntlong+qhmem.freelong, insize, qhmem.totlong, qhmem.cntlong-qhmem.freelong);
    +    qh_free(object);
    +  }
    +} /* memfree */
    +
    +
    +/*---------------------------------
    +
    +  qh_memfreeshort( curlong, totlong )
    +    frees up all short and qhmem memory allocations
    +
    +  returns:
    +    number and size of current long allocations
    +
    +  see:
    +    qh_freeqhull(allMem)
    +    qh_memtotal(curlong, totlong, curshort, totshort, maxlong, totbuffer);
    +*/
    +void qh_memfreeshort(int *curlong, int *totlong) {
    +  void *buffer, *nextbuffer;
    +  FILE *ferr;
    +
    +  *curlong= qhmem.cntlong - qhmem.freelong;
    +  *totlong= qhmem.totlong;
    +  for (buffer= qhmem.curbuffer; buffer; buffer= nextbuffer) {
    +    nextbuffer= *((void **) buffer);
    +    qh_free(buffer);
    +  }
    +  qhmem.curbuffer= NULL;
    +  if (qhmem.LASTsize) {
    +    qh_free(qhmem.indextable);
    +    qh_free(qhmem.freelists);
    +    qh_free(qhmem.sizetable);
    +  }
    +  ferr= qhmem.ferr;
    +  memset((char *)&qhmem, 0, sizeof(qhmem));  /* every field is 0, FALSE, NULL */
    +  qhmem.ferr= ferr;
    +} /* memfreeshort */
    +
    +
    +/*----------------------------------
    +
    +  qh_meminit( ferr )
    +    initialize qhmem and test sizeof( void*)
    +    Does not throw errors.  qh_exit on failure
    +*/
    +void qh_meminit(FILE *ferr) {
    +
    +  memset((char *)&qhmem, 0, sizeof(qhmem));  /* every field is 0, FALSE, NULL */
    +  if (ferr)
    +    qhmem.ferr= ferr;
    +  else
    +    qhmem.ferr= stderr;
    +  if (sizeof(void*) < sizeof(int)) {
    +    qh_fprintf(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(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();
    +} /* meminit */
    +
    +/*---------------------------------
    +
    +  qh_meminitbuffers( 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(int tracelevel, int alignment, int numsizes, int bufsize, int bufinit) {
    +
    +  qhmem.IStracing= tracelevel;
    +  qhmem.NUMsizes= numsizes;
    +  qhmem.BUFsize= bufsize;
    +  qhmem.BUFinit= bufinit;
    +  qhmem.ALIGNmask= alignment-1;
    +  if (qhmem.ALIGNmask & ~qhmem.ALIGNmask) {
    +    qh_fprintf(qhmem.ferr, 6085, "qhull internal error (qh_meminit): memory alignment %d is not a power of 2\n", alignment);
    +    qh_errexit(qhmem_ERRqhull, NULL, NULL);
    +  }
    +  qhmem.sizetable= (int *) calloc((size_t)numsizes, sizeof(int));
    +  qhmem.freelists= (void **) calloc((size_t)numsizes, sizeof(void *));
    +  if (!qhmem.sizetable || !qhmem.freelists) {
    +    qh_fprintf(qhmem.ferr, 6086, "qhull error (qh_meminit): insufficient memory\n");
    +    qh_errexit(qhmem_ERRmem, NULL, NULL);
    +  }
    +  if (qhmem.IStracing >= 1)
    +    qh_fprintf(qhmem.ferr, 8059, "qh_meminitbuffers: memory initialized with alignment %d\n", alignment);
    +} /* meminitbuffers */
    +
    +/*---------------------------------
    +
    +  qh_memsetup()
    +    set up memory after running memsize()
    +*/
    +void qh_memsetup(void) {
    +  int k,i;
    +
    +  qsort(qhmem.sizetable, (size_t)qhmem.TABLEsize, sizeof(int), qh_intcompare);
    +  qhmem.LASTsize= qhmem.sizetable[qhmem.TABLEsize-1];
    +  if (qhmem.LASTsize >= qhmem.BUFsize || qhmem.LASTsize >= qhmem.BUFinit) {
    +    qh_fprintf(qhmem.ferr, 6087, "qhull error (qh_memsetup): largest mem size %d is >= buffer size %d or initial buffer size %d\n",
    +            qhmem.LASTsize, qhmem.BUFsize, qhmem.BUFinit);
    +    qh_errexit(qhmem_ERRmem, NULL, NULL);
    +  }
    +  if (!(qhmem.indextable= (int *)qh_malloc((qhmem.LASTsize+1) * sizeof(int)))) {
    +    qh_fprintf(qhmem.ferr, 6088, "qhull error (qh_memsetup): insufficient memory\n");
    +    qh_errexit(qhmem_ERRmem, NULL, NULL);
    +  }
    +  for (k=qhmem.LASTsize+1; k--; )
    +    qhmem.indextable[k]= k;
    +  i= 0;
    +  for (k=0; k <= qhmem.LASTsize; k++) {
    +    if (qhmem.indextable[k] <= qhmem.sizetable[i])
    +      qhmem.indextable[k]= i;
    +    else
    +      qhmem.indextable[k]= ++i;
    +  }
    +} /* memsetup */
    +
    +/*---------------------------------
    +
    +  qh_memsize( size )
    +    define a free list for this size
    +*/
    +void qh_memsize(int size) {
    +  int k;
    +
    +  if (qhmem.LASTsize) {
    +    qh_fprintf(qhmem.ferr, 6089, "qhull error (qh_memsize): called after qhmem_setup\n");
    +    qh_errexit(qhmem_ERRqhull, NULL, NULL);
    +  }
    +  size= (size + qhmem.ALIGNmask) & ~qhmem.ALIGNmask;
    +  for (k=qhmem.TABLEsize; k--; ) {
    +    if (qhmem.sizetable[k] == size)
    +      return;
    +  }
    +  if (qhmem.TABLEsize < qhmem.NUMsizes)
    +    qhmem.sizetable[qhmem.TABLEsize++]= size;
    +  else
    +    qh_fprintf(qhmem.ferr, 7060, "qhull warning (memsize): free list table has room for only %d sizes\n", qhmem.NUMsizes);
    +} /* memsize */
    +
    +
    +/*---------------------------------
    +
    +  qh_memstatistics( fp )
    +    print out memory statistics
    +
    +    Verifies that qhmem.totfree == sum of freelists
    +*/
    +void qh_memstatistics(FILE *fp) {
    +  int i;
    +  int count;
    +  void *object;
    +
    +  qh_memcheck();
    +  qh_fprintf(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",
    +           qhmem.cntquick, qhmem.cntshort, qhmem.cntlong,
    +           qhmem.freeshort, qhmem.freelong,
    +           qhmem.totshort, qhmem.totfree,
    +           qhmem.totdropped + qhmem.freesize, qhmem.totunused,
    +           qhmem.maxlong, qhmem.totlong, qhmem.cntlong - qhmem.freelong,
    +           qhmem.totbuffer, qhmem.BUFsize, qhmem.BUFinit);
    +  if (qhmem.cntlarger) {
    +    qh_fprintf(fp, 9279, "%7d calls to qh_setlarger\n%7.2g     average copy size\n",
    +           qhmem.cntlarger, ((float)qhmem.totlarger)/(float)qhmem.cntlarger);
    +    qh_fprintf(fp, 9280, "  freelists(bytes->count):");
    +  }
    +  for (i=0; i < qhmem.TABLEsize; i++) {
    +    count=0;
    +    for (object= qhmem.freelists[i]; object; object= *((void **)object))
    +      count++;
    +    qh_fprintf(fp, 9281, " %d->%d", qhmem.sizetable[i], count);
    +  }
    +  qh_fprintf(fp, 9282, "\n\n");
    +} /* memstatistics */
    +
    +
    +/*---------------------------------
    +
    +  qh_NOmem
    +    turn off quick-fit memory allocation
    +
    +  notes:
    +    uses qh_malloc() and qh_free() instead
    +*/
    +#else /* qh_NOmem */
    +
    +void *qh_memalloc(int insize) {
    +  void *object;
    +
    +  if (!(object= qh_malloc((size_t)insize))) {
    +    qh_fprintf(qhmem.ferr, 6090, "qhull error (qh_memalloc): insufficient memory\n");
    +    qh_errexit(qhmem_ERRmem, NULL, NULL);
    +  }
    +  qhmem.cntlong++;
    +  qhmem.totlong += insize;
    +  if (qhmem.maxlong < qhmem.totlong)
    +      qhmem.maxlong= qhmem.totlong;
    +  if (qhmem.IStracing >= 5)
    +    qh_fprintf(qhmem.ferr, 8060, "qh_mem %p n %8d alloc long: %d bytes (tot %d cnt %d)\n", object, qhmem.cntlong+qhmem.freelong, insize, qhmem.totlong, qhmem.cntlong-qhmem.freelong);
    +  return object;
    +}
    +
    +void qh_memfree(void *object, int insize) {
    +
    +  if (!object)
    +    return;
    +  qh_free(object);
    +  qhmem.freelong++;
    +  qhmem.totlong -= insize;
    +  if (qhmem.IStracing >= 5)
    +    qh_fprintf(qhmem.ferr, 8061, "qh_mem %p n %8d free long: %d bytes (tot %d cnt %d)\n", object, qhmem.cntlong+qhmem.freelong, insize, qhmem.totlong, qhmem.cntlong-qhmem.freelong);
    +}
    +
    +void qh_memfreeshort(int *curlong, int *totlong) {
    +  *totlong= qhmem.totlong;
    +  *curlong= qhmem.cntlong - qhmem.freelong;
    +  memset((char *)&qhmem, 0, sizeof(qhmem));  /* every field is 0, FALSE, NULL */
    +}
    +
    +void qh_meminit(FILE *ferr) {
    +
    +  memset((char *)&qhmem, 0, sizeof(qhmem));  /* every field is 0, FALSE, NULL */
    +  if (ferr)
    +      qhmem.ferr= ferr;
    +  else
    +      qhmem.ferr= stderr;
    +  if (sizeof(void*) < sizeof(int)) {
    +    qh_fprintf(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(qhmem_ERRqhull, NULL, NULL);
    +  }
    +}
    +
    +void qh_meminitbuffers(int tracelevel, int alignment, int numsizes, int bufsize, int bufinit) {
    +
    +  qhmem.IStracing= tracelevel;
    +}
    +
    +void qh_memsetup(void) {
    +
    +}
    +
    +void qh_memsize(int size) {
    +
    +}
    +
    +void qh_memstatistics(FILE *fp) {
    +
    +  qh_fprintf(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",
    +           qhmem.cntlong,
    +           qhmem.freelong,
    +           qhmem.maxlong, qhmem.totlong, qhmem.cntlong - qhmem.freelong);
    +}
    +
    +#endif /* qh_NOmem */
    +
    +/*---------------------------------
    +
    +  qh_memtotal( 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 (UsingLibQhull.cpp)
    +*/
    +void qh_memtotal(int *totlong, int *curlong, int *totshort, int *curshort, int *maxlong, int *totbuffer) {
    +    *totlong= qhmem.totlong;
    +    *curlong= qhmem.cntlong - qhmem.freelong;
    +    *totshort= qhmem.totshort;
    +    *curshort= qhmem.cntshort + qhmem.cntquick - qhmem.freeshort;
    +    *maxlong= qhmem.maxlong;
    +    *totbuffer= qhmem.totbuffer;
    +} /* memtotlong */
    +
    diff --git a/xs/src/qhull/src/libqhull/mem.h b/xs/src/qhull/src/libqhull/mem.h
    new file mode 100644
    index 000000000..453f319df
    --- /dev/null
    +++ b/xs/src/qhull/src/libqhull/mem.h
    @@ -0,0 +1,222 @@
    +/*
      ---------------------------------
    +
    +   mem.h
    +     prototypes for memory management functions
    +
    +   see qh-mem.htm, mem.c and qset.h
    +
    +   for error handling, writes message and calls
    +     qh_errexit(qhmem_ERRmem, NULL, NULL) if insufficient memory
    +       and
    +     qh_errexit(qhmem_ERRqhull, NULL, NULL) otherwise
    +
    +   Copyright (c) 1993-2015 The Geometry Center.
    +   $Id: //main/2015/qhull/src/libqhull/mem.h#2 $$Change: 2062 $
    +   $DateTime: 2016/01/17 13:13:18 $$Author: bbarber $
    +*/
    +
    +#ifndef qhDEFmem
    +#define qhDEFmem 1
    +
    +#include 
    +
    +/*---------------------------------
    +
    +  qh_NOmem
    +    turn off quick-fit memory allocation
    +
    +  notes:
    +    mem.c implements Quickfit memory allocation for about 20% time
    +    savings.  If it fails on your machine, try to locate the
    +    problem, and send the answer to qhull@qhull.org.  If this can
    +    not be done, define qh_NOmem to use malloc/free instead.
    +
    +   #define qh_NOmem
    +*/
    +
    +/*---------------------------------
    +
    +qh_TRACEshort
    +Trace short and quick memory allocations at T5
    +
    +*/
    +#define qh_TRACEshort
    +
    +/*-------------------------------------------
    +    to avoid bus errors, memory allocation must consider alignment requirements.
    +    malloc() automatically takes care of alignment.   Since mem.c manages
    +    its own memory, we need to explicitly specify alignment in
    +    qh_meminitbuffers().
    +
    +    A safe choice is sizeof(double).  sizeof(float) may be used if doubles
    +    do not occur in data structures and pointers are the same size.  Be careful
    +    of machines (e.g., DEC Alpha) with large pointers.  If gcc is available,
    +    use __alignof__(double) or fmax_(__alignof__(float), __alignof__(void *)).
    +
    +   see qh_MEMalign in user.h for qhull's alignment
    +*/
    +
    +#define qhmem_ERRmem 4    /* matches qh_ERRmem in libqhull.h */
    +#define qhmem_ERRqhull 5  /* matches qh_ERRqhull in libqhull.h */
    +
    +/*----------------------------------
    +
    +  ptr_intT
    +    for casting a void * to an integer-type that holds a pointer
    +    Used for integer expressions (e.g., computing qh_gethash() in poly.c)
    +
    +  notes:
    +    WARN64 -- these notes indicate 64-bit issues
    +    On 64-bit machines, a pointer may be larger than an 'int'.
    +    qh_meminit()/mem.c checks that 'ptr_intT' holds a 'void*'
    +    ptr_intT is typically a signed value, but not necessarily so
    +    size_t is typically unsigned, but should match the parameter type
    +    Qhull uses int instead of size_t except for system calls such as malloc, qsort, qh_malloc, etc.
    +    This matches Qt convention and is easier to work with.
    +*/
    +#if (defined(__MINGW64__)) && defined(_WIN64)
    +typedef long long ptr_intT;
    +#elif (_MSC_VER) && defined(_WIN64)
    +typedef long long ptr_intT;
    +#else
    +typedef long ptr_intT;
    +#endif
    +
    +/*----------------------------------
    +
    +  qhmemT
    +    global memory structure for mem.c
    +
    + notes:
    +   users should ignore qhmem except for writing extensions
    +   qhmem is allocated in mem.c
    +
    +   qhmem could be swapable like qh and qhstat, but then
    +   multiple qh's and qhmem's would need to keep in synch.
    +   A swapable qhmem would also waste memory buffers.  As long
    +   as memory operations are atomic, there is no problem with
    +   multiple qh structures being active at the same time.
    +   If you need separate address spaces, you can swap the
    +   contents of qhmem.
    +*/
    +typedef struct qhmemT qhmemT;
    +extern qhmemT qhmem;
    +
    +#ifndef DEFsetT
    +#define DEFsetT 1
    +typedef struct setT setT;          /* defined in qset.h */
    +#endif
    +
    +/* Update qhmem in mem.c if add or remove fields */
    +struct qhmemT {               /* global memory management variables */
    +  int      BUFsize;           /* size of memory allocation buffer */
    +  int      BUFinit;           /* initial size of memory allocation buffer */
    +  int      TABLEsize;         /* actual number of sizes in free list table */
    +  int      NUMsizes;          /* maximum number of sizes in free list table */
    +  int      LASTsize;          /* last size in free list table */
    +  int      ALIGNmask;         /* worst-case alignment, must be 2^n-1 */
    +  void   **freelists;          /* free list table, linked by offset 0 */
    +  int     *sizetable;         /* size of each freelist */
    +  int     *indextable;        /* size->index table */
    +  void    *curbuffer;         /* current buffer, linked by offset 0 */
    +  void    *freemem;           /*   free memory in curbuffer */
    +  int      freesize;          /*   size of freemem in bytes */
    +  setT    *tempstack;         /* stack of temporary memory, managed by users */
    +  FILE    *ferr;              /* file for reporting errors when 'qh' may be undefined */
    +  int      IStracing;         /* =5 if tracing memory allocations */
    +  int      cntquick;          /* count of quick allocations */
    +                              /* Note: removing statistics doesn't effect speed */
    +  int      cntshort;          /* count of short allocations */
    +  int      cntlong;           /* count of long allocations */
    +  int      freeshort;         /* count of short memfrees */
    +  int      freelong;          /* count of long memfrees */
    +  int      totbuffer;         /* total short memory buffers minus buffer links */
    +  int      totdropped;        /* total dropped memory at end of short memory buffers (e.g., freesize) */
    +  int      totfree;           /* total size of free, short memory on freelists */
    +  int      totlong;           /* total size of long memory in use */
    +  int      maxlong;           /*   maximum totlong */
    +  int      totshort;          /* total size of short memory in use */
    +  int      totunused;         /* total unused short memory (estimated, short size - request size of first allocations) */
    +  int      cntlarger;         /* count of setlarger's */
    +  int      totlarger;         /* total copied by setlarger */
    +};
    +
    +
    +/*==================== -macros ====================*/
    +
    +/*----------------------------------
    +
    +  qh_memalloc_(insize, freelistp, object, type)
    +    returns object of size bytes
    +        assumes size<=qhmem.LASTsize and void **freelistp is a temp
    +*/
    +
    +#if defined qh_NOmem
    +#define qh_memalloc_(insize, freelistp, object, type) {\
    +  object= (type*)qh_memalloc(insize); }
    +#elif defined qh_TRACEshort
    +#define qh_memalloc_(insize, freelistp, object, type) {\
    +    freelistp= NULL; /* Avoid warnings */ \
    +    object= (type*)qh_memalloc(insize); }
    +#else /* !qh_NOmem */
    +
    +#define qh_memalloc_(insize, freelistp, object, type) {\
    +  freelistp= qhmem.freelists + qhmem.indextable[insize];\
    +  if ((object= (type*)*freelistp)) {\
    +    qhmem.totshort += qhmem.sizetable[qhmem.indextable[insize]]; \
    +    qhmem.totfree -= qhmem.sizetable[qhmem.indextable[insize]]; \
    +    qhmem.cntquick++;  \
    +    *freelistp= *((void **)*freelistp);\
    +  }else object= (type*)qh_memalloc(insize);}
    +#endif
    +
    +/*----------------------------------
    +
    +  qh_memfree_(object, insize, freelistp)
    +    free up an object
    +
    +  notes:
    +    object may be NULL
    +    assumes size<=qhmem.LASTsize and void **freelistp is a temp
    +*/
    +#if defined qh_NOmem
    +#define qh_memfree_(object, insize, freelistp) {\
    +  qh_memfree(object, insize); }
    +#elif defined qh_TRACEshort
    +#define qh_memfree_(object, insize, freelistp) {\
    +    freelistp= NULL; /* Avoid warnings */ \
    +    qh_memfree(object, insize); }
    +#else /* !qh_NOmem */
    +
    +#define qh_memfree_(object, insize, freelistp) {\
    +  if (object) { \
    +    qhmem.freeshort++;\
    +    freelistp= qhmem.freelists + qhmem.indextable[insize];\
    +    qhmem.totshort -= qhmem.sizetable[qhmem.indextable[insize]]; \
    +    qhmem.totfree += qhmem.sizetable[qhmem.indextable[insize]]; \
    +    *((void **)object)= *freelistp;\
    +    *freelistp= object;}}
    +#endif
    +
    +/*=============== prototypes in alphabetical order ============*/
    +
    +void *qh_memalloc(int insize);
    +void qh_memcheck(void);
    +void qh_memfree(void *object, int insize);
    +void qh_memfreeshort(int *curlong, int *totlong);
    +void qh_meminit(FILE *ferr);
    +void qh_meminitbuffers(int tracelevel, int alignment, int numsizes,
    +                        int bufsize, int bufinit);
    +void qh_memsetup(void);
    +void qh_memsize(int size);
    +void qh_memstatistics(FILE *fp);
    +void qh_memtotal(int *totlong, int *curlong, int *totshort, int *curshort, int *maxlong, int *totbuffer);
    +
    +#endif /* qhDEFmem */
    diff --git a/xs/src/qhull/src/libqhull/merge.c b/xs/src/qhull/src/libqhull/merge.c
    new file mode 100644
    index 000000000..22104dc03
    --- /dev/null
    +++ b/xs/src/qhull/src/libqhull/merge.c
    @@ -0,0 +1,3628 @@
    +/*
      ---------------------------------
    +
    +   merge.c
    +   merges non-convex facets
    +
    +   see qh-merge.htm and merge.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.c):
    +     qh_partitionvisible(!qh_ALL, &numoutside);  // visible_list, newfacet_list
    +     qh_deletevisible();         // qh.visible_list
    +     qh_resetlists(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/merge.c#4 $$Change: 2064 $
    +   $DateTime: 2016/01/18 12:36:08 $$Author: bbarber $
    +*/
    +
    +#include "qhull_a.h"
    +
    +#ifndef qh_NOmerge
    +
    +/*===== functions(alphabetical after premerge and postmerge) ======*/
    +
    +/*---------------------------------
    +
    +  qh_premerge( 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(vertexT *apex, realT maxcentrum, realT maxangle) {
    +  boolT othermerge= False;
    +  facetT *newfacet;
    +
    +  if (qh ZEROcentrum && qh_checkzero(!qh_ALL))
    +    return;
    +  trace2((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 centrum_radius= maxcentrum;
    +  qh cos_max= maxangle;
    +  qh degen_mergeset= qh_settemp(qh TEMPsize);
    +  qh facet_mergeset= qh_settemp(qh TEMPsize);
    +  if (qh hull_dim >=3) {
    +    qh_mark_dupridges(qh newfacet_list); /* facet_mergeset */
    +    qh_mergecycle_all(qh newfacet_list, &othermerge);
    +    qh_forcedmerges(&othermerge /* qh.facet_mergeset */);
    +    FORALLnew_facets {  /* test samecycle merges */
    +      if (!newfacet->simplicial && !newfacet->mergeridge)
    +        qh_degen_redundant_neighbors(newfacet, NULL);
    +    }
    +    if (qh_merge_degenredundant())
    +      othermerge= True;
    +  }else /* qh.hull_dim == 2 */
    +    qh_mergecycle_all(qh newfacet_list, &othermerge);
    +  qh_flippedmerges(qh newfacet_list, &othermerge);
    +  if (!qh MERGEexact || zzval_(Ztotmerge)) {
    +    zinc_(Zpremergetot);
    +    qh POSTmerging= False;
    +    qh_getmergeset_initial(qh newfacet_list);
    +    qh_all_merges(othermerge, False);
    +  }
    +  qh_settempfree(&qh facet_mergeset);
    +  qh_settempfree(&qh degen_mergeset);
    +} /* premerge */
    +
    +/*---------------------------------
    +
    +  qh_postmerge( 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(const char *reason, realT maxcentrum, realT maxangle,
    +                      boolT vneighbors) {
    +  facetT *newfacet;
    +  boolT othermerges= False;
    +  vertexT *vertex;
    +
    +  if (qh REPORTfreq || qh IStracing) {
    +    qh_buildtracing(NULL, NULL);
    +    qh_printsummary(qh ferr);
    +    if (qh PRINTstatistics)
    +      qh_printallstatistics(qh ferr, "reason");
    +    qh_fprintf(qh ferr, 8062, "\n%s with 'C%.2g' and 'A%.2g'\n",
    +        reason, maxcentrum, maxangle);
    +  }
    +  trace2((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 TEMPsize);
    +  qh facet_mergeset= qh_settemp(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(); /* was skipped during pre-merging */
    +      }
    +    }
    +    if (!qh PREmerge && !qh MERGEexact)
    +      qh_flippedmerges(qh newfacet_list, &othermerges);
    +  }
    +  qh_getmergeset_initial(qh newfacet_list);
    +  qh_all_merges(False, vneighbors);
    +  qh_settempfree(&qh facet_mergeset);
    +  qh_settempfree(&qh degen_mergeset);
    +} /* post_merge */
    +
    +/*---------------------------------
    +
    +  qh_all_merges( 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(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 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 facet_mergeset)) {
    +      while ((merge= (mergeT*)qh_setdellast(qh facet_mergeset))) {
    +        facet1= merge->facet1;
    +        facet2= merge->facet2;
    +        mergetype= merge->type;
    +        qh_memfree_(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(facet1, facet2, mergetype);
    +        numdegenredun += qh_merge_degenredundant();
    +        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();  /* otherwise large post merges too slow */
    +      }
    +      qh_getmergeset(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_getmergeset(qh newfacet_list); /* facet_mergeset */
    +          continue;
    +        }
    +      }
    +    }
    +    if (vneighbors && qh_test_vneighbors(/* qh.newfacet_list */))
    +      continue;
    +    break;
    +  } /* while (True) */
    +  if (qh CHECKfrequently && !qh MERGEexact) {
    +    qh old_randomdist= qh RANDOMdist;
    +    qh RANDOMdist= False;
    +    qh_checkconvex(qh newfacet_list, qh_ALGORITHMfault);
    +    /* qh_checkconnect(); [this is slow and it changes the facet order] */
    +    qh RANDOMdist= qh old_randomdist;
    +  }
    +  trace1((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();
    +} /* all_merges */
    +
    +
    +/*---------------------------------
    +
    +  qh_appendmergeset( 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(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_((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 facet_mergeset), merge);
    +  else if (mergetype == MRGdegen) {
    +    facet->degenerate= True;
    +    if (!(lastmerge= (mergeT*)qh_setlast(qh degen_mergeset))
    +    || lastmerge->type == MRGdegen)
    +      qh_setappend(&(qh degen_mergeset), merge);
    +    else
    +      qh_setaddnth(&(qh degen_mergeset), 0, merge);
    +  }else if (mergetype == MRGredundant) {
    +    facet->redundant= True;
    +    qh_setappend(&(qh degen_mergeset), merge);
    +  }else /* mergetype == MRGmirror */ {
    +    if (facet->redundant || neighbor->redundant) {
    +      qh_fprintf(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_ERRqhull, facet, neighbor);
    +    }
    +    if (!qh_setequal(facet->vertices, neighbor->vertices)) {
    +      qh_fprintf(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_ERRqhull, facet, neighbor);
    +    }
    +    facet->redundant= True;
    +    neighbor->redundant= True;
    +    qh_setappend(&(qh degen_mergeset), merge);
    +  }
    +} /* appendmergeset */
    +
    +
    +/*---------------------------------
    +
    +  qh_basevertices( 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(facetT *samecycle) {
    +  facetT *same;
    +  vertexT *apex, *vertex, **vertexp;
    +  setT *vertices= qh_settemp(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(&vertices, vertex);
    +        vertex->visitid= qh vertex_visit;
    +        vertex->seen= False;
    +      }
    +    }
    +  }
    +  trace4((qh ferr, 4019, "qh_basevertices: found %d vertices\n",
    +         qh_setsize(vertices)));
    +  return vertices;
    +} /* basevertices */
    +
    +/*---------------------------------
    +
    +  qh_checkconnect()
    +    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(void /* qh.newfacet_list */) {
    +  facetT *facet, *newfacet, *errfacet= NULL, *neighbor, **neighborp;
    +
    +  facet= qh newfacet_list;
    +  qh_removefacet(facet);
    +  qh_appendfacet(facet);
    +  facet->visitid= ++qh visit_id;
    +  FORALLfacet_(facet) {
    +    FOREACHneighbor_(facet) {
    +      if (neighbor->visitid != qh visit_id) {
    +        qh_removefacet(neighbor);
    +        qh_appendfacet(neighbor);
    +        neighbor->visitid= qh visit_id;
    +      }
    +    }
    +  }
    +  FORALLnew_facets {
    +    if (newfacet->visitid == qh visit_id)
    +      break;
    +    qh_fprintf(qh ferr, 6094, "qhull error: f%d is not attached to the new facets\n",
    +         newfacet->id);
    +    errfacet= newfacet;
    +  }
    +  if (errfacet)
    +    qh_errexit(qh_ERRqhull, errfacet, NULL);
    +} /* checkconnect */
    +
    +/*---------------------------------
    +
    +  qh_checkzero( 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(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 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(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(vertex->point, facet, &dist);
    +          if (dist >= -qh DISTround) {
    +            qh ZEROall_ok= False;
    +            if (!qh MERGEexact || dist > qh DISTround)
    +              goto LABELnonconvex;
    +          }
    +          break;
    +        }
    +      }
    +    }
    +  }
    +  trace2((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 ferr, 2013, "qh_checkzero: facet f%d needs pre-merging\n",
    +       facet->id));
    +  return False;
    +
    + LABELnonconvex:
    +  trace2((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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  qh_copynonconvex( 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(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 ferr, 4020, "qh_copynonconvex: moved nonconvex flag from r%d to r%d\n",
    +              atridge->id, ridge->id));
    +      break;
    +    }
    +  }
    +} /* copynonconvex */
    +
    +/*---------------------------------
    +
    +  qh_degen_redundant_facet( 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(facetT *facet) {
    +  vertexT *vertex, **vertexp;
    +  facetT *neighbor, **neighborp;
    +
    +  trace4((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(facet, neighbor, MRGredundant, NULL);
    +      trace2((qh ferr, 2015, "qh_degen_redundant_facet: f%d is contained in f%d.  merge\n", facet->id, neighbor->id));
    +      return;
    +    }
    +  }
    +  if (qh_setsize(facet->neighbors) < qh hull_dim) {
    +    qh_appendmergeset(facet, facet, MRGdegen, NULL);
    +    trace2((qh ferr, 2016, "qh_degen_redundant_neighbors: f%d is degenerate.\n", facet->id));
    +  }
    +} /* degen_redundant_facet */
    +
    +
    +/*---------------------------------
    +
    +  qh_degen_redundant_neighbors( 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(facetT *facet, facetT *delfacet) {
    +  vertexT *vertex, **vertexp;
    +  facetT *neighbor, **neighborp;
    +  int size;
    +
    +  trace4((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(facet->neighbors)) < qh hull_dim) {
    +    qh_appendmergeset(facet, facet, MRGdegen, NULL);
    +    trace2((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(neighbor, facet, MRGredundant, NULL);
    +      trace2((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(neighbor->neighbors)) < qh hull_dim) {
    +      qh_appendmergeset(neighbor, neighbor, MRGdegen, NULL);
    +      trace2((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 */
    +
    +
    +/*---------------------------------
    +
    +  qh_find_newvertex( 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(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 ferr, 8063, "qh_find_newvertex: find new vertex for v%d from ",
    +             oldvertex->id);
    +    FOREACHvertex_(vertices)
    +      qh_fprintf(qh ferr, 8064, "v%d ", vertex->id);
    +    FOREACHridge_(ridges)
    +      qh_fprintf(qh ferr, 8065, "r%d ", ridge->id);
    +    qh_fprintf(qh ferr, 8066, "\n");
    +  }
    +#endif
    +  FOREACHvertex_(vertices)
    +    vertex->visitid= 0;
    +  FOREACHridge_(ridges) {
    +    FOREACHvertex_(ridge->vertices)
    +      vertex->visitid++;
    +  }
    +  FOREACHvertex_(vertices) {
    +    if (!vertex->visitid) {
    +      qh_setdelnth(vertices, SETindex_(vertices,vertex));
    +      vertexp--; /* repeat since deleted this vertex */
    +    }
    +  }
    +  qh vertex_visit += (unsigned int)qh_setsize(ridges);
    +  if (!qh_setsize(vertices)) {
    +    trace4((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(vertices),
    +                sizeof(vertexT *), qh_comparevisit);
    +  /* can now use qh vertex_visit */
    +  if (qh PRINTstatistics) {
    +    size= qh_setsize(vertices);
    +    zinc_(Zintersect);
    +    zadd_(Zintersecttot, size);
    +    zmax_(Zintersectmax, size);
    +  }
    +  hashsize= qh_newhashtable(qh_setsize(ridges));
    +  FOREACHridge_(ridges)
    +    qh_hashridge(qh hash_table, hashsize, ridge, oldvertex);
    +  FOREACHvertex_(vertices) {
    +    newridges= qh_vertexridges(vertex);
    +    FOREACHridge_(newridges) {
    +      if (qh_hashridge_find(qh hash_table, hashsize, ridge, vertex, oldvertex, &hash)) {
    +        zinc_(Zdupridge);
    +        break;
    +      }
    +    }
    +    qh_settempfree(&newridges);
    +    if (!ridge)
    +      break;  /* found a rename */
    +  }
    +  if (vertex) {
    +    /* counted in qh_renamevertex */
    +    trace2((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(vertices), qh_setsize(ridges)));
    +  }else {
    +    zinc_(Zfindfail);
    +    trace0((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 hash_table);
    +  return vertex;
    +} /* find_newvertex */
    +
    +/*---------------------------------
    +
    +  qh_findbest_test( 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(boolT testcentrum, facetT *facet, facetT *neighbor,
    +      facetT **bestfacet, realT *distp, realT *mindistp, realT *maxdistp) {
    +  realT dist, mindist, maxdist;
    +
    +  if (testcentrum) {
    +    zzinc_(Zbestdist);
    +    qh_distplane(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(facet, neighbor, &mindist, &maxdist);
    +  if (dist < *distp) {
    +    *bestfacet= neighbor;
    +    *mindistp= mindist;
    +    *maxdistp= maxdist;
    +    *distp= dist;
    +  }
    +} /* findbest_test */
    +
    +/*---------------------------------
    +
    +  qh_findbestneighbor( 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(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(facet->vertices);
    +
    +  if(qh CENTERtype==qh_ASvoronoi){
    +    qh_fprintf(qh ferr, 6272, "qhull error: cannot call qh_findbestneighor for f%d while qh.CENTERtype is qh_ASvoronoi\n", facet->id);
    +    qh_errexit(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(facet);
    +  }
    +  if (size > qh hull_dim + qh_BESTnonconvex) {
    +    FOREACHridge_(facet->ridges) {
    +      if (ridge->nonconvex) {
    +        neighbor= otherfacet_(ridge, facet);
    +        qh_findbest_test(testcentrum, facet, neighbor,
    +                          &bestfacet, distp, mindistp, maxdistp);
    +      }
    +    }
    +  }
    +  if (!bestfacet) {
    +    nonconvex= False;
    +    FOREACHneighbor_(facet)
    +      qh_findbest_test(testcentrum, facet, neighbor,
    +                        &bestfacet, distp, mindistp, maxdistp);
    +  }
    +  if (!bestfacet) {
    +    qh_fprintf(qh ferr, 6095, "qhull internal error (qh_findbestneighbor): no neighbors for f%d\n", facet->id);
    +
    +    qh_errexit(qh_ERRqhull, facet, NULL);
    +  }
    +  if (testcentrum)
    +    qh_getdistance(facet, bestfacet, mindistp, maxdistp);
    +  trace3((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 */
    +
    +
    +/*---------------------------------
    +
    +  qh_flippedmerges( 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(facetT *facetlist, boolT *wasmerge) {
    +  facetT *facet, *neighbor, *facet1;
    +  realT dist, mindist, maxdist;
    +  mergeT *merge, **mergep;
    +  setT *othermerges;
    +  int nummerge=0;
    +
    +  trace4((qh ferr, 4024, "qh_flippedmerges: begin\n"));
    +  FORALLfacet_(facetlist) {
    +    if (facet->flipped && !facet->visible)
    +      qh_appendmergeset(facet, facet, MRGflip, NULL);
    +  }
    +  othermerges= qh_settemppop(); /* was facet_mergeset */
    +  qh facet_mergeset= qh_settemp(qh TEMPsize);
    +  qh_settemppush(othermerges);
    +  FOREACHmerge_(othermerges) {
    +    facet1= merge->facet1;
    +    if (merge->type != MRGflip || facet1->visible)
    +      continue;
    +    if (qh TRACEmerge-1 == zzval_(Ztotmerge))
    +      qhmem.IStracing= qh IStracing= qh TRACElevel;
    +    neighbor= qh_findbestneighbor(facet1, &dist, &mindist, &maxdist);
    +    trace0((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(facet1, neighbor, &mindist, &maxdist, !qh_MERGEapex);
    +    nummerge++;
    +    if (qh PRINTstatistics) {
    +      zinc_(Zflipped);
    +      wadd_(Wflippedtot, dist);
    +      wmax_(Wflippedmax, dist);
    +    }
    +    qh_merge_degenredundant();
    +  }
    +  FOREACHmerge_(othermerges) {
    +    if (merge->facet1->visible || merge->facet2->visible)
    +      qh_memfree(merge, (int)sizeof(mergeT));
    +    else
    +      qh_setappend(&qh facet_mergeset, merge);
    +  }
    +  qh_settempfree(&othermerges);
    +  if (nummerge)
    +    *wasmerge= True;
    +  trace1((qh ferr, 1010, "qh_flippedmerges: merged %d flipped facets into a good neighbor\n", nummerge));
    +} /* flippedmerges */
    +
    +
    +/*---------------------------------
    +
    +  qh_forcedmerges( 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(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))
    +    qhmem.IStracing= qh IStracing= qh TRACElevel;
    +  trace4((qh ferr, 4025, "qh_forcedmerges: begin\n"));
    +  othermerges= qh_settemppop(); /* was facet_mergeset */
    +  qh facet_mergeset= qh_settemp(qh TEMPsize);
    +  qh_settemppush(othermerges);
    +  FOREACHmerge_(othermerges) {
    +    if (merge->type != MRGridge)
    +        continue;
    +    if (qh TRACEmerge-1 == zzval_(Ztotmerge))
    +        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 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_ERRqhull, facet1, facet2);
    +    }
    +    dist1= qh_getdistance(facet1, facet2, &mindist1, &maxdist1);
    +    dist2= qh_getdistance(facet2, facet1, &mindist2, &maxdist2);
    +    qh_check_dupridge(facet1, dist1, facet2, dist2);
    +    if (dist1 < dist2)
    +      qh_mergefacet(facet1, facet2, &mindist1, &maxdist1, !qh_MERGEapex);
    +    else {
    +      qh_mergefacet(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(merge, (int)sizeof(mergeT));
    +    else
    +      qh_setappend(&qh facet_mergeset, merge);
    +  }
    +  qh_settempfree(&othermerges);
    +  if (nummerge)
    +    *wasmerge= True;
    +  trace1((qh ferr, 1011, "qh_forcedmerges: merged %d facets and %d flipped facets across duplicated ridges\n",
    +                nummerge, numflip));
    +} /* forcedmerges */
    +
    +
    +/*---------------------------------
    +
    +  qh_getmergeset( 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(facetT *facetlist) {
    +  facetT *facet, *neighbor, **neighborp;
    +  ridgeT *ridge, **ridgep;
    +  int nummerges;
    +
    +  nummerges= qh_setsize(qh facet_mergeset);
    +  trace4((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(facet, neighbor))
    +          ridge->nonconvex= True;
    +      }
    +    }
    +  }
    +  nummerges= qh_setsize(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 ferr, 2021, "qh_getmergeset: %d merges found\n", nummerges));
    +} /* getmergeset */
    +
    +
    +/*---------------------------------
    +
    +  qh_getmergeset_initial( 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(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(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 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 ferr, 2022, "qh_getmergeset_initial: %d merges found\n", nummerges));
    +} /* getmergeset_initial */
    +
    +
    +/*---------------------------------
    +
    +  qh_hashridge( 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(setT *hashtable, int hashsize, ridgeT *ridge, vertexT *oldvertex) {
    +  int hash;
    +  ridgeT *ridgeA;
    +
    +  hash= qh_gethash(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 */
    +
    +
    +/*---------------------------------
    +
    +  qh_hashridge_find( 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(setT *hashtable, int hashsize, ridgeT *ridge,
    +              vertexT *vertex, vertexT *oldvertex, int *hashslot) {
    +  int hash;
    +  ridgeT *ridgeA;
    +
    +  *hashslot= 0;
    +  zinc_(Zhashridge);
    +  hash= qh_gethash(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 */
    +
    +
    +/*---------------------------------
    +
    +  qh_makeridges( 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(facetT *facet) {
    +  facetT *neighbor, **neighborp;
    +  ridgeT *ridge, **ridgep;
    +  int neighbor_i, neighbor_n;
    +  boolT toporient, mergeridge= False;
    +
    +  if (!facet->simplicial)
    +    return;
    +  trace4((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_(facet) {
    +    if (neighbor == qh_MERGEridge)
    +      continue;  /* fixed by qh_mark_dupridges */
    +    else if (!neighbor->seen) {  /* no current ridges */
    +      ridge= qh_newridge();
    +      ridge->vertices= qh_setnew_delnthsorted(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(&(facet->ridges), ridge);
    +      qh_setappend(&(neighbor->ridges), ridge);
    +    }
    +  }
    +  if (mergeridge) {
    +    while (qh_setdel(facet->neighbors, qh_MERGEridge))
    +      ; /* delete each one */
    +  }
    +} /* makeridges */
    +
    +
    +/*---------------------------------
    +
    +  qh_mark_dupridges( 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(facetT *facetlist) {
    +  facetT *facet, *neighbor, **neighborp;
    +  int nummerge=0;
    +  mergeT *merge, **mergep;
    +
    +
    +  trace4((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(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(facet);
    +  }
    +  FOREACHmerge_(qh facet_mergeset) {   /* restore the missing neighbors */
    +    if (merge->type == MRGridge) {
    +      qh_setappend(&merge->facet2->neighbors, merge->facet1);
    +      qh_makeridges(merge->facet1);   /* and the missing ridges */
    +    }
    +  }
    +  trace1((qh ferr, 1012, "qh_mark_dupridges: found %d duplicated ridges\n",
    +                nummerge));
    +} /* mark_dupridges */
    +
    +/*---------------------------------
    +
    +  qh_maydropneighbor( 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(facetT *facet) {
    +  ridgeT *ridge, **ridgep;
    +  realT angledegen= qh_ANGLEdegen;
    +  facetT *neighbor, **neighborp;
    +
    +  qh visit_id++;
    +  trace4((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 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(neighbor->neighbors) < qh hull_dim) {
    +        zinc_(Zdropdegen);
    +        qh_appendmergeset(neighbor, neighbor, MRGdegen, &angledegen);
    +        trace2((qh ferr, 2023, "qh_maydropneighbors: f%d is degenerate.\n", neighbor->id));
    +      }
    +    }
    +  }
    +  if (qh_setsize(facet->neighbors) < qh hull_dim) {
    +    zinc_(Zdropdegen);
    +    qh_appendmergeset(facet, facet, MRGdegen, &angledegen);
    +    trace2((qh ferr, 2024, "qh_maydropneighbors: f%d is degenerate.\n", facet->id));
    +  }
    +} /* maydropneighbor */
    +
    +
    +/*---------------------------------
    +
    +  qh_merge_degenredundant()
    +    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(void) {
    +  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(merge, (int)sizeof(mergeT));
    +    if (facet1->visible)
    +      continue;
    +    facet1->degenerate= False;
    +    facet1->redundant= False;
    +    if (qh TRACEmerge-1 == zzval_(Ztotmerge))
    +      qhmem.IStracing= qh IStracing= qh TRACElevel;
    +    if (mergetype == MRGredundant) {
    +      zinc_(Zneighbor);
    +      while (facet2->visible) {
    +        if (!facet2->f.replace) {
    +          qh_fprintf(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_ERRqhull, facet1, facet2);
    +        }
    +        facet2= facet2->f.replace;
    +      }
    +      if (facet1 == facet2) {
    +        qh_degen_redundant_facet(facet1); /* in case of others */
    +        continue;
    +      }
    +      trace2((qh ferr, 2025, "qh_merge_degenredundant: facet f%d is contained in f%d, will merge\n",
    +            facet1->id, facet2->id));
    +      qh_mergefacet(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(facet1->neighbors))) {
    +        zinc_(Zdelfacetdup);
    +        trace2((qh ferr, 2026, "qh_merge_degenredundant: facet f%d has no neighbors.  Deleted\n", facet1->id));
    +        qh_willdelete(facet1, NULL);
    +        FOREACHvertex_(facet1->vertices) {
    +          qh_setdel(vertex->neighbors, facet1);
    +          if (!SETfirst_(vertex->neighbors)) {
    +            zinc_(Zdegenvertex);
    +            trace2((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 del_vertices, vertex);
    +          }
    +        }
    +        nummerges++;
    +      }else if (size < qh hull_dim) {
    +        bestneighbor= qh_findbestneighbor(facet1, &dist, &mindist, &maxdist);
    +        trace2((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(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 */
    +
    +/*---------------------------------
    +
    +  qh_merge_nonconvex( 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(facetT *facet1, facetT *facet2, mergeType mergetype) {
    +  facetT *bestfacet, *bestneighbor, *neighbor;
    +  realT dist, dist2, mindist, mindist2, maxdist, maxdist2;
    +
    +  if (qh TRACEmerge-1 == zzval_(Ztotmerge))
    +    qhmem.IStracing= qh IStracing= qh TRACElevel;
    +  trace3((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(bestfacet, &dist, &mindist, &maxdist);
    +  neighbor= qh_findbestneighbor(facet2, &dist2, &mindist2, &maxdist2);
    +  if (dist < dist2) {
    +    qh_mergefacet(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 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(bestfacet, bestneighbor, &mindist, &maxdist, !qh_MERGEapex);
    +  }else {
    +    qh_mergefacet(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 */
    +
    +/*---------------------------------
    +
    +  qh_mergecycle( 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(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 ferr, 6224, "Qhull internal error (qh_mergecycle): does not work for tricoplanar facets.  Use option 'Q11'\n");
    +      qh_errexit(qh_ERRqhull, newfacet, NULL);
    +    }
    +    newfacet->tricoplanar= False;
    +    newfacet->keepcentrum= False;
    +  }
    +  if (!qh VERTEXneighbors)
    +    qh_vertexneighbors();
    +  zzinc_(Ztotmerge);
    +  if (qh REPORTfreq2 && qh POSTmerging) {
    +    if (zzval_(Ztotmerge) > qh mergereport + qh REPORTfreq2)
    +      qh_tracemerging();
    +  }
    +#ifndef qh_NOtrace
    +  if (qh TRACEmerge == zzval_(Ztotmerge))
    +    qhmem.IStracing= qh IStracing= qh TRACElevel;
    +  trace2((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 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 ferr, 8069, "  same cycle:");
    +    FORALLsame_cycle_(samecycle)
    +      qh_fprintf(qh ferr, 8070, " f%d", same->id);
    +    qh_fprintf(qh ferr, 8071, "\n");
    +  }
    +  if (qh IStracing >=4)
    +    qh_errprint("MERGING CYCLE", samecycle, newfacet, NULL, NULL);
    +#endif /* !qh_NOtrace */
    +  apex= SETfirstt_(samecycle->vertices, vertexT);
    +  qh_makeridges(newfacet);
    +  qh_mergecycle_neighbors(samecycle, newfacet);
    +  qh_mergecycle_ridges(samecycle, newfacet);
    +  qh_mergecycle_vneighbors(samecycle, newfacet);
    +  if (SETfirstt_(newfacet->vertices, vertexT) != apex)
    +    qh_setaddnth(&newfacet->vertices, 0, apex);  /* apex has last id */
    +  if (!newfacet->newfacet)
    +    qh_newvertices(newfacet->vertices);
    +  qh_mergecycle_facets(samecycle, newfacet);
    +  qh_tracemerge(samecycle, newfacet);
    +  /* check for degen_redundant_neighbors after qh_forcedmerges() */
    +  if (traceonce) {
    +    qh_fprintf(qh ferr, 8072, "qh_mergecycle: end of trace facet\n");
    +    qh IStracing= tracerestore;
    +  }
    +} /* mergecycle */
    +
    +/*---------------------------------
    +
    +  qh_mergecycle_all( 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(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 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 ferr, 6225, "Qhull internal error (qh_mergecycle_all): f%d without normal\n", facet->id);
    +      qh_errexit(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(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(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(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 ferr, 1013, "qh_mergecycle_all: merged %d same cycles or facets into coplanar horizons\n", cycles));
    +} /* mergecycle_all */
    +
    +/*---------------------------------
    +
    +  qh_mergecycle_facets( 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(facetT *samecycle, facetT *newfacet) {
    +  facetT *same, *next;
    +
    +  trace4((qh ferr, 4030, "qh_mergecycle_facets: make newfacet new and samecycle deleted\n"));
    +  qh_removefacet(newfacet);  /* append as a newfacet to end of qh facet_list */
    +  qh_appendfacet(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(same, newfacet);
    +  }
    +  if (newfacet->center
    +      && qh_setsize(newfacet->vertices) <= qh hull_dim + qh_MAXnewcentrum) {
    +    qh_memfree(newfacet->center, qh normal_size);
    +    newfacet->center= NULL;
    +  }
    +  trace3((qh ferr, 3004, "qh_mergecycle_facets: merged facets from cycle f%d into f%d\n",
    +             samecycle->id, newfacet->id));
    +} /* mergecycle_facets */
    +
    +/*---------------------------------
    +
    +  qh_mergecycle_neighbors( 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(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(samecycle);
    +    same->visitid= samevisitid;
    +  }
    +  newfacet->visitid= ++qh visit_id;
    +  trace4((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(newfacet->neighbors);
    +
    +  trace4((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(&newfacet->neighbors, neighbor);
    +          qh_setreplace(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(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(&neighbor->neighbors, newfacet);
    +          qh_setappend(&newfacet->neighbors, neighbor);
    +          neighbor->visitid= qh visit_id;
    +          newneighbors++;
    +        }
    +      }
    +    }
    +  }
    +  trace2((qh ferr, 2032, "qh_mergecycle_neighbors: deleted %d neighbors and added %d\n",
    +             delneighbors, newneighbors));
    +} /* mergecycle_neighbors */
    +
    +/*---------------------------------
    +
    +  qh_mergecycle_ridges( 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(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 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(newfacet->ridges);
    +
    +  trace4((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(&newfacet->ridges, ridge);
    +        numold++;  /* already set by qh_mergecycle_neighbors */
    +        continue;
    +      }else {
    +        qh_fprintf(qh ferr, 6098, "qhull internal error (qh_mergecycle_ridges): bad ridge r%d\n", ridge->id);
    +        qh_errexit(qh_ERRqhull, NULL, ridge);
    +      }
    +      if (neighbor == newfacet) {
    +        qh_setfree(&(ridge->vertices));
    +        qh_memfree_(ridge, (int)sizeof(ridgeT), freelistp);
    +        numold++;
    +      }else if (neighbor->visitid == samevisitid) {
    +        qh_setdel(neighbor->ridges, ridge);
    +        qh_setfree(&(ridge->vertices));
    +        qh_memfree_(ridge, (int)sizeof(ridgeT), freelistp);
    +        numold++;
    +      }else {
    +        qh_setappend(&newfacet->ridges, ridge);
    +        numold++;
    +      }
    +    }
    +    if (same->ridges)
    +      qh_settruncate(same->ridges, 0);
    +    if (!same->simplicial)
    +      continue;
    +    FOREACHneighbor_i_(same) {       /* note: !newfact->simplicial */
    +      if (neighbor->visitid != samevisitid && neighbor->simplicial) {
    +        ridge= qh_newridge();
    +        ridge->vertices= qh_setnew_delnthsorted(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(&(newfacet->ridges), ridge);
    +        qh_setappend(&(neighbor->ridges), ridge);
    +        numnew++;
    +      }
    +    }
    +  }
    +
    +  trace2((qh ferr, 2033, "qh_mergecycle_ridges: found %d old ridges and %d new ones\n",
    +             numold, numnew));
    +} /* mergecycle_ridges */
    +
    +/*---------------------------------
    +
    +  qh_mergecycle_vneighbors( 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(facetT *samecycle, facetT *newfacet) {
    +  facetT *neighbor, **neighborp;
    +  unsigned int mergeid;
    +  vertexT *vertex, **vertexp, *apex;
    +  setT *vertices;
    +
    +  trace4((qh ferr, 4035, "qh_mergecycle_vneighbors: update vertex neighbors for newfacet\n"));
    +  mergeid= qh visit_id - 1;
    +  newfacet->visitid= mergeid;
    +  vertices= qh_basevertices(samecycle); /* temp */
    +  apex= SETfirstt_(samecycle->vertices, vertexT);
    +  qh_setappend(&vertices, apex);
    +  FOREACHvertex_(vertices) {
    +    vertex->delridge= True;
    +    FOREACHneighbor_(vertex) {
    +      if (neighbor->visitid == mergeid)
    +        SETref_(neighbor)= NULL;
    +    }
    +    qh_setcompact(vertex->neighbors);
    +    qh_setappend(&vertex->neighbors, newfacet);
    +    if (!SETsecond_(vertex->neighbors)) {
    +      zinc_(Zcyclevertex);
    +      trace2((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 del_vertices, vertex);
    +    }
    +  }
    +  qh_settempfree(&vertices);
    +  trace3((qh ferr, 3005, "qh_mergecycle_vneighbors: merged vertices from cycle f%d into f%d\n",
    +             samecycle->id, newfacet->id));
    +} /* mergecycle_vneighbors */
    +
    +/*---------------------------------
    +
    +  qh_mergefacet( 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(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 ferr, 6226, "Qhull internal error (qh_mergefacet): does not work for tricoplanar facets.  Use option 'Q11'\n");
    +      qh_errexit2(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();
    +  }
    +#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 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 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 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 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_ERRqhull, facet1, facet2);
    +  }
    +  if (qh num_facets - qh num_visible <= qh hull_dim + 1) {
    +    qh_fprintf(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 ferr, 8079, "Option 'Qx' may avoid this problem.\n");
    +    qh_errexit(qh_ERRprec, NULL, NULL);
    +  }
    +  if (!qh VERTEXneighbors)
    +    qh_vertexneighbors();
    +  qh_makeridges(facet1);
    +  qh_makeridges(facet2);
    +  if (qh IStracing >=4)
    +    qh_errprint("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(facet1, facet2);
    +  if (qh hull_dim > 2 && qh_setsize(facet1->vertices) == qh hull_dim)
    +    qh_mergesimplex(facet1, facet2, mergeapex);
    +  else {
    +    qh vertex_visit++;
    +    FOREACHvertex_(facet2->vertices)
    +      vertex->visitid= qh vertex_visit;
    +    if (qh hull_dim == 2)
    +      qh_mergefacet2d(facet1, facet2);
    +    else {
    +      qh_mergeneighbors(facet1, facet2);
    +      qh_mergevertices(facet1->vertices, &facet2->vertices);
    +    }
    +    qh_mergeridges(facet1, facet2);
    +    qh_mergevertex_neighbors(facet1, facet2);
    +    if (!facet2->newfacet)
    +      qh_newvertices(facet2->vertices);
    +  }
    +  if (!mergeapex)
    +    qh_degen_redundant_neighbors(facet2, facet1);
    +  if (facet2->coplanar || !facet2->newfacet) {
    +    zinc_(Zmergeintohorizon);
    +  }else if (!facet1->newfacet && facet2->newfacet) {
    +    zinc_(Zmergehorizon);
    +  }else {
    +    zinc_(Zmergenew);
    +  }
    +  qh_willdelete(facet1, facet2);
    +  qh_removefacet(facet2);  /* append as a newfacet to end of qh facet_list */
    +  qh_appendfacet(facet2);
    +  facet2->newfacet= True;
    +  facet2->tested= False;
    +  qh_tracemerge(facet1, facet2);
    +  if (traceonce) {
    +    qh_fprintf(qh ferr, 8080, "qh_mergefacet: end of wide tracing\n");
    +    qh IStracing= tracerestore;
    +  }
    +} /* mergefacet */
    +
    +
    +/*---------------------------------
    +
    +  qh_mergefacet2d( 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(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(neighborB);
    +  qh_setreplace(neighborB->neighbors, facet1, facet2);
    +  trace4((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 */
    +
    +
    +/*---------------------------------
    +
    +  qh_mergeneighbors( 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(facetT *facet1, facetT *facet2) {
    +  facetT *neighbor, **neighborp;
    +
    +  trace4((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(neighbor);
    +      if (SETfirstt_(neighbor->neighbors, facetT) != facet1) /*keep newfacet->horizon*/
    +        qh_setdel(neighbor->neighbors, facet1);
    +      else {
    +        qh_setdel(neighbor->neighbors, facet2);
    +        qh_setreplace(neighbor->neighbors, facet1, facet2);
    +      }
    +    }else if (neighbor != facet2) {
    +      qh_setappend(&(facet2->neighbors), neighbor);
    +      qh_setreplace(neighbor->neighbors, facet1, facet2);
    +    }
    +  }
    +  qh_setdel(facet1->neighbors, facet2);  /* here for makeridges */
    +  qh_setdel(facet2->neighbors, facet1);
    +} /* mergeneighbors */
    +
    +
    +/*---------------------------------
    +
    +  qh_mergeridges( 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(facetT *facet1, facetT *facet2) {
    +  ridgeT *ridge, **ridgep;
    +  vertexT *vertex, **vertexp;
    +
    +  trace4((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(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(&(facet2->ridges), ridge);
    +  }
    +} /* mergeridges */
    +
    +
    +/*---------------------------------
    +
    +  qh_mergesimplex( 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(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(facet2->vertices);  /* apex is new */
    +    apex= SETfirstt_(facet1->vertices, vertexT);
    +    if (SETfirstt_(facet2->vertices, vertexT) != apex)
    +      qh_setaddnth(&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 ferr, 4039, "qh_mergesimplex: merge apex v%d of f%d into facet f%d\n",
    +          apex->id, facet1->id, facet2->id));
    +    FOREACHvertex_i_(facet2->vertices) {
    +      if (vertex->id < apex->id) {
    +        break;
    +      }else if (vertex->id == apex->id) {
    +        issubset= True;
    +        break;
    +      }
    +    }
    +    if (!issubset)
    +      qh_setaddnth(&facet2->vertices, vertex_i, apex);
    +    if (!facet2->newfacet)
    +      qh_newvertices(facet2->vertices);
    +    else if (!apex->newlist) {
    +      qh_removevertex(apex);
    +      qh_appendvertex(apex);
    +    }
    +  }
    +  trace4((qh ferr, 4040, "qh_mergesimplex: update vertex neighbors of f%d\n",
    +          facet1->id));
    +  FOREACHvertex_(facet1->vertices) {
    +    if (vertex == apex && !issubset)
    +      qh_setreplace(vertex->neighbors, facet1, facet2);
    +    else {
    +      qh_setdel(vertex->neighbors, facet1);
    +      if (!SETsecond_(vertex->neighbors))
    +        qh_mergevertex_del(vertex, facet1, facet2);
    +    }
    +  }
    +  trace4((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(&(ridge->vertices));
    +      qh_memfree(ridge, (int)sizeof(ridgeT));
    +      qh_setdel(facet2->neighbors, facet1);
    +    }else {
    +      qh_setappend(&facet2->ridges, ridge);
    +      if (otherfacet->visitid != qh visit_id) {
    +        qh_setappend(&facet2->neighbors, otherfacet);
    +        qh_setreplace(otherfacet->neighbors, facet1, facet2);
    +        otherfacet->visitid= qh visit_id;
    +      }else {
    +        if (otherfacet->simplicial)    /* is degen, needs ridges */
    +          qh_makeridges(otherfacet);
    +        if (SETfirstt_(otherfacet->neighbors, facetT) != facet1)
    +          qh_setdel(otherfacet->neighbors, facet1);
    +        else {   /*keep newfacet->neighbors->horizon*/
    +          qh_setdel(otherfacet->neighbors, facet2);
    +          qh_setreplace(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 ferr, 3006, "qh_mergesimplex: merged simplex f%d apex v%d into facet f%d\n",
    +          facet1->id, getid_(apex), facet2->id));
    +} /* mergesimplex */
    +
    +/*---------------------------------
    +
    +  qh_mergevertex_del( 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(vertexT *vertex, facetT *facet1, facetT *facet2) {
    +
    +  zinc_(Zmergevertex);
    +  trace2((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 del_vertices, vertex);
    +} /* mergevertex_del */
    +
    +/*---------------------------------
    +
    +  qh_mergevertex_neighbors( 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(facetT *facet1, facetT *facet2) {
    +  vertexT *vertex, **vertexp;
    +
    +  trace4((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 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("TRACE", NULL, NULL, NULL, qh tracevertex);
    +  }
    +  FOREACHvertex_(facet1->vertices) {
    +    if (vertex->visitid != qh vertex_visit)
    +      qh_setreplace(vertex->neighbors, facet1, facet2);
    +    else {
    +      qh_setdel(vertex->neighbors, facet1);
    +      if (!SETsecond_(vertex->neighbors))
    +        qh_mergevertex_del(vertex, facet1, facet2);
    +    }
    +  }
    +  if (qh tracevertex)
    +    qh_errprint("TRACE", NULL, NULL, NULL, qh tracevertex);
    +} /* mergevertex_neighbors */
    +
    +
    +/*---------------------------------
    +
    +  qh_mergevertices( 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(setT *vertices1, setT **vertices2) {
    +  int newsize= qh_setsize(vertices1)+qh_setsize(*vertices2) - qh hull_dim + 1;
    +  setT *mergedvertices;
    +  vertexT *vertex, **vertexp, **vertex2= SETaddr_(*vertices2, vertexT);
    +
    +  mergedvertices= qh_settemp(newsize);
    +  FOREACHvertex_(vertices1) {
    +    if (!*vertex2 || vertex->id > (*vertex2)->id)
    +      qh_setappend(&mergedvertices, vertex);
    +    else {
    +      while (*vertex2 && (*vertex2)->id > vertex->id)
    +        qh_setappend(&mergedvertices, *vertex2++);
    +      if (!*vertex2 || (*vertex2)->id < vertex->id)
    +        qh_setappend(&mergedvertices, vertex);
    +      else
    +        qh_setappend(&mergedvertices, *vertex2++);
    +    }
    +  }
    +  while (*vertex2)
    +    qh_setappend(&mergedvertices, *vertex2++);
    +  if (newsize < qh_setsize(mergedvertices)) {
    +    qh_fprintf(qh ferr, 6100, "qhull internal error (qh_mergevertices): facets did not share a ridge\n");
    +    qh_errexit(qh_ERRqhull, NULL, NULL);
    +  }
    +  qh_setfree(vertices2);
    +  *vertices2= mergedvertices;
    +  qh_settemppop();
    +} /* mergevertices */
    +
    +
    +/*---------------------------------
    +
    +  qh_neighbor_intersections( 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(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(neighborA->vertices, 0);
    +  else
    +    intersect= qh_vertexintersect_new(neighborA->vertices, neighborB->vertices);
    +  qh_settemppush(intersect);
    +  qh_setdelsorted(intersect, vertex);
    +  FOREACHneighbor_i_(vertex) {
    +    if (neighbor_i >= 2) {
    +      zinc_(Zintersectnum);
    +      qh_vertexintersect(&intersect, neighbor->vertices);
    +      if (!SETfirst_(intersect)) {
    +        zinc_(Zintersectfail);
    +        qh_settempfree(&intersect);
    +        return NULL;
    +      }
    +    }
    +  }
    +  trace3((qh ferr, 3007, "qh_neighbor_intersections: %d vertices in neighbor intersection of v%d\n",
    +          qh_setsize(intersect), vertex->id));
    +  return intersect;
    +} /* neighbor_intersections */
    +
    +/*---------------------------------
    +
    +  qh_newvertices( 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(setT *vertices) {
    +  vertexT *vertex, **vertexp;
    +
    +  FOREACHvertex_(vertices) {
    +    if (!vertex->newlist) {
    +      qh_removevertex(vertex);
    +      qh_appendvertex(vertex);
    +    }
    +  }
    +} /* newvertices */
    +
    +/*---------------------------------
    +
    +  qh_reducevertices()
    +    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(void) {
    +  int numshare=0, numrename= 0;
    +  boolT degenredun= False;
    +  facetT *newfacet;
    +  vertexT *vertex, **vertexp;
    +
    +  if (qh hull_dim == 2)
    +    return False;
    +  if (qh_merge_degenredundant())
    +    degenredun= True;
    + LABELrestart:
    +  FORALLnew_facets {
    +    if (newfacet->newmerge) {
    +      if (!qh MERGEvertices)
    +        newfacet->newmerge= False;
    +      qh_remove_extravertices(newfacet);
    +    }
    +  }
    +  if (!qh MERGEvertices)
    +    return False;
    +  FORALLnew_facets {
    +    if (newfacet->newmerge) {
    +      newfacet->newmerge= False;
    +      FOREACHvertex_(newfacet->vertices) {
    +        if (vertex->delridge) {
    +          if (qh_rename_sharedvertex(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(vertex)) {
    +        numrename++;
    +        if (qh_merge_degenredundant()) {
    +          degenredun= True;
    +          goto LABELrestart;
    +        }
    +      }
    +    }
    +  }
    +  trace1((qh ferr, 1014, "qh_reducevertices: renamed %d shared vertices and %d redundant vertices. Degen? %d\n",
    +          numshare, numrename, degenredun));
    +  return degenredun;
    +} /* reducevertices */
    +
    +/*---------------------------------
    +
    +  qh_redundant_vertex( 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(vertexT *vertex) {
    +  vertexT *newvertex= NULL;
    +  setT *vertices, *ridges;
    +
    +  trace3((qh ferr, 3008, "qh_redundant_vertex: check if v%d can be renamed\n", vertex->id));
    +  if ((vertices= qh_neighbor_intersections(vertex))) {
    +    ridges= qh_vertexridges(vertex);
    +    if ((newvertex= qh_find_newvertex(vertex, vertices, ridges)))
    +      qh_renamevertex(vertex, newvertex, ridges, NULL, NULL);
    +    qh_settempfree(&ridges);
    +    qh_settempfree(&vertices);
    +  }
    +  return newvertex;
    +} /* redundant_vertex */
    +
    +/*---------------------------------
    +
    +  qh_remove_extravertices( 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(facetT *facet) {
    +  ridgeT *ridge, **ridgep;
    +  vertexT *vertex, **vertexp;
    +  boolT foundrem= False;
    +
    +  trace4((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(vertex->neighbors)) {
    +        vertex->deleted= True;
    +        qh_setappend(&qh del_vertices, vertex);
    +        zinc_(Zremvertexdel);
    +        trace2((qh ferr, 2036, "qh_remove_extravertices: v%d deleted because it's lost all ridges\n", vertex->id));
    +      }else
    +        trace3((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 */
    +
    +/*---------------------------------
    +
    +  qh_rename_sharedvertex( 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(vertexT *vertex, facetT *facet) {
    +  facetT *neighbor, **neighborp, *neighborA= NULL;
    +  setT *vertices, *ridges;
    +  vertexT *newvertex;
    +
    +  if (qh_setsize(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 ferr, 6101, "qhull internal error (qh_rename_sharedvertex): v%d's neighbors not in f%d\n",
    +        vertex->id, facet->id);
    +      qh_errprint("ERRONEOUS", facet, NULL, NULL, vertex);
    +      qh_errexit(qh_ERRqhull, NULL, NULL);
    +    }
    +  }
    +  /* the vertex is shared by facet and neighborA */
    +  ridges= qh_settemp(qh TEMPsize);
    +  neighborA->visitid= ++qh visit_id;
    +  qh_vertexridges_facet(vertex, facet, &ridges);
    +  trace2((qh ferr, 2037, "qh_rename_sharedvertex: p%d(v%d) is shared by f%d(%d ridges) and f%d\n",
    +    qh_pointid(vertex->point), vertex->id, facet->id, qh_setsize(ridges), neighborA->id));
    +  zinc_(Zintersectnum);
    +  vertices= qh_vertexintersect_new(facet->vertices, neighborA->vertices);
    +  qh_setdel(vertices, vertex);
    +  qh_settemppush(vertices);
    +  if ((newvertex= qh_find_newvertex(vertex, vertices, ridges)))
    +    qh_renamevertex(vertex, newvertex, ridges, facet, neighborA);
    +  qh_settempfree(&vertices);
    +  qh_settempfree(&ridges);
    +  return newvertex;
    +} /* rename_sharedvertex */
    +
    +/*---------------------------------
    +
    +  qh_renameridgevertex( 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(ridgeT *ridge, vertexT *oldvertex, vertexT *newvertex) {
    +  int nth= 0, oldnth;
    +  facetT *temp;
    +  vertexT *vertex, **vertexp;
    +
    +  oldnth= qh_setindex(ridge->vertices, oldvertex);
    +  qh_setdelnthsorted(ridge->vertices, oldnth);
    +  FOREACHvertex_(ridge->vertices) {
    +    if (vertex == newvertex) {
    +      zinc_(Zdelridge);
    +      if (ridge->nonconvex) /* only one ridge has nonconvex set */
    +        qh_copynonconvex(ridge);
    +      trace2((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(ridge);
    +      return;
    +    }
    +    if (vertex->id < newvertex->id)
    +      break;
    +    nth++;
    +  }
    +  qh_setaddnth(&ridge->vertices, nth, newvertex);
    +  if (abs(oldnth - nth)%2) {
    +    trace3((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 */
    +
    +
    +/*---------------------------------
    +
    +  qh_renamevertex( 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(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(ridge, oldvertex, newvertex);
    +  if (!oldfacet) {
    +    zinc_(Zrenameall);
    +    if (istrace)
    +      qh_fprintf(qh ferr, 8082, "qh_renamevertex: renamed v%d to v%d in several facets\n",
    +               oldvertex->id, newvertex->id);
    +    FOREACHneighbor_(oldvertex) {
    +      qh_maydropneighbor(neighbor);
    +      qh_setdelsorted(neighbor->vertices, oldvertex);
    +      if (qh_remove_extravertices(neighbor))
    +        neighborp--; /* neighbor may be deleted */
    +    }
    +    if (!oldvertex->deleted) {
    +      oldvertex->deleted= True;
    +      qh_setappend(&qh del_vertices, oldvertex);
    +    }
    +  }else if (qh_setsize(oldvertex->neighbors) == 2) {
    +    zinc_(Zrenameshare);
    +    if (istrace)
    +      qh_fprintf(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 del_vertices, oldvertex);
    +  }else {
    +    zinc_(Zrenamepinch);
    +    if (istrace || qh IStracing)
    +      qh_fprintf(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(neighborA);
    +  }
    +} /* renamevertex */
    +
    +
    +/*---------------------------------
    +
    +  qh_test_appendmerge( 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(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(facet->normal, neighbor->normal);
    +    zinc_(Zangletests);
    +    if (angle > qh cos_max) {
    +      zinc_(Zcoplanarangle);
    +      qh_appendmergeset(facet, neighbor, MRGanglecoplanar, &angle);
    +      trace2((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(facet);
    +  zzinc_(Zcentrumtests);
    +  qh_distplane(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(neighbor);
    +    zzinc_(Zcentrumtests);
    +    qh_distplane(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(facet->normal, neighbor->normal);
    +    zinc_(Zangletests);
    +  }
    +  if (isconcave) {
    +    zinc_(Zconcaveridge);
    +    if (qh ANGLEmerge)
    +      angle += qh_ANGLEconcave + 0.5;
    +    qh_appendmergeset(facet, neighbor, MRGconcave, &angle);
    +    trace0((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(facet, neighbor, MRGcoplanar, &angle);
    +    trace2((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 */
    +
    +/*---------------------------------
    +
    +  qh_test_vneighbors()
    +    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(void /* qh.newfacet_list */) {
    +  facetT *newfacet, *neighbor, **neighborp;
    +  vertexT *vertex, **vertexp;
    +  int nummerges= 0;
    +
    +  trace1((qh ferr, 1015, "qh_test_vneighbors: testing vertex neighbors for convexity\n"));
    +  if (!qh VERTEXneighbors)
    +    qh_vertexneighbors();
    +  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(newfacet, neighbor))
    +          nummerges++;
    +      }
    +    }
    +  }
    +  zadd_(Ztestvneighbor, nummerges);
    +  trace1((qh ferr, 1016, "qh_test_vneighbors: found %d non-convex, vertex neighbors\n",
    +           nummerges));
    +  return (nummerges > 0);
    +} /* test_vneighbors */
    +
    +/*---------------------------------
    +
    +  qh_tracemerge( facet1, facet2 )
    +    print trace message after merge
    +*/
    +void qh_tracemerge(facetT *facet1, facetT *facet2) {
    +  boolT waserror= False;
    +
    +#ifndef qh_NOtrace
    +  if (qh IStracing >= 4)
    +    qh_errprint("MERGED", facet2, NULL, NULL, NULL);
    +  if (facet2 == qh tracefacet || (qh tracevertex && qh tracevertex->newlist)) {
    +    qh_fprintf(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("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 ferr, 8086, "qh_tracemerge: trace vertex deleted at furthest p%d\n",
    +            qh furthest_id);
    +    else
    +      qh_checkvertex(qh tracevertex);
    +  }
    +  if (qh tracefacet) {
    +    qh_checkfacet(qh tracefacet, True, &waserror);
    +    if (waserror)
    +      qh_errexit(qh_ERRqhull, qh tracefacet, NULL);
    +  }
    +#endif /* !qh_NOtrace */
    +  if (qh CHECKfrequently || qh IStracing >= 4) { /* can't check polygon here */
    +    qh_checkfacet(facet2, True, &waserror);
    +    if (waserror)
    +      qh_errexit(qh_ERRqhull, NULL, NULL);
    +  }
    +} /* tracemerge */
    +
    +/*---------------------------------
    +
    +  qh_tracemerging()
    +    print trace message during POSTmerging
    +
    +  returns:
    +    updates qh.mergereport
    +
    +  notes:
    +    called from qh_mergecycle() and qh_mergefacet()
    +
    +  see:
    +    qh_buildtracing()
    +*/
    +void qh_tracemerging(void) {
    +  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 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 del_vertices));
    +} /* tracemerging */
    +
    +/*---------------------------------
    +
    +  qh_updatetested( 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(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(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(facet2->center, qh normal_size);
    +    facet2->center= NULL;
    +    FOREACHridge_(facet2->ridges)
    +      ridge->tested= False;
    +  }
    +} /* updatetested */
    +
    +/*---------------------------------
    +
    +  qh_vertexridges( 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(vertexT *vertex) {
    +  facetT *neighbor, **neighborp;
    +  setT *ridges= qh_settemp(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(vertex, neighbor, &ridges);
    +  }
    +  if (qh PRINTstatistics || qh IStracing) {
    +    size= qh_setsize(ridges);
    +    zinc_(Zvertexridge);
    +    zadd_(Zvertexridgetot, size);
    +    zmax_(Zvertexridgemax, size);
    +    trace3((qh ferr, 3011, "qh_vertexridges: found %d ridges for v%d\n",
    +             size, vertex->id));
    +  }
    +  return ridges;
    +} /* vertexridges */
    +
    +/*---------------------------------
    +
    +  qh_vertexridges_facet( 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(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(ridges, ridge);
    +  }
    +  facet->visitid= qh visit_id-1;
    +} /* vertexridges_facet */
    +
    +/*---------------------------------
    +
    +  qh_willdelete( facet, replace )
    +    moves facet to visible list
    +    sets facet->f.replace to replace (may be NULL)
    +
    +  returns:
    +    bumps qh.num_visible
    +*/
    +void qh_willdelete(facetT *facet, facetT *replace) {
    +
    +  qh_removefacet(facet);
    +  qh_prependfacet(facet, &qh visible_list);
    +  qh num_visible++;
    +  facet->visible= True;
    +  facet->f.replace= replace;
    +} /* willdelete */
    +
    +#else /* qh_NOmerge */
    +void qh_premerge(vertexT *apex, realT maxcentrum, realT maxangle) {
    +}
    +void qh_postmerge(const char *reason, realT maxcentrum, realT maxangle,
    +                      boolT vneighbors) {
    +}
    +boolT qh_checkzero(boolT testall) {
    +   }
    +#endif /* qh_NOmerge */
    +
    diff --git a/xs/src/qhull/src/libqhull/merge.h b/xs/src/qhull/src/libqhull/merge.h
    new file mode 100644
    index 000000000..7f5ec3fb6
    --- /dev/null
    +++ b/xs/src/qhull/src/libqhull/merge.h
    @@ -0,0 +1,178 @@
    +/*
      ---------------------------------
    +
    +   merge.h
    +   header file for merge.c
    +
    +   see qh-merge.htm and merge.c
    +
    +   Copyright (c) 1993-2015 C.B. Barber.
    +   $Id: //main/2015/qhull/src/libqhull/merge.h#1 $$Change: 1981 $
    +   $DateTime: 2015/09/28 20:26:32 $$Author: bbarber $
    +*/
    +
    +#ifndef qhDEFmerge
    +#define qhDEFmerge 1
    +
    +#include "libqhull.h"
    +
    +
    +/*============ -constants- ==============*/
    +
    +/*----------------------------------
    +
    +  qh_ANGLEredundant
    +    indicates redundant merge in mergeT->angle
    +*/
    +#define qh_ANGLEredundant 6.0
    +
    +/*----------------------------------
    +
    +  qh_ANGLEdegen
    +    indicates degenerate facet in mergeT->angle
    +*/
    +#define qh_ANGLEdegen     5.0
    +
    +/*----------------------------------
    +
    +  qh_ANGLEconcave
    +    offset to indicate concave facets in mergeT->angle
    +
    +  notes:
    +    concave facets are assigned the range of [2,4] in mergeT->angle
    +    roundoff error may make the angle less than 2
    +*/
    +#define qh_ANGLEconcave  1.5
    +
    +/*----------------------------------
    +
    +  MRG... (mergeType)
    +    indicates the type of a merge (mergeT->type)
    +*/
    +typedef enum {  /* in sort order for facet_mergeset */
    +  MRGnone= 0,
    +  MRGcoplanar,          /* centrum coplanar */
    +  MRGanglecoplanar,     /* angle coplanar */
    +                        /* could detect half concave ridges */
    +  MRGconcave,           /* concave ridge */
    +  MRGflip,              /* flipped facet. facet1 == facet2 */
    +  MRGridge,             /* duplicate ridge (qh_MERGEridge) */
    +                        /* degen and redundant go onto degen_mergeset */
    +  MRGdegen,             /* degenerate facet (!enough neighbors) facet1 == facet2 */
    +  MRGredundant,         /* redundant facet (vertex subset) */
    +                        /* merge_degenredundant assumes degen < redundant */
    +  MRGmirror,            /* mirror facet from qh_triangulate */
    +  ENDmrg
    +} mergeType;
    +
    +/*----------------------------------
    +
    +  qh_MERGEapex
    +    flag for qh_mergefacet() to indicate an apex merge
    +*/
    +#define qh_MERGEapex     True
    +
    +/*============ -structures- ====================*/
    +
    +/*----------------------------------
    +
    +  mergeT
    +    structure used to merge facets
    +*/
    +
    +typedef struct mergeT mergeT;
    +struct mergeT {         /* initialize in qh_appendmergeset */
    +  realT   angle;        /* angle between normals of facet1 and facet2 */
    +  facetT *facet1;       /* will merge facet1 into facet2 */
    +  facetT *facet2;
    +  mergeType type;
    +};
    +
    +
    +/*=========== -macros- =========================*/
    +
    +/*----------------------------------
    +
    +  FOREACHmerge_( merges ) {...}
    +    assign 'merge' to each merge in merges
    +
    +  notes:
    +    uses 'mergeT *merge, **mergep;'
    +    if qh_mergefacet(),
    +      restart since qh.facet_mergeset may change
    +    see FOREACHsetelement_
    +*/
    +#define FOREACHmerge_( merges ) FOREACHsetelement_(mergeT, merges, merge)
    +
    +/*============ prototypes in alphabetical order after pre/postmerge =======*/
    +
    +void    qh_premerge(vertexT *apex, realT maxcentrum, realT maxangle);
    +void    qh_postmerge(const char *reason, realT maxcentrum, realT maxangle,
    +             boolT vneighbors);
    +void    qh_all_merges(boolT othermerge, boolT vneighbors);
    +void    qh_appendmergeset(facetT *facet, facetT *neighbor, mergeType mergetype, realT *angle);
    +setT   *qh_basevertices( facetT *samecycle);
    +void    qh_checkconnect(void /* qh.new_facets */);
    +boolT   qh_checkzero(boolT testall);
    +int     qh_compareangle(const void *p1, const void *p2);
    +int     qh_comparemerge(const void *p1, const void *p2);
    +int     qh_comparevisit(const void *p1, const void *p2);
    +void    qh_copynonconvex(ridgeT *atridge);
    +void    qh_degen_redundant_facet(facetT *facet);
    +void    qh_degen_redundant_neighbors(facetT *facet, facetT *delfacet);
    +vertexT *qh_find_newvertex(vertexT *oldvertex, setT *vertices, setT *ridges);
    +void    qh_findbest_test(boolT testcentrum, facetT *facet, facetT *neighbor,
    +           facetT **bestfacet, realT *distp, realT *mindistp, realT *maxdistp);
    +facetT *qh_findbestneighbor(facetT *facet, realT *distp, realT *mindistp, realT *maxdistp);
    +void    qh_flippedmerges(facetT *facetlist, boolT *wasmerge);
    +void    qh_forcedmerges( boolT *wasmerge);
    +void    qh_getmergeset(facetT *facetlist);
    +void    qh_getmergeset_initial(facetT *facetlist);
    +void    qh_hashridge(setT *hashtable, int hashsize, ridgeT *ridge, vertexT *oldvertex);
    +ridgeT *qh_hashridge_find(setT *hashtable, int hashsize, ridgeT *ridge,
    +              vertexT *vertex, vertexT *oldvertex, int *hashslot);
    +void    qh_makeridges(facetT *facet);
    +void    qh_mark_dupridges(facetT *facetlist);
    +void    qh_maydropneighbor(facetT *facet);
    +int     qh_merge_degenredundant(void);
    +void    qh_merge_nonconvex( facetT *facet1, facetT *facet2, mergeType mergetype);
    +void    qh_mergecycle(facetT *samecycle, facetT *newfacet);
    +void    qh_mergecycle_all(facetT *facetlist, boolT *wasmerge);
    +void    qh_mergecycle_facets( facetT *samecycle, facetT *newfacet);
    +void    qh_mergecycle_neighbors(facetT *samecycle, facetT *newfacet);
    +void    qh_mergecycle_ridges(facetT *samecycle, facetT *newfacet);
    +void    qh_mergecycle_vneighbors( facetT *samecycle, facetT *newfacet);
    +void    qh_mergefacet(facetT *facet1, facetT *facet2, realT *mindist, realT *maxdist, boolT mergeapex);
    +void    qh_mergefacet2d(facetT *facet1, facetT *facet2);
    +void    qh_mergeneighbors(facetT *facet1, facetT *facet2);
    +void    qh_mergeridges(facetT *facet1, facetT *facet2);
    +void    qh_mergesimplex(facetT *facet1, facetT *facet2, boolT mergeapex);
    +void    qh_mergevertex_del(vertexT *vertex, facetT *facet1, facetT *facet2);
    +void    qh_mergevertex_neighbors(facetT *facet1, facetT *facet2);
    +void    qh_mergevertices(setT *vertices1, setT **vertices);
    +setT   *qh_neighbor_intersections(vertexT *vertex);
    +void    qh_newvertices(setT *vertices);
    +boolT   qh_reducevertices(void);
    +vertexT *qh_redundant_vertex(vertexT *vertex);
    +boolT   qh_remove_extravertices(facetT *facet);
    +vertexT *qh_rename_sharedvertex(vertexT *vertex, facetT *facet);
    +void    qh_renameridgevertex(ridgeT *ridge, vertexT *oldvertex, vertexT *newvertex);
    +void    qh_renamevertex(vertexT *oldvertex, vertexT *newvertex, setT *ridges,
    +                        facetT *oldfacet, facetT *neighborA);
    +boolT   qh_test_appendmerge(facetT *facet, facetT *neighbor);
    +boolT   qh_test_vneighbors(void /* qh.newfacet_list */);
    +void    qh_tracemerge(facetT *facet1, facetT *facet2);
    +void    qh_tracemerging(void);
    +void    qh_updatetested( facetT *facet1, facetT *facet2);
    +setT   *qh_vertexridges(vertexT *vertex);
    +void    qh_vertexridges_facet(vertexT *vertex, facetT *facet, setT **ridges);
    +void    qh_willdelete(facetT *facet, facetT *replace);
    +
    +#endif /* qhDEFmerge */
    diff --git a/xs/src/qhull/src/libqhull/poly.c b/xs/src/qhull/src/libqhull/poly.c
    new file mode 100644
    index 000000000..b8db6a9ef
    --- /dev/null
    +++ b/xs/src/qhull/src/libqhull/poly.c
    @@ -0,0 +1,1205 @@
    +/*
      ---------------------------------
    +
    +   poly.c
    +   implements polygons and simplices
    +
    +   see qh-poly.htm, poly.h and libqhull.h
    +
    +   infrequent code is in poly2.c
    +   (all but top 50 and their callers 12/3/95)
    +
    +   Copyright (c) 1993-2015 The Geometry Center.
    +   $Id: //main/2015/qhull/src/libqhull/poly.c#3 $$Change: 2064 $
    +   $DateTime: 2016/01/18 12:36:08 $$Author: bbarber $
    +*/
    +
    +#include "qhull_a.h"
    +
    +/*======== functions in alphabetical order ==========*/
    +
    +/*---------------------------------
    +
    +  qh_appendfacet( 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(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 ferr, 4044, "qh_appendfacet: append f%d to facet_list\n", facet->id));
    +} /* appendfacet */
    +
    +
    +/*---------------------------------
    +
    +  qh_appendvertex( 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(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 ferr, 4045, "qh_appendvertex: append v%d to vertex_list\n", vertex->id));
    +} /* appendvertex */
    +
    +
    +/*---------------------------------
    +
    +  qh_attachnewfacets( )
    +    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(void /* qh.visible_list, newfacet_list */) {
    +  facetT *newfacet= NULL, *neighbor, **neighborp, *horizon, *visible;
    +  ridgeT *ridge, **ridgep;
    +
    +  qh NEWfacets= True;
    +  trace3((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(&(ridge->vertices)); /* delete on 2nd visit */
    +          qh_memfree(ridge, (int)sizeof(ridgeT));
    +        }
    +      }
    +      SETfirst_(visible->ridges)= NULL;
    +    }
    +    SETfirst_(visible->neighbors)= NULL;
    +  }
    +  trace1((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(horizon->neighbors, visible, newfacet);
    +      }else {
    +        qh_fprintf(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_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(horizon->neighbors,
    +                        SETindex_(horizon->neighbors, neighbor));
    +          neighborp--; /* repeat */
    +        }
    +      }
    +      qh_setappend(&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 */
    +
    +/*---------------------------------
    +
    +  qh_checkflipped( 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(facetT *facet, realT *distp, boolT allerror) {
    +  realT dist;
    +
    +  if (facet->flipped && !distp)
    +    return False;
    +  zzinc_(Zdistcheck);
    +  qh_distplane(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 ferr, 19, "qh_checkflipped: facet f%d is flipped, distance= %6.12g during p%d\n",
    +              facet->id, dist, qh furthest_id));
    +    qh_precision("flipped facet");
    +    return False;
    +  }
    +  return True;
    +} /* checkflipped */
    +
    +/*---------------------------------
    +
    +  qh_delfacet( facet )
    +    removes facet from facet_list and frees up its memory
    +
    +  notes:
    +    assumes vertices and ridges already freed
    +*/
    +void qh_delfacet(facetT *facet) {
    +  void **freelistp; /* used if !qh_NOmem by qh_memfree_() */
    +
    +  trace4((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(facet);
    +  if (!facet->tricoplanar || facet->keepcentrum) {
    +    qh_memfree_(facet->normal, qh normal_size, freelistp);
    +    if (qh CENTERtype == qh_ASvoronoi) {   /* braces for macro calls */
    +      qh_memfree_(facet->center, qh center_size, freelistp);
    +    }else /* AScentrum */ {
    +      qh_memfree_(facet->center, qh normal_size, freelistp);
    +    }
    +  }
    +  qh_setfree(&(facet->neighbors));
    +  if (facet->ridges)
    +    qh_setfree(&(facet->ridges));
    +  qh_setfree(&(facet->vertices));
    +  if (facet->outsideset)
    +    qh_setfree(&(facet->outsideset));
    +  if (facet->coplanarset)
    +    qh_setfree(&(facet->coplanarset));
    +  qh_memfree_(facet, (int)sizeof(facetT), freelistp);
    +} /* delfacet */
    +
    +
    +/*---------------------------------
    +
    +  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(void /*qh.visible_list*/) {
    +  facetT *visible, *nextfacet;
    +  vertexT *vertex, **vertexp;
    +  int numvisible= 0, numdel= qh_setsize(qh del_vertices);
    +
    +  trace1((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(visible);
    +  }
    +  if (numvisible != qh num_visible) {
    +    qh_fprintf(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_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(vertex);
    +  qh_settruncate(qh del_vertices, 0);
    +} /* deletevisible */
    +
    +/*---------------------------------
    +
    +  qh_facetintersect( 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(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 ferr, 6104, "qhull internal error (qh_facetintersect): f%d or f%d not in others neighbors\n",
    +            facetA->id, facetB->id);
    +    qh_errexit2(qh_ERRqhull, facetA, facetB);
    +  }
    +  intersect= qh_setnew_delnthsorted(facetA->vertices, qh hull_dim, *skipA, prepend);
    +  trace4((qh ferr, 4047, "qh_facetintersect: f%d skip %d matches f%d skip %d\n",
    +          facetA->id, *skipA, facetB->id, *skipB));
    +  return(intersect);
    +} /* facetintersect */
    +
    +/*---------------------------------
    +
    +  qh_gethash( 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(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 ferr, 6202, "qhull internal error: negative hashsize %d passed to qh_gethash [poly.c]\n", hashsize);
    +    qh_errexit2(qh_ERRqhull, NULL, NULL);
    +  }
    +  result= (unsigned)hash;
    +  result %= (unsigned)hashsize;
    +  /* result= 0; for debugging */
    +  return result;
    +#ifdef _MSC_VER
    +#pragma warning( pop)
    +#endif
    +} /* gethash */
    +
    +/*---------------------------------
    +
    +  qh_makenewfacet( 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(setT *vertices, boolT toporient,facetT *horizon) {
    +  facetT *newfacet;
    +  vertexT *vertex, **vertexp;
    +
    +  FOREACHvertex_(vertices) {
    +    if (!vertex->newlist) {
    +      qh_removevertex(vertex);
    +      qh_appendvertex(vertex);
    +    }
    +  }
    +  newfacet= qh_newfacet();
    +  newfacet->vertices= vertices;
    +  newfacet->toporient= (unsigned char)toporient;
    +  if (horizon)
    +    qh_setappend(&(newfacet->neighbors), horizon);
    +  qh_appendfacet(newfacet);
    +  return(newfacet);
    +} /* makenewfacet */
    +
    +
    +/*---------------------------------
    +
    +  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(void /* newfacet_list */) {
    +  facetT *newfacet;
    +
    +  FORALLnew_facets {
    +    if (!newfacet->mergehorizon)
    +      qh_setfacetplane(newfacet);
    +  }
    +  if (qh JOGGLEmax < REALmax/2)
    +    minimize_(qh min_vertex, -wwval_(Wnewvertexmax));
    +} /* makenewplanes */
    +
    +/*---------------------------------
    +
    +  qh_makenew_nonsimplicial( 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(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(&(ridge->vertices));  /* delete on 2nd visit */
    +          qh_memfree_(ridge, (int)sizeof(ridgeT), freelistp);
    +        }
    +      }
    +    }else {  /* neighbor is an horizon facet */
    +      toporient= (ridge->top == visible);
    +      vertices= qh_setnew(qh hull_dim); /* makes sure this is quick */
    +      qh_setappend(&vertices, apex);
    +      qh_setappend_set(&vertices, ridge->vertices);
    +      newfacet= qh_makenewfacet(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(&(newfacet->ridges), ridge);
    +      }else {  /* qh_attachnewfacets */
    +        if (neighbor->seen) {
    +          if (neighbor->simplicial) {
    +            qh_fprintf(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_ERRqhull, neighbor, visible);
    +          }
    +          qh_setappend(&(neighbor->neighbors), newfacet);
    +        }else
    +          qh_setreplace(neighbor->neighbors, visible, newfacet);
    +        if (neighbor->simplicial) {
    +          qh_setdel(neighbor->ridges, ridge);
    +          qh_setfree(&(ridge->vertices));
    +          qh_memfree(ridge, (int)sizeof(ridgeT));
    +        }else {
    +          qh_setappend(&(newfacet->ridges), ridge);
    +          if (toporient)
    +            ridge->top= newfacet;
    +          else
    +            ridge->bottom= newfacet;
    +        }
    +      trace4((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(facetT *visible, vertexT *apex, int *numnew) {
    +  return NULL;
    +}
    +#endif /* qh_NOmerge */
    +
    +/*---------------------------------
    +
    +  qh_makenew_simplicial( 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(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(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(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 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 */
    +
    +/*---------------------------------
    +
    +  qh_matchneighbor( 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(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(hashsize, newfacet->vertices, qh hull_dim, 1,
    +                     SETelem_(newfacet->vertices, newskip));
    +  trace4((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(1, newfacet->vertices, newskip, facet->vertices, &skip, &same)) {
    +      if (SETelem_(newfacet->vertices, newskip) ==
    +          SETelem_(facet->vertices, skip)) {
    +        qh_precision("two facets with the same vertices");
    +        qh_fprintf(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_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 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("a ridge with more than two neighbors");
    +        qh_fprintf(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_ERRprec, facet, newfacet);
    +      }
    +      SETelem_(newfacet->neighbors, newskip)= qh_DUPLICATEridge;
    +      newfacet->dupridge= True;
    +      if (!newfacet->normal)
    +        qh_setfacetplane(newfacet);
    +      qh_addhash(newfacet, qh hash_table, hashsize, hash);
    +      (*hashcount)++;
    +      if (!facet->normal)
    +        qh_setfacetplane(facet);
    +      if (matchfacet != qh_DUPLICATEridge) {
    +        SETelem_(facet->neighbors, skip)= qh_DUPLICATEridge;
    +        facet->dupridge= True;
    +        if (!facet->normal)
    +          qh_setfacetplane(facet);
    +        if (matchfacet) {
    +          matchskip= qh_setindex(matchfacet->neighbors, facet);
    +          if (matchskip<0) {
    +              qh_fprintf(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_ERRqhull, matchfacet, facet);
    +          }
    +          SETelem_(matchfacet->neighbors, matchskip)= qh_DUPLICATEridge; /* matchskip>=0 by QH6260 */
    +          matchfacet->dupridge= True;
    +          if (!matchfacet->normal)
    +            qh_setfacetplane(matchfacet);
    +          qh_addhash(matchfacet, qh hash_table, hashsize, hash);
    +          *hashcount += 2;
    +        }
    +      }
    +      trace4((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 ferr, 4053, "qh_matchneighbor: no match for f%d skip %d at hash %d\n",
    +           newfacet->id, newskip, hash));
    +} /* matchneighbor */
    +
    +
    +/*---------------------------------
    +
    +  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(void /* 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 ferr, 1019, "qh_matchnewfacets: match neighbors for new facets.\n"));
    +  FORALLnew_facets {
    +    numnew++;
    +    {  /* inline qh_setzero(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(numnew*(qh hull_dim-1)); /* twice what is normally needed,
    +                                     but every ridge could be DUPLICATEridge */
    +  hashsize= qh_setsize(qh hash_table);
    +  FORALLnew_facets {
    +    for (newskip=1; newskip0 because hull_dim>1 and numnew>0 */
    +      qh_matchneighbor(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 ferr, 8088, "qh_matchnewfacets: after adding facet %d, hashcount %d != count %d\n",
    +                 newfacet->id, hashcount, count);
    +        qh_errexit(qh_ERRqhull, newfacet, NULL);
    +      }
    +    }
    +#endif  /* end of trap code */
    +  }
    +  if (hashcount) {
    +    FORALLnew_facets {
    +      if (newfacet->dupridge) {
    +        FOREACHneighbor_i_(newfacet) {
    +          if (neighbor == qh_DUPLICATEridge) {
    +            qh_matchduplicates(newfacet, neighbor_i, hashsize, &hashcount);
    +                    /* this may report MERGEfacet */
    +          }
    +        }
    +      }
    +    }
    +  }
    +  if (hashcount) {
    +    qh_fprintf(qh ferr, 6108, "qhull internal error (qh_matchnewfacets): %d neighbors did not match up\n",
    +        hashcount);
    +    qh_printhashtable(qh ferr);
    +    qh_errexit(qh_ERRqhull, NULL, NULL);
    +  }
    +#ifndef qh_NOtrace
    +  if (qh IStracing >= 2) {
    +    FOREACHfacet_i_(qh hash_table) {
    +      if (!facet)
    +        numfree++;
    +    }
    +    qh_fprintf(qh ferr, 8089, "qh_matchnewfacets: %d new facets, %d unused hash entries .  hashsize %d\n",
    +             numnew, numfree, qh_setsize(qh hash_table));
    +  }
    +#endif /* !qh_NOtrace */
    +  qh_setfree(&qh hash_table);
    +  if (qh PREmerge || qh MERGEexact) {
    +    if (qh IStracing >= 4)
    +      qh_printfacetlist(qh newfacet_list, NULL, qh_ALL);
    +    FORALLnew_facets {
    +      if (newfacet->normal)
    +        qh_checkflipped(newfacet, NULL, qh_ALL);
    +    }
    +  }else if (qh FORCEoutput)
    +    qh_checkflipped_all(qh newfacet_list);  /* prints warnings for flipped */
    +} /* matchnewfacets */
    +
    +
    +/*---------------------------------
    +
    +  qh_matchvertices( 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(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 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 */
    +
    +/*---------------------------------
    +
    +  qh_newfacet()
    +    return a new facet
    +
    +  returns:
    +    all fields initialized or cleared   (NULL)
    +    preallocates neighbors set
    +*/
    +facetT *qh_newfacet(void) {
    +  facetT *facet;
    +  void **freelistp; /* used if !qh_NOmem by qh_memalloc_() */
    +
    +  qh_memalloc_((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 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 ferr, 4055, "qh_newfacet: created facet f%d\n", facet->id));
    +  return(facet);
    +} /* newfacet */
    +
    +
    +/*---------------------------------
    +
    +  qh_newridge()
    +    return a new ridge
    +*/
    +ridgeT *qh_newridge(void) {
    +  ridgeT *ridge;
    +  void **freelistp;   /* used if !qh_NOmem by qh_memalloc_() */
    +
    +  qh_memalloc_((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 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 ferr, 4056, "qh_newridge: created ridge r%d\n", ridge->id));
    +  return(ridge);
    +} /* newridge */
    +
    +
    +/*---------------------------------
    +
    +  qh_pointid(  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(pointT *point) {
    +  ptr_intT offset, id;
    +
    +  if (!point)
    +    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 */
    +
    +/*---------------------------------
    +
    +  qh_removefacet( 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(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 ferr, 4057, "qh_removefacet: remove f%d from facet_list\n", facet->id));
    +} /* removefacet */
    +
    +
    +/*---------------------------------
    +
    +  qh_removevertex( vertex )
    +    unlinks vertex from qh.vertex_list,
    +
    +  returns:
    +    updates qh.vertex_list .newvertex_list
    +    decrements qh.num_vertices
    +*/
    +void qh_removevertex(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 ferr, 4058, "qh_removevertex: remove v%d from vertex_list\n", vertex->id));
    +} /* removevertex */
    +
    +
    +/*---------------------------------
    +
    +  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(void /*qh.newvertex_list, newfacet_list, visible_list*/) {
    +  facetT *newfacet= NULL, *neighbor, **neighborp, *visible;
    +  vertexT *vertex, **vertexp;
    +
    +  trace3((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(vertex->neighbors);
    +    }
    +    FORALLnew_facets {
    +      FOREACHvertex_(newfacet->vertices)
    +        qh_setappend(&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 del_vertices, vertex);
    +            trace2((qh ferr, 2041, "qh_updatevertices: delete vertex p%d(v%d) in f%d\n",
    +                  qh_pointid(vertex->point), vertex->id, visible->id));
    +          }
    +        }
    +      }
    +    }
    +  }else {  /* !VERTEXneighbors */
    +    FORALLvisible_facets {
    +      FOREACHvertex_(visible->vertices) {
    +        if (!vertex->newlist && !vertex->deleted) {
    +          vertex->deleted= True;
    +          qh_setappend(&qh del_vertices, vertex);
    +          trace2((qh ferr, 2042, "qh_updatevertices: delete vertex p%d(v%d) in f%d\n",
    +                  qh_pointid(vertex->point), vertex->id, visible->id));
    +        }
    +      }
    +    }
    +  }
    +} /* updatevertices */
    +
    +
    +
    diff --git a/xs/src/qhull/src/libqhull/poly.h b/xs/src/qhull/src/libqhull/poly.h
    new file mode 100644
    index 000000000..af8b42077
    --- /dev/null
    +++ b/xs/src/qhull/src/libqhull/poly.h
    @@ -0,0 +1,296 @@
    +/*
      ---------------------------------
    +
    +   poly.h
    +   header file for poly.c and poly2.c
    +
    +   see qh-poly.htm, libqhull.h and poly.c
    +
    +   Copyright (c) 1993-2015 The Geometry Center.
    +   $Id: //main/2015/qhull/src/libqhull/poly.h#3 $$Change: 2047 $
    +   $DateTime: 2016/01/04 22:03:18 $$Author: bbarber $
    +*/
    +
    +#ifndef qhDEFpoly
    +#define qhDEFpoly 1
    +
    +#include "libqhull.h"
    +
    +/*===============   constants ========================== */
    +
    +/*----------------------------------
    +
    +  ALGORITHMfault
    +    use as argument to checkconvex() to report errors during buildhull
    +*/
    +#define qh_ALGORITHMfault 0
    +
    +/*----------------------------------
    +
    +  DATAfault
    +    use as argument to checkconvex() to report errors during initialhull
    +*/
    +#define qh_DATAfault 1
    +
    +/*----------------------------------
    +
    +  DUPLICATEridge
    +    special value for facet->neighbor to indicate a duplicate ridge
    +
    +  notes:
    +    set by matchneighbor, used by matchmatch and mark_dupridge
    +*/
    +#define qh_DUPLICATEridge (facetT *)1L
    +
    +/*----------------------------------
    +
    +  MERGEridge       flag in facet
    +    special value for facet->neighbor to indicate a merged ridge
    +
    +  notes:
    +    set by matchneighbor, used by matchmatch and mark_dupridge
    +*/
    +#define qh_MERGEridge (facetT *)2L
    +
    +
    +/*============ -structures- ====================*/
    +
    +/*=========== -macros- =========================*/
    +
    +/*----------------------------------
    +
    +  FORALLfacet_( facetlist ) { ... }
    +    assign 'facet' to each facet in facetlist
    +
    +  notes:
    +    uses 'facetT *facet;'
    +    assumes last facet is a sentinel
    +
    +  see:
    +    FORALLfacets
    +*/
    +#define FORALLfacet_( facetlist ) if (facetlist ) for ( facet=( facetlist ); facet && facet->next; facet= facet->next )
    +
    +/*----------------------------------
    +
    +  FORALLnew_facets { ... }
    +    assign 'newfacet' to each facet in qh.newfacet_list
    +
    +  notes:
    +    uses 'facetT *newfacet;'
    +    at exit, newfacet==NULL
    +*/
    +#define FORALLnew_facets for ( newfacet=qh newfacet_list;newfacet && newfacet->next;newfacet=newfacet->next )
    +
    +/*----------------------------------
    +
    +  FORALLvertex_( vertexlist ) { ... }
    +    assign 'vertex' to each vertex in vertexlist
    +
    +  notes:
    +    uses 'vertexT *vertex;'
    +    at exit, vertex==NULL
    +*/
    +#define FORALLvertex_( vertexlist ) for (vertex=( vertexlist );vertex && vertex->next;vertex= vertex->next )
    +
    +/*----------------------------------
    +
    +  FORALLvisible_facets { ... }
    +    assign 'visible' to each visible facet in qh.visible_list
    +
    +  notes:
    +    uses 'vacetT *visible;'
    +    at exit, visible==NULL
    +*/
    +#define FORALLvisible_facets for (visible=qh visible_list; visible && visible->visible; visible= visible->next)
    +
    +/*----------------------------------
    +
    +  FORALLsame_( newfacet ) { ... }
    +    assign 'same' to each facet in newfacet->f.samecycle
    +
    +  notes:
    +    uses 'facetT *same;'
    +    stops when it returns to newfacet
    +*/
    +#define FORALLsame_(newfacet) for (same= newfacet->f.samecycle; same != newfacet; same= same->f.samecycle)
    +
    +/*----------------------------------
    +
    +  FORALLsame_cycle_( newfacet ) { ... }
    +    assign 'same' to each facet in newfacet->f.samecycle
    +
    +  notes:
    +    uses 'facetT *same;'
    +    at exit, same == NULL
    +*/
    +#define FORALLsame_cycle_(newfacet) \
    +     for (same= newfacet->f.samecycle; \
    +         same; same= (same == newfacet ?  NULL : same->f.samecycle))
    +
    +/*----------------------------------
    +
    +  FOREACHneighborA_( facet ) { ... }
    +    assign 'neighborA' to each neighbor in facet->neighbors
    +
    +  FOREACHneighborA_( vertex ) { ... }
    +    assign 'neighborA' to each neighbor in vertex->neighbors
    +
    +  declare:
    +    facetT *neighborA, **neighborAp;
    +
    +  see:
    +    FOREACHsetelement_
    +*/
    +#define FOREACHneighborA_(facet)  FOREACHsetelement_(facetT, facet->neighbors, neighborA)
    +
    +/*----------------------------------
    +
    +  FOREACHvisible_( facets ) { ... }
    +    assign 'visible' to each facet in facets
    +
    +  notes:
    +    uses 'facetT *facet, *facetp;'
    +    see FOREACHsetelement_
    +*/
    +#define FOREACHvisible_(facets) FOREACHsetelement_(facetT, facets, visible)
    +
    +/*----------------------------------
    +
    +  FOREACHnewfacet_( facets ) { ... }
    +    assign 'newfacet' to each facet in facets
    +
    +  notes:
    +    uses 'facetT *newfacet, *newfacetp;'
    +    see FOREACHsetelement_
    +*/
    +#define FOREACHnewfacet_(facets) FOREACHsetelement_(facetT, facets, newfacet)
    +
    +/*----------------------------------
    +
    +  FOREACHvertexA_( vertices ) { ... }
    +    assign 'vertexA' to each vertex in vertices
    +
    +  notes:
    +    uses 'vertexT *vertexA, *vertexAp;'
    +    see FOREACHsetelement_
    +*/
    +#define FOREACHvertexA_(vertices) FOREACHsetelement_(vertexT, vertices, vertexA)
    +
    +/*----------------------------------
    +
    +  FOREACHvertexreverse12_( vertices ) { ... }
    +    assign 'vertex' to each vertex in vertices
    +    reverse order of first two vertices
    +
    +  notes:
    +    uses 'vertexT *vertex, *vertexp;'
    +    see FOREACHsetelement_
    +*/
    +#define FOREACHvertexreverse12_(vertices) FOREACHsetelementreverse12_(vertexT, vertices, vertex)
    +
    +
    +/*=============== prototypes poly.c in alphabetical order ================*/
    +
    +void    qh_appendfacet(facetT *facet);
    +void    qh_appendvertex(vertexT *vertex);
    +void    qh_attachnewfacets(void /* qh.visible_list, qh.newfacet_list */);
    +boolT   qh_checkflipped(facetT *facet, realT *dist, boolT allerror);
    +void    qh_delfacet(facetT *facet);
    +void    qh_deletevisible(void /*qh.visible_list, qh.horizon_list*/);
    +setT   *qh_facetintersect(facetT *facetA, facetT *facetB, int *skipAp,int *skipBp, int extra);
    +int     qh_gethash(int hashsize, setT *set, int size, int firstindex, void *skipelem);
    +facetT *qh_makenewfacet(setT *vertices, boolT toporient, facetT *facet);
    +void    qh_makenewplanes(void /* newfacet_list */);
    +facetT *qh_makenew_nonsimplicial(facetT *visible, vertexT *apex, int *numnew);
    +facetT *qh_makenew_simplicial(facetT *visible, vertexT *apex, int *numnew);
    +void    qh_matchneighbor(facetT *newfacet, int newskip, int hashsize,
    +                          int *hashcount);
    +void    qh_matchnewfacets(void);
    +boolT   qh_matchvertices(int firstindex, setT *verticesA, int skipA,
    +                          setT *verticesB, int *skipB, boolT *same);
    +facetT *qh_newfacet(void);
    +ridgeT *qh_newridge(void);
    +int     qh_pointid(pointT *point);
    +void    qh_removefacet(facetT *facet);
    +void    qh_removevertex(vertexT *vertex);
    +void    qh_updatevertices(void);
    +
    +
    +/*========== -prototypes poly2.c in alphabetical order ===========*/
    +
    +void    qh_addhash(void* newelem, setT *hashtable, int hashsize, int hash);
    +void    qh_check_bestdist(void);
    +void    qh_check_dupridge(facetT *facet1, realT dist1, facetT *facet2, realT dist2);
    +void    qh_check_maxout(void);
    +void    qh_check_output(void);
    +void    qh_check_point(pointT *point, facetT *facet, realT *maxoutside, realT *maxdist, facetT **errfacet1, facetT **errfacet2);
    +void    qh_check_points(void);
    +void    qh_checkconvex(facetT *facetlist, int fault);
    +void    qh_checkfacet(facetT *facet, boolT newmerge, boolT *waserrorp);
    +void    qh_checkflipped_all(facetT *facetlist);
    +void    qh_checkpolygon(facetT *facetlist);
    +void    qh_checkvertex(vertexT *vertex);
    +void    qh_clearcenters(qh_CENTER type);
    +void    qh_createsimplex(setT *vertices);
    +void    qh_delridge(ridgeT *ridge);
    +void    qh_delvertex(vertexT *vertex);
    +setT   *qh_facet3vertex(facetT *facet);
    +facetT *qh_findbestfacet(pointT *point, boolT bestoutside,
    +           realT *bestdist, boolT *isoutside);
    +facetT *qh_findbestlower(facetT *upperfacet, pointT *point, realT *bestdistp, int *numpart);
    +facetT *qh_findfacet_all(pointT *point, realT *bestdist, boolT *isoutside,
    +                          int *numpart);
    +int     qh_findgood(facetT *facetlist, int goodhorizon);
    +void    qh_findgood_all(facetT *facetlist);
    +void    qh_furthestnext(void /* qh.facet_list */);
    +void    qh_furthestout(facetT *facet);
    +void    qh_infiniteloop(facetT *facet);
    +void    qh_initbuild(void);
    +void    qh_initialhull(setT *vertices);
    +setT   *qh_initialvertices(int dim, setT *maxpoints, pointT *points, int numpoints);
    +vertexT *qh_isvertex(pointT *point, setT *vertices);
    +vertexT *qh_makenewfacets(pointT *point /*horizon_list, visible_list*/);
    +void    qh_matchduplicates(facetT *atfacet, int atskip, int hashsize, int *hashcount);
    +void    qh_nearcoplanar(void /* qh.facet_list */);
    +vertexT *qh_nearvertex(facetT *facet, pointT *point, realT *bestdistp);
    +int     qh_newhashtable(int newsize);
    +vertexT *qh_newvertex(pointT *point);
    +ridgeT *qh_nextridge3d(ridgeT *atridge, facetT *facet, vertexT **vertexp);
    +void    qh_outcoplanar(void /* facet_list */);
    +pointT *qh_point(int id);
    +void    qh_point_add(setT *set, pointT *point, void *elem);
    +setT   *qh_pointfacet(void /*qh.facet_list*/);
    +setT   *qh_pointvertex(void /*qh.facet_list*/);
    +void    qh_prependfacet(facetT *facet, facetT **facetlist);
    +void    qh_printhashtable(FILE *fp);
    +void    qh_printlists(void);
    +void    qh_resetlists(boolT stats, boolT resetVisible /*qh.newvertex_list qh.newfacet_list qh.visible_list*/);
    +void    qh_setvoronoi_all(void);
    +void    qh_triangulate(void /*qh.facet_list*/);
    +void    qh_triangulate_facet(facetT *facetA, vertexT **first_vertex);
    +void    qh_triangulate_link(facetT *oldfacetA, facetT *facetA, facetT *oldfacetB, facetT *facetB);
    +void    qh_triangulate_mirror(facetT *facetA, facetT *facetB);
    +void    qh_triangulate_null(facetT *facetA);
    +void    qh_vertexintersect(setT **vertexsetA,setT *vertexsetB);
    +setT   *qh_vertexintersect_new(setT *vertexsetA,setT *vertexsetB);
    +void    qh_vertexneighbors(void /*qh.facet_list*/);
    +boolT   qh_vertexsubset(setT *vertexsetA, setT *vertexsetB);
    +
    +
    +#endif /* qhDEFpoly */
    diff --git a/xs/src/qhull/src/libqhull/poly2.c b/xs/src/qhull/src/libqhull/poly2.c
    new file mode 100644
    index 000000000..de3e6ad0b
    --- /dev/null
    +++ b/xs/src/qhull/src/libqhull/poly2.c
    @@ -0,0 +1,3222 @@
    +/*
      ---------------------------------
    +
    +   poly2.c
    +   implements polygons and simplices
    +
    +   see qh-poly.htm, poly.h and libqhull.h
    +
    +   frequently used code is in poly.c
    +
    +   Copyright (c) 1993-2015 The Geometry Center.
    +   $Id: //main/2015/qhull/src/libqhull/poly2.c#11 $$Change: 2069 $
    +   $DateTime: 2016/01/18 22:05:03 $$Author: bbarber $
    +*/
    +
    +#include "qhull_a.h"
    +
    +/*======== functions in alphabetical order ==========*/
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  qh_check_bestdist()
    +    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(void) {
    +  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 ferr, 1020, "qh_check_bestdist: check points below nearest facet.  Facet_list f%d\n",
    +      qh facet_list->id));
    +  maxoutside= qh_maxouter();
    +  maxoutside += qh DISTround;
    +  /* one more qh.DISTround for check computation */
    +  trace1((qh ferr, 1021, "qh_check_bestdist: check that all points are within %2.2g of best facet\n", maxoutside));
    +  facets= qh_pointfacet(/*qh.facet_list*/);
    +  if (!qh_QUICKhelp && qh PRINTprecision)
    +    qh_fprintf(qh ferr, 8091, "\n\
    +qhull output completed.  Verifying that %d points are\n\
    +below %2.2g of the nearest %sfacet.\n",
    +             qh_setsize(facets), maxoutside, (qh ONLYgood ?  "good " : ""));
    +  FOREACHfacet_i_(facets) {  /* for each point with facet assignment */
    +    if (facet)
    +      unassigned= False;
    +    else {
    +      unassigned= True;
    +      facet= qh facet_list;
    +    }
    +    point= qh_point(facet_i);
    +    if (point == qh GOODpointp)
    +      continue;
    +    qh_distplane(point, facet, &dist);
    +    numpart++;
    +    bestfacet= qh_findbesthorizon(!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(point, bestfacet, &dist, &facetlist))
    +               && dist > maxoutside))
    +        notgood++;
    +      else {
    +        waserror= True;
    +        qh_fprintf(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(&facets);
    +  if (notverified && !qh DELAUNAY && !qh_QUICKhelp && qh PRINTprecision)
    +    qh_fprintf(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 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_ERRprec, errfacet1, errfacet2);
    +  }else if (waserror && qh outside_err > REALmax/2)
    +    qh_errexit2(qh_ERRprec, errfacet1, errfacet2);
    +  /* else if waserror, the error was logged to qh.ferr but does not effect the output */
    +  trace0((qh ferr, 20, "qh_check_bestdist: max distance outside %2.2g\n", maxdist));
    +} /* check_bestdist */
    +
    +/*---------------------------------
    +
    +  qh_check_dupridge(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(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(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 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 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 ferr, 8145, "- A bounding box for the input sites may alleviate this error.\n");
    +    if(minvertex > qh_WIDEduplicate*prevdist)
    +      qh_fprintf(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_ERRqhull, facet1, facet2);
    +  }
    +} /* check_dupridge */
    +#endif
    +
    +/*---------------------------------
    +
    +  qh_check_maxout()
    +    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(void) {
    +  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 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 ferr, 1023, "qh_check_maxout: determine actual maxoutside and minvertex\n"));
    +    vertices= qh_pointvertex(/*qh.facet_list*/);
    +    FORALLvertices {
    +      FOREACHneighbor_(vertex) {
    +        zinc_(Zdistvertex);  /* distance also computed by main loop below */
    +        qh_distplane(vertex->point, neighbor, &dist);
    +        minimize_(minvertex, dist);
    +        if (-dist > qh TRACEdist || dist > qh TRACEdist
    +        || neighbor == qh tracefacet || vertex == qh tracevertex)
    +          qh_fprintf(qh ferr, 8093, "qh_check_maxout: p%d(v%d) is %.2g from f%d\n",
    +                    qh_pointid(vertex->point), vertex->id, dist, neighbor->id);
    +      }
    +    }
    +    if (qh MERGING) {
    +      wmin_(Wminvertex, qh min_vertex);
    +    }
    +    qh min_vertex= minvertex;
    +    qh_settempfree(&vertices);
    +  }
    +  facets= qh_pointfacet(/*qh.facet_list*/);
    +  do {
    +    old_maxoutside= fmax_(qh max_outside, maxoutside);
    +    FOREACHfacet_i_(facets) {     /* for each point with facet assignment */
    +      if (facet) {
    +        point= qh_point(facet_i);
    +        if (point == qh GOODpointp)
    +          continue;
    +        zzinc_(Ztotcheck);
    +        qh_distplane(point, facet, &dist);
    +        numpart++;
    +        bestfacet= qh_findbesthorizon(qh_IScheckmax, point, facet, !qh_NOupper, &dist, &numpart);
    +        if (bestfacet && dist > maxoutside) {
    +          if (qh ONLYgood && !bestfacet->good
    +          && !((bestfacet= qh_findgooddist(point, bestfacet, &dist, &facetlist))
    +               && dist > maxoutside))
    +            notgood++;
    +          else
    +            maxoutside= dist;
    +        }
    +        if (dist > qh TRACEdist || (bestfacet && bestfacet == qh tracefacet))
    +          qh_fprintf(qh ferr, 8094, "qh_check_maxout: p%d is %.2g above f%d\n",
    +                     qh_pointid(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(&facets);
    +  wval_(Wmaxout)= maxoutside - qh max_outside;
    +  wmax_(Wmaxoutside, qh max_outside);
    +  qh max_outside= maxoutside;
    +  qh_nearcoplanar(/*qh.facet_list*/);
    +  qh maxoutdone= True;
    +  trace1((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(void) {
    +}
    +#endif
    +
    +/*---------------------------------
    +
    +  qh_check_output()
    +    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(void) {
    +  int i;
    +
    +  if (qh STOPcone)
    +    return;
    +  if (qh VERIFYoutput | qh IStracing | qh CHECKfrequently) {
    +    qh_checkpolygon(qh facet_list);
    +    qh_checkflipped_all(qh facet_list);
    +    qh_checkconvex(qh facet_list, qh_ALGORITHMfault);
    +  }else if (!qh MERGING && qh_newstats(qhstat precision, &i)) {
    +    qh_checkflipped_all(qh facet_list);
    +    qh_checkconvex(qh facet_list, qh_ALGORITHMfault);
    +  }
    +} /* check_output */
    +
    +
    +
    +/*---------------------------------
    +
    +  qh_check_point( point, facet, maxoutside, maxdist, errfacet1, errfacet2 )
    +    check that point is less than maxoutside from facet
    +*/
    +void qh_check_point(pointT *point, facetT *facet, realT *maxoutside, realT *maxdist, facetT **errfacet1, facetT **errfacet2) {
    +  realT dist;
    +
    +  /* occurs after statistics reported */
    +  qh_distplane(point, facet, &dist);
    +  if (dist > *maxoutside) {
    +    if (*errfacet1 != facet) {
    +      *errfacet2= *errfacet1;
    +      *errfacet1= facet;
    +    }
    +    qh_fprintf(qh ferr, 6111, "qhull precision error: point p%d is outside facet f%d, distance= %6.8g maxoutside= %6.8g\n",
    +              qh_pointid(point), facet->id, dist, *maxoutside);
    +  }
    +  maximize_(*maxdist, dist);
    +} /* qh_check_point */
    +
    +
    +/*---------------------------------
    +
    +  qh_check_points()
    +    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(void) {
    +  facetT *facet, *errfacet1= NULL, *errfacet2= NULL;
    +  realT total, maxoutside, maxdist= -REALmax;
    +  pointT *point, **pointp, *pointtemp;
    +  boolT testouter;
    +
    +  maxoutside= qh_maxouter();
    +  maxoutside += qh DISTround;
    +  /* one more qh.DISTround for check computation */
    +  trace1((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 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();
    +  }else {
    +    if (qh_MAXoutside && qh maxoutdone)
    +      testouter= True;
    +    else
    +      testouter= False;
    +    if (!qh_QUICKhelp) {
    +      if (qh MERGEexact)
    +        qh_fprintf(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 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 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 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 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(point, facet, &maxoutside, &maxdist, &errfacet1, &errfacet2);
    +      }
    +      FOREACHpoint_(qh other_points) {
    +        if (point != qh GOODpointp)
    +          qh_check_point(point, facet, &maxoutside, &maxdist, &errfacet1, &errfacet2);
    +      }
    +    }
    +    if (maxdist > qh outside_err) {
    +      qh_fprintf(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_ERRprec, errfacet1, errfacet2 );
    +    }else if (errfacet1 && qh outside_err > REALmax/2)
    +        qh_errexit2( qh_ERRprec, errfacet1, errfacet2 );
    +    /* else if errfacet1, the error was logged to qh.ferr but does not effect the output */
    +    trace0((qh ferr, 21, "qh_check_points: max distance outside %2.2g\n", maxdist));
    +  }
    +} /* check_points */
    +
    +
    +/*---------------------------------
    +
    +  qh_checkconvex( 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(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 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("flipped facet");
    +      qh_fprintf(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(vertex->point, neighbor, &dist);
    +        if (dist > -qh DISTround) {
    +          if (fault == qh_DATAfault) {
    +            qh_precision("coplanar or concave ridge");
    +            qh_fprintf(qh ferr, 6114, "qhull precision error: initial simplex is not convex. Distance=%.2g\n", dist);
    +            qh_errexit(qh_ERRsingular, NULL, NULL);
    +          }
    +          if (dist > qh DISTround) {
    +            zzinc_(Zconcaveridges);
    +            qh_precision("concave ridge");
    +            qh_fprintf(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(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("coplanar ridge");
    +              qh_fprintf(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(vertex->point), vertex->id, dist);
    +              errfacet1= facet;
    +              errfacet2= neighbor;
    +              waserror= True;
    +            }
    +          }else {
    +            zzinc_(Zcoplanarridges);
    +            qh_precision("coplanar ridge");
    +            trace0((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(vertex->point), vertex->id, dist, qh furthest_id));
    +          }
    +        }
    +      }
    +    }
    +    if (!allsimplicial) {
    +      if (qh CENTERtype == qh_AScentrum) {
    +        if (!facet->center)
    +          facet->center= qh_getcentrum(facet);
    +        centrum= facet->center;
    +      }else {
    +        if (!centrum_warning && (!facet->simplicial || facet->tricoplanar)) {
    +           centrum_warning= True;
    +           qh_fprintf(qh ferr, 7062, "qhull warning: recomputing centrums for convexity test.  This may lead to false, precision errors.\n");
    +        }
    +        centrum= qh_getcentrum(facet);
    +        tempcentrum= True;
    +      }
    +      FOREACHneighbor_(facet) {
    +        if (qh ZEROcentrum && facet->simplicial && neighbor->simplicial)
    +          continue;
    +        if (facet->tricoplanar || neighbor->tricoplanar)
    +          continue;
    +        zzinc_(Zdistconvex);
    +        qh_distplane(centrum, neighbor, &dist);
    +        if (dist > qh DISTround) {
    +          zzinc_(Zconcaveridges);
    +          qh_precision("concave ridge");
    +          qh_fprintf(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("coplanar ridge");
    +          qh_fprintf(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(centrum, qh normal_size);
    +    }
    +  }
    +  if (waserror && !qh FORCEoutput)
    +    qh_errexit2(qh_ERRprec, errfacet1, errfacet2);
    +} /* checkconvex */
    +
    +
    +/*---------------------------------
    +
    +  qh_checkfacet( facet, newmerge, waserror )
    +    checks for consistency errors in facet
    +    newmerge set if from merge.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(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 ferr, 6119, "qhull internal error (qh_checkfacet): facet f%d is on the visible_list\n",
    +      facet->id);
    +    qh_errexit(qh_ERRqhull, facet, NULL);
    +  }
    +  if (!facet->normal) {
    +    qh_fprintf(qh ferr, 6120, "qhull internal error (qh_checkfacet): facet f%d does not have  a normal\n",
    +      facet->id);
    +    waserror= True;
    +  }
    +  qh_setcheck(facet->vertices, "vertices for f", facet->id);
    +  qh_setcheck(facet->ridges, "ridges for f", facet->id);
    +  qh_setcheck(facet->outsideset, "outsideset for f", facet->id);
    +  qh_setcheck(facet->coplanarset, "coplanarset for f", facet->id);
    +  qh_setcheck(facet->neighbors, "neighbors for f", facet->id);
    +  FOREACHvertex_(facet->vertices) {
    +    if (vertex->deleted) {
    +      qh_fprintf(qh ferr, 6121, "qhull internal error (qh_checkfacet): deleted vertex v%d in f%d\n", vertex->id, facet->id);
    +      qh_errprint("ERRONEOUS", NULL, NULL, NULL, vertex);
    +      waserror= True;
    +    }
    +    if (vertex->id >= previousid) {
    +      qh_fprintf(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(facet->neighbors);
    +  numvertices= qh_setsize(facet->vertices);
    +  numridges= qh_setsize(facet->ridges);
    +  if (facet->simplicial) {
    +    if (numvertices+numneighbors != 2*qh hull_dim
    +    && !facet->degenerate && !facet->redundant) {
    +      qh_fprintf(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 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 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 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 ferr, 6126, "qhull internal error (qh_checkfacet): facet f%d still has a MERGE or DUP neighbor\n", facet->id);
    +      qh_errexit(qh_ERRqhull, facet, NULL);
    +    }
    +    neighbor->seen= True;
    +  }
    +  FOREACHneighbor_(facet) {
    +    if (!qh_setin(neighbor->neighbors, facet)) {
    +      qh_fprintf(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 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(ridge->vertices, "vertices for r", ridge->id);
    +    ridge->seen= False;
    +  }
    +  FOREACHridge_(facet->ridges) {
    +    if (ridge->seen) {
    +      qh_fprintf(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(ridge->vertices);
    +    if (numRvertices != qh hull_dim - 1) {
    +      qh_fprintf(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 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 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(facet->vertices, neighbor->vertices);
    +      qh_settemppush(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 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_ERRqhull, facet, ridge);
    +          }
    +          vertex->seen2= True;
    +        }
    +      }
    +      if (!newmerge) {
    +        FOREACHvertex_(intersection) {
    +          if (!vertex->seen2) {
    +            if (qh IStracing >=3 || !qh MERGING) {
    +              qh_fprintf(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("ERRONEOUS", facet, neighbor, NULL, vertex);
    +                if (!qh MERGING)
    +                  qh_errexit(qh_ERRqhull, NULL, NULL);
    +              }
    +            }
    +          }
    +        }
    +      }
    +      qh_settempfree(&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 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_(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 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("ERRONEOUS", facet, errother, errridge, NULL);
    +    *waserrorp= True;
    +  }
    +} /* checkfacet */
    +
    +
    +/*---------------------------------
    +
    +  qh_checkflipped_all( facetlist )
    +    checks orientation of facets in list against interior point
    +*/
    +void qh_checkflipped_all(facetT *facetlist) {
    +  facetT *facet;
    +  boolT waserror= False;
    +  realT dist;
    +
    +  if (facetlist == qh facet_list)
    +    zzval_(Zflippedfacets)= 0;
    +  FORALLfacet_(facetlist) {
    +    if (facet->normal && !qh_checkflipped(facet, &dist, !qh_ALL)) {
    +      qh_fprintf(qh ferr, 6136, "qhull precision error: facet f%d is flipped, distance= %6.12g\n",
    +              facet->id, dist);
    +      if (!qh FORCEoutput) {
    +        qh_errprint("ERRONEOUS", facet, NULL, NULL, NULL);
    +        waserror= True;
    +      }
    +    }
    +  }
    +  if (waserror) {
    +    qh_fprintf(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_ERRprec, NULL, NULL);
    +  }
    +} /* checkflipped_all */
    +
    +/*---------------------------------
    +
    +  qh_checkpolygon( 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(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 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(facet->outsideset)) {
    +          if (!qh NARROWhull
    +#if !qh_COMPUTEfurthest
    +               || facet->furthestdist >= qh MINoutside
    +#endif
    +                        ) {
    +            qh_fprintf(qh ferr, 6137, "qhull internal error (qh_checkpolygon): f%d has outside points before qh facet_next\n",
    +                     facet->id);
    +            qh_errexit(qh_ERRqhull, facet, NULL);
    +          }
    +        }
    +      }
    +      numfacets++;
    +      qh_checkfacet(facet, False, &waserror);
    +    }
    +  }
    +  if (qh visible_list && !visibleseen && facetlist == qh facet_list) {
    +    qh_fprintf(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_errexit(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(facet->ridges);
    +    FOREACHvertex_(facet->vertices) {
    +      vertex->visitid++;
    +      if (!vertex->seen) {
    +        vertex->seen= True;
    +        numvertices++;
    +        if (qh_pointid(vertex->point) == qh_IDunknown) {
    +          qh_fprintf(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 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(vertex->neighbors, "neighbors for v", vertex->id);
    +        if (vertex->deleted)
    +          continue;
    +        totvneighbors += qh_setsize(vertex->neighbors);
    +      }
    +      FORALLfacet_(facetlist)
    +        totvertices += qh_setsize(facet->vertices);
    +      if (totvneighbors != totvertices) {
    +        qh_fprintf(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 del_vertices)) {
    +      qh_fprintf(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 del_vertices));
    +      waserror= True;
    +    }
    +    if (qh hull_dim == 2 && numvertices != numfacets) {
    +      qh_fprintf(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 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_ERRqhull, NULL, NULL);
    +} /* checkpolygon */
    +
    +
    +/*---------------------------------
    +
    +  qh_checkvertex( vertex )
    +    check vertex for consistency
    +    checks vertex->neighbors
    +
    +  notes:
    +    neighbors checked efficiently in checkpolygon
    +*/
    +void qh_checkvertex(vertexT *vertex) {
    +  boolT waserror= False;
    +  facetT *neighbor, **neighborp, *errfacet=NULL;
    +
    +  if (qh_pointid(vertex->point) == qh_IDunknown) {
    +    qh_fprintf(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 ferr, 6145, "qhull internal error (qh_checkvertex): unknown vertex id %d\n", vertex->id);
    +    waserror= True;
    +  }
    +  if (!waserror && !vertex->deleted) {
    +    if (qh_setsize(vertex->neighbors)) {
    +      FOREACHneighbor_(vertex) {
    +        if (!qh_setin(neighbor->vertices, vertex)) {
    +          qh_fprintf(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("ERRONEOUS", NULL, NULL, NULL, vertex);
    +    qh_errexit(qh_ERRqhull, errfacet, NULL);
    +  }
    +} /* checkvertex */
    +
    +/*---------------------------------
    +
    +  qh_clearcenters( type )
    +    clear old data from facet->center
    +
    +  notes:
    +    sets new centertype
    +    nop if CENTERtype is the same
    +*/
    +void qh_clearcenters(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(facet->center, qh center_size);
    +          facet->center= NULL;
    +        }
    +      }else /* qh.CENTERtype == qh_AScentrum */ {
    +        if (facet->center) {
    +          qh_memfree(facet->center, qh normal_size);
    +          facet->center= NULL;
    +        }
    +      }
    +    }
    +    qh CENTERtype= type;
    +  }
    +  trace2((qh ferr, 2043, "qh_clearcenters: switched to center type %d\n", type));
    +} /* clearcenters */
    +
    +/*---------------------------------
    +
    +  qh_createsimplex( 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(setT *vertices) {
    +  facetT *facet= NULL, *newfacet;
    +  boolT toporient= True;
    +  int vertex_i, vertex_n, nth;
    +  setT *newfacets= qh_settemp(qh hull_dim+1);
    +  vertexT *vertex;
    +
    +  qh facet_list= qh newfacet_list= qh facet_tail= qh_newfacet();
    +  qh num_facets= qh num_vertices= qh num_visible= 0;
    +  qh vertex_list= qh newvertex_list= qh vertex_tail= qh_newvertex(NULL);
    +  FOREACHvertex_i_(vertices) {
    +    newfacet= qh_newfacet();
    +    newfacet->vertices= qh_setnew_delnthsorted(vertices, vertex_n,
    +                                                vertex_i, 0);
    +    newfacet->toporient= (unsigned char)toporient;
    +    qh_appendfacet(newfacet);
    +    newfacet->newfacet= True;
    +    qh_appendvertex(vertex);
    +    qh_setappend(&newfacets, newfacet);
    +    toporient ^= True;
    +  }
    +  FORALLnew_facets {
    +    nth= 0;
    +    FORALLfacet_(qh newfacet_list) {
    +      if (facet != newfacet)
    +        SETelem_(newfacet->neighbors, nth++)= facet;
    +    }
    +    qh_settruncate(newfacet->neighbors, qh hull_dim);
    +  }
    +  qh_settempfree(&newfacets);
    +  trace1((qh ferr, 1028, "qh_createsimplex: created simplex\n"));
    +} /* createsimplex */
    +
    +/*---------------------------------
    +
    +  qh_delridge( ridge )
    +    deletes ridge from data structures it belongs to
    +    frees up its memory
    +
    +  notes:
    +    in merge.c, caller sets vertex->delridge for each vertex
    +    ridges also freed in qh_freeqhull
    +*/
    +void qh_delridge(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(&(ridge->vertices));
    +  qh_memfree_(ridge, (int)sizeof(ridgeT), freelistp);
    +} /* delridge */
    +
    +
    +/*---------------------------------
    +
    +  qh_delvertex( vertex )
    +    deletes a vertex and frees its memory
    +
    +  notes:
    +    assumes vertex->adjacencies have been updated if needed
    +    unlinks from vertex_list
    +*/
    +void qh_delvertex(vertexT *vertex) {
    +
    +  if (vertex == qh tracevertex)
    +    qh tracevertex= NULL;
    +  qh_removevertex(vertex);
    +  qh_setfree(&vertex->neighbors);
    +  qh_memfree(vertex, (int)sizeof(vertexT));
    +} /* delvertex */
    +
    +
    +/*---------------------------------
    +
    +  qh_facet3vertex(  )
    +    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(facetT *facet) {
    +  ridgeT *ridge, *firstridge;
    +  vertexT *vertex;
    +  int cntvertices, cntprojected=0;
    +  setT *vertices;
    +
    +  cntvertices= qh_setsize(facet->vertices);
    +  vertices= qh_settemp(cntvertices);
    +  if (facet->simplicial) {
    +    if (cntvertices != 3) {
    +      qh_fprintf(qh ferr, 6147, "qhull internal error (qh_facet3vertex): only %d vertices for simplicial facet f%d\n",
    +                  cntvertices, facet->id);
    +      qh_errexit(qh_ERRqhull, facet, NULL);
    +    }
    +    qh_setappend(&vertices, SETfirst_(facet->vertices));
    +    if (facet->toporient ^ qh_ORIENTclock)
    +      qh_setappend(&vertices, SETsecond_(facet->vertices));
    +    else
    +      qh_setaddnth(&vertices, 0, SETsecond_(facet->vertices));
    +    qh_setappend(&vertices, SETelem_(facet->vertices, 2));
    +  }else {
    +    ridge= firstridge= SETfirstt_(facet->ridges, ridgeT);   /* no infinite */
    +    while ((ridge= qh_nextridge3d(ridge, facet, &vertex))) {
    +      qh_setappend(&vertices, vertex);
    +      if (++cntprojected > cntvertices || ridge == firstridge)
    +        break;
    +    }
    +    if (!ridge || cntprojected != cntvertices) {
    +      qh_fprintf(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_ERRqhull, facet, ridge);
    +    }
    +  }
    +  return vertices;
    +} /* facet3vertex */
    +
    +/*---------------------------------
    +
    +  qh_findbestfacet( 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 locate a facet with qh_findbestfacet()
    +
    +    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:
    +    qh_findbest
    +*/
    +facetT *qh_findbestfacet(pointT *point, boolT bestoutside,
    +           realT *bestdist, boolT *isoutside) {
    +  facetT *bestfacet= NULL;
    +  int numpart, totpart= 0;
    +
    +  bestfacet= qh_findbest(point, qh facet_list,
    +                            bestoutside, !qh_ISnewfacets, bestoutside /* qh_NOupper */,
    +                            bestdist, isoutside, &totpart);
    +  if (*bestdist < -qh DISTround) {
    +    bestfacet= qh_findfacet_all(point, bestdist, isoutside, &numpart);
    +    totpart += numpart;
    +    if ((isoutside && *isoutside && bestoutside)
    +    || (isoutside && !*isoutside && bestfacet->upperdelaunay)) {
    +      bestfacet= qh_findbest(point, bestfacet,
    +                            bestoutside, False, bestoutside,
    +                            bestdist, isoutside, &totpart);
    +      totpart += numpart;
    +    }
    +  }
    +  trace3((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 */
    +
    +/*---------------------------------
    +
    +  qh_findbestlower( 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(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(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(upperfacet, point, &dist);
    +    qh_vertexneighbors();
    +    FOREACHneighbor_(vertex) {
    +      if (neighbor->upperdelaunay || neighbor->flipped)
    +        continue;
    +      (*numpart)++;
    +      qh_distplane(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 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(point, &bestdist, &isoutside, numpart);
    +  }
    +  *bestdistp= bestdist;
    +  trace3((qh ferr, 3015, "qh_findbestlower: f%d dist %2.2g for f%d p%d\n",
    +          bestfacet->id, bestdist, upperfacet->id, qh_pointid(point)));
    +  return bestfacet;
    +} /* findbestlower */
    +
    +/*---------------------------------
    +
    +  qh_findfacet_all( 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(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(point, facet, &dist);
    +    if (dist > *bestdist) {
    +      *bestdist= dist;
    +      bestfacet= facet;
    +      if (dist > qh MINoutside) {
    +        *isoutside= True;
    +        break;
    +      }
    +    }
    +  }
    +  *numpart= totpart;
    +  trace3((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 */
    +
    +/*---------------------------------
    +
    +  qh_findgood( 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(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 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(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 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 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 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 */
    +
    +/*---------------------------------
    +
    +  qh_findgood_all( 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(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 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 ferr, 7064, "qhull warning: good vertex p%d does not match last good facet f%d.  Ignored.\n",
    +               qh_pointid(qh GOODvertexp), facet->id);
    +            return;
    +          }else if (qh GOODvertex > 0)
    +            qh_fprintf(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 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(facet->normal, &angle)) {
    +          facet->good= False;
    +          numgood--;
    +          if (angle < bestangle) {
    +            bestangle= angle;
    +            bestfacet= facet;
    +          }
    +        }
    +      }
    +    }
    +    if (!numgood && bestfacet) {
    +      bestfacet->good= True;
    +      numgood++;
    +      trace0((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 ferr, 24, "qh_findgood_all: %d good facets remain out of %d facets\n",
    +        numgood, startgood));
    +} /* findgood_all */
    +
    +/*---------------------------------
    +
    +  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(void /* 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(furthest, facet, &dist);
    +#else
    +      dist= facet->furthestdist;
    +#endif
    +      if (dist > bestdist) {
    +        bestfacet= facet;
    +        bestdist= dist;
    +      }
    +    }
    +  }
    +  if (bestfacet) {
    +    qh_removefacet(bestfacet);
    +    qh_prependfacet(bestfacet, &qh facet_next);
    +    trace1((qh ferr, 1029, "qh_furthestnext: made f%d next facet(dist %.2g)\n",
    +            bestfacet->id, bestdist));
    +  }
    +} /* furthestnext */
    +
    +/*---------------------------------
    +
    +  qh_furthestout( 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(facetT *facet) {
    +  pointT *point, **pointp, *bestpoint= NULL;
    +  realT dist, bestdist= -REALmax;
    +
    +  FOREACHpoint_(facet->outsideset) {
    +    qh_distplane(point, facet, &dist);
    +    zinc_(Zcomputefurthest);
    +    if (dist > bestdist) {
    +      bestpoint= point;
    +      bestdist= dist;
    +    }
    +  }
    +  if (bestpoint) {
    +    qh_setdel(facet->outsideset, point);
    +    qh_setappend(&facet->outsideset, point);
    +#if !qh_COMPUTEfurthest
    +    facet->furthestdist= bestdist;
    +#endif
    +  }
    +  facet->notfurthest= False;
    +  trace3((qh ferr, 3017, "qh_furthestout: p%d is furthest outside point of f%d\n",
    +          qh_pointid(point), facet->id));
    +} /* furthestout */
    +
    +
    +/*---------------------------------
    +
    +  qh_infiniteloop( facet )
    +    report infinite loop error due to facet
    +*/
    +void qh_infiniteloop(facetT *facet) {
    +
    +  qh_fprintf(qh ferr, 6149, "qhull internal error (qh_infiniteloop): potential infinite loop detected\n");
    +  qh_errexit(qh_ERRqhull, facet, NULL);
    +} /* qh_infiniteloop */
    +
    +/*---------------------------------
    +
    +  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( void) {
    +  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 GOODpoint-1);
    +  else if (qh GOODpoint < 0)
    +    qh GOODpointp= qh_point(-qh GOODpoint-1);
    +  if (qh GOODvertex > 0)
    +    qh GOODvertexp= qh_point(qh GOODvertex-1);
    +  else if (qh GOODvertex < 0)
    +    qh GOODvertexp= qh_point(-qh GOODvertex-1);
    +  if ((qh GOODpoint
    +       && (qh GOODpointp < qh first_point  /* also catches !GOODpointp */
    +           || qh GOODpointp > qh_point(qh num_points-1)))
    +    || (qh GOODvertex
    +        && (qh GOODvertexp < qh first_point  /* also catches !GOODvertexp */
    +            || qh GOODvertexp > qh_point(qh num_points-1)))) {
    +    qh_fprintf(qh ferr, 6150, "qhull input error: either QGn or QVn point is > p%d\n",
    +             qh num_points-1);
    +    qh_errexit(qh_ERRinput, NULL, NULL);
    +  }
    +  maxpoints= qh_maxmin(qh first_point, qh num_points, qh hull_dim);
    +  if (qh SCALElast)
    +    qh_scalelast(qh first_point, qh num_points, qh hull_dim,
    +               qh MINlastcoord, qh MAXlastcoord, qh MAXwidth);
    +  qh_detroundoff();
    +  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 hull_dim, maxpoints, qh first_point, qh num_points);
    +  qh_initialhull(vertices);  /* initial qh facet_list */
    +  qh_partitionall(vertices, qh first_point, qh num_points);
    +  if (qh PRINToptions1st || qh TRACElevel || qh IStracing) {
    +    if (qh TRACElevel || qh IStracing)
    +      qh_fprintf(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 ferr, 8104, "Options selected for Qhull %s:\n%s\n", qh_version, qh qhull_options);
    +  }
    +  qh_resetlists(False, qh_RESETvisible /*qh.visible_list newvertex_list newfacet_list */);
    +  qh facet_next= qh facet_list;
    +  qh_furthestnext(/* 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 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_ERRinput, NULL, NULL);
    +    }
    +    if (!(qh GOODthreshold || qh GOODpoint
    +         || (!qh MERGEexact && !qh PREmerge && qh GOODvertexp))) {
    +      qh_fprintf(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_ERRinput, NULL, NULL);
    +    }
    +    if (qh GOODvertex > 0  && !qh MERGING  /* matches qh_partitionall */
    +        && !qh_isvertex(qh GOODvertexp, vertices)) {
    +      facet= qh_findbestnew(qh GOODvertexp, qh facet_list,
    +                          &dist, !qh_ALL, &isoutside, &numpart);
    +      zadd_(Zdistgood, numpart);
    +      if (!isoutside) {
    +        qh_fprintf(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 GOODvertexp));
    +        qh_errexit(qh_ERRinput, NULL, NULL);
    +      }
    +      if (!qh_addpoint(qh GOODvertexp, facet, False)) {
    +        qh_settempfree(&vertices);
    +        qh_settempfree(&maxpoints);
    +        return;
    +      }
    +    }
    +    qh_findgood(qh facet_list, 0);
    +  }
    +  qh_settempfree(&vertices);
    +  qh_settempfree(&maxpoints);
    +  trace1((qh ferr, 1030, "qh_initbuild: initial hull created and points partitioned\n"));
    +} /* initbuild */
    +
    +/*---------------------------------
    +
    +  qh_initialhull( 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(setT *vertices) {
    +  facetT *facet, *firstfacet, *neighbor, **neighborp;
    +  realT dist, angle, minangle= REALmax;
    +#ifndef qh_NOtrace
    +  int k;
    +#endif
    +
    +  qh_createsimplex(vertices);  /* qh.facet_list */
    +  qh_resetlists(False, qh_RESETvisible);
    +  qh facet_next= qh facet_list;      /* advance facet when processed */
    +  qh interior_point= qh_getcenter(vertices);
    +  firstfacet= qh facet_list;
    +  qh_setfacetplane(firstfacet);
    +  zinc_(Znumvisibility); /* needs to be in printsummary */
    +  qh_distplane(qh interior_point, firstfacet, &dist);
    +  if (dist > 0) {
    +    FORALLfacets
    +      facet->toporient ^= (unsigned char)True;
    +  }
    +  FORALLfacets
    +    qh_setfacetplane(facet);
    +  FORALLfacets {
    +    if (!qh_checkflipped(facet, NULL, qh_ALL)) {/* due to axis-parallel facet */
    +      trace1((qh ferr, 1031, "qh_initialhull: initial orientation incorrect.  Correct all facets\n"));
    +      facet->flipped= False;
    +      FORALLfacets {
    +        facet->toporient ^= (unsigned char)True;
    +        qh_orientoutside(facet);
    +      }
    +      break;
    +    }
    +  }
    +  FORALLfacets {
    +    if (!qh_checkflipped(facet, NULL, !qh_ALL)) {  /* can happen with 'R0.1' */
    +      if (qh DELAUNAY && ! qh ATinfinity) {
    +        if (qh UPPERdelaunay)
    +          qh_fprintf(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 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_ERRinput, NULL, NULL);
    +      }
    +      qh_precision("initial simplex is flat");
    +      qh_fprintf(qh ferr, 6154, "Qhull precision error: Initial simplex is flat (facet %d is coplanar with the interior point)\n",
    +                 facet->id);
    +      qh_errexit(qh_ERRsingular, NULL, NULL);  /* calls qh_printhelp_singular */
    +    }
    +    FOREACHneighbor_(facet) {
    +      angle= qh_getangle(facet->normal, neighbor->normal);
    +      minimize_( minangle, angle);
    +    }
    +  }
    +  if (minangle < qh_MAXnarrow && !qh NOnarrow) {
    +    realT diff= 1.0 + minangle;
    +
    +    qh NARROWhull= True;
    +    qh_option("_narrow-hull", NULL, &diff);
    +    if (minangle < qh_WARNnarrow && !qh RERUN && qh PRINTprecision)
    +      qh_printhelp_narrowhull(qh ferr, minangle);
    +  }
    +  zzval_(Zprocessed)= qh hull_dim+1;
    +  qh_checkpolygon(qh facet_list);
    +  qh_checkconvex(qh facet_list,   qh_DATAfault);
    +#ifndef qh_NOtrace
    +  if (qh IStracing >= 1) {
    +    qh_fprintf(qh ferr, 8105, "qh_initialhull: simplex constructed, interior point:");
    +    for (k=0; k < qh hull_dim; k++)
    +      qh_fprintf(qh ferr, 8106, " %6.4g", qh interior_point[k]);
    +    qh_fprintf(qh ferr, 8107, "\n");
    +  }
    +#endif
    +} /* initialhull */
    +
    +/*---------------------------------
    +
    +  qh_initialvertices( 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(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(dim + 1);
    +  simplex= qh_settemp(dim+1);
    +  if (qh ALLpoints)
    +    qh_maxsimplex(dim, NULL, points, numpoints, &simplex);
    +  else if (qh RANDOMoutside) {
    +    while (qh_setsize(simplex) != dim+1) {
    +      randr= qh_RANDOMint;
    +      randr= randr/(qh_RANDOMmax+1);
    +      idx= (int)floor(qh num_points * randr);
    +      while (qh_setin(simplex, qh_point(idx))) {
    +        idx++; /* in case qh_RANDOMint always returns the same value */
    +        idx= idx < qh num_points ? idx : 0;
    +      }
    +      qh_setappend(&simplex, qh_point(idx));
    +    }
    +  }else if (qh hull_dim >= qh_INITIALmax) {
    +    tested= qh_settemp(dim+1);
    +    qh_setappend(&simplex, SETfirst_(maxpoints));   /* max and min X coord */
    +    qh_setappend(&simplex, SETsecond_(maxpoints));
    +    qh_maxsimplex(fmin_(qh_INITIALsearch, dim), maxpoints, points, numpoints, &simplex);
    +    k= qh_setsize(simplex);
    +    FOREACHpoint_i_(maxpoints) {
    +      if (point_i & 0x1) {     /* first pick up max. coord. points */
    +        if (!qh_setin(simplex, point) && !qh_setin(tested, point)){
    +          qh_detsimplex(point, simplex, k, &nearzero);
    +          if (nearzero)
    +            qh_setappend(&tested, point);
    +          else {
    +            qh_setappend(&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(point, simplex, k, &nearzero);
    +        if (nearzero)
    +          qh_setappend(&tested, point);
    +        else {
    +          qh_setappend(&simplex, point);
    +          k++;
    +        }
    +      }
    +    }
    +    idx= 0;
    +    while (k != dim && (point= qh_point(idx++))) {
    +      if (!qh_setin(simplex, point) && !qh_setin(tested, point)){
    +        qh_detsimplex(point, simplex, k, &nearzero);
    +        if (!nearzero){
    +          qh_setappend(&simplex, point);
    +          k++;
    +        }
    +      }
    +    }
    +    qh_settempfree(&tested);
    +    qh_maxsimplex(dim, maxpoints, points, numpoints, &simplex);
    +  }else
    +    qh_maxsimplex(dim, maxpoints, points, numpoints, &simplex);
    +  FOREACHpoint_(simplex)
    +    qh_setaddnth(&vertices, 0, qh_newvertex(point)); /* descending order */
    +  qh_settempfree(&simplex);
    +  return vertices;
    +} /* initialvertices */
    +
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  qh_makenewfacets( 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(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(point);
    +  qh_appendvertex(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(visible, apex, &numnew);
    +    }
    +    if (visible->simplicial)
    +      newfacet= qh_makenew_simplicial(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 ferr, 1032, "qh_makenewfacets: created %d new facets from point p%d to horizon\n",
    +          numnew, qh_pointid(point)));
    +  if (qh IStracing >= 4)
    +    qh_printfacetlist(qh newfacet_list, NULL, qh_ALL);
    +  return apex;
    +} /* makenewfacets */
    +
    +/*---------------------------------
    +
    +  qh_matchduplicates( 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(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(hashsize, atfacet->vertices, qh hull_dim, 1,
    +                     SETelem_(atfacet->vertices, atskip));
    +  trace2((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(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 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_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 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(facet, newfacet, &low, &high);
    +            dist2= qh_getdistance(newfacet, facet, &low, &high);
    +            minimize_(mindist, dist2);
    +            if (mindist > maxdist) {
    +              maxdist= mindist;
    +              maxmatch= facet;
    +              maxskip= skip;
    +              maxmatch2= newfacet;
    +              maxskip2= newskip;
    +            }
    +            trace3((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 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_ERRqhull, newfacet, NULL);
    +        }
    +      }
    +    } /* end of for each new facet at hash */
    +    if (!makematch) {
    +      if (!maxmatch) {
    +        qh_fprintf(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_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 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("ridge with multiple neighbors");
    +      if (qh IStracing >= 4)
    +        qh_errprint("DUPLICATED/MATCH", maxmatch, maxmatch2, NULL, NULL);
    +    }
    +  }
    +} /* matchduplicates */
    +
    +/*---------------------------------
    +
    +  qh_nearcoplanar()
    +    for all facets, remove near-inside points from facet->coplanarset
    +    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()
    +  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(void /* qh.facet_list */) {
    +  facetT *facet;
    +  pointT *point, **pointp;
    +  int numpart;
    +  realT dist, innerplane;
    +
    +  if (!qh KEEPcoplanar && !qh KEEPinside) {
    +    FORALLfacets {
    +      if (facet->coplanarset)
    +        qh_setfree( &facet->coplanarset);
    +    }
    +  }else if (!qh KEEPcoplanar || !qh KEEPinside) {
    +    qh_outerinner(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(point, facet, &dist);
    +          if (dist < innerplane) {
    +            if (!qh KEEPinside)
    +              SETref_(point)= NULL;
    +          }else if (!qh KEEPcoplanar)
    +            SETref_(point)= NULL;
    +        }
    +        qh_setcompact(facet->coplanarset);
    +      }
    +    }
    +    zzadd_(Zcheckpart, numpart);
    +  }
    +} /* nearcoplanar */
    +
    +/*---------------------------------
    +
    +  qh_nearvertex( 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(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 ferr, 6158, "qhull internal error (qh_nearvertex): qh.VERTEXneighbors and facet->center required for tricoplanar facets\n");
    +      qh_errexit(qh_ERRqhull, facet, NULL);
    +    }
    +    vertices= qh_settemp(qh TEMPsize);
    +    apex= SETfirstt_(facet->vertices, vertexT);
    +    center= facet->center;
    +    FOREACHneighbor_(apex) {
    +      if (neighbor->center == center) {
    +        FOREACHvertex_(neighbor->vertices)
    +          qh_setappend(&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(&vertices);
    +  *bestdistp= sqrt(bestdist);
    +  if (!bestvertex) {
    +      qh_fprintf(qh ferr, 6261, "qhull internal error (qh_nearvertex): did not find bestvertex for f%d p%d\n", facet->id, qh_pointid(point));
    +      qh_errexit(qh_ERRqhull, facet, NULL);
    +  }
    +  trace3((qh ferr, 3019, "qh_nearvertex: v%d dist %2.2g for f%d p%d\n",
    +        bestvertex->id, *bestdistp, facet->id, qh_pointid(point))); /* bestvertex!=0 by QH2161 */
    +  return bestvertex;
    +} /* nearvertex */
    +
    +/*---------------------------------
    +
    +  qh_newhashtable( 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(int newsize) {
    +  int size;
    +
    +  size= ((newsize+1)*qh_HASHfactor) | 0x1;  /* odd number */
    +  while (True) {
    +    if (newsize<0 || size<0) {
    +        qh_fprintf(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(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(size);
    +  qh_setzero(qh hash_table, 0, size);
    +  return size;
    +} /* newhashtable */
    +
    +/*---------------------------------
    +
    +  qh_newvertex( point )
    +    returns a new vertex for point
    +*/
    +vertexT *qh_newvertex(pointT *point) {
    +  vertexT *vertex;
    +
    +  zinc_(Ztotvertices);
    +  vertex= (vertexT *)qh_memalloc((int)sizeof(vertexT));
    +  memset((char *) vertex, (size_t)0, sizeof(vertexT));
    +  if (qh vertex_id == UINT_MAX) {
    +    qh_memfree(vertex, (int)sizeof(vertexT));
    +    qh_fprintf(qh ferr, 6159, "qhull error: more than 2^32 vertices.  vertexT.id field overflows.  Vertices would not be sorted correctly.\n");
    +    qh_errexit(qh_ERRqhull, NULL, NULL);
    +  }
    +  if (qh vertex_id == qh tracevertex_id)
    +    qh tracevertex= vertex;
    +  vertex->id= qh vertex_id++;
    +  vertex->point= point;
    +  trace4((qh ferr, 4060, "qh_newvertex: vertex p%d(v%d) created\n", qh_pointid(vertex->point),
    +          vertex->id));
    +  return(vertex);
    +} /* newvertex */
    +
    +/*---------------------------------
    +
    +  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 qh_qh.
    +
    +  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 qh_qh 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(facetT *atfacet, int atskip, int hashsize, int *hashcount) {
    +}
    +ridgeT *qh_nextridge3d(ridgeT *atridge, facetT *facet, vertexT **vertexp) {
    +
    +  return NULL;
    +}
    +#endif /* qh_NOmerge */
    +
    +/*---------------------------------
    +
    +  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(void /* facet_list */) {
    +  pointT *point, **pointp;
    +  facetT *facet;
    +  realT dist;
    +
    +  trace1((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(point, facet, &dist);
    +        zinc_(Zpartition);
    +        qh_partitioncoplanar(point, facet, &dist);
    +      }
    +    }
    +    qh_setfree(&facet->outsideset);
    +  }
    +} /* outcoplanar */
    +
    +/*---------------------------------
    +
    +  qh_point( 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(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 other_points))
    +    return SETelemt_(qh other_points, id, pointT);
    +  return NULL;
    +} /* point */
    +
    +/*---------------------------------
    +
    +  qh_point_add( 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(setT *set, pointT *point, void *elem) {
    +  int id, size;
    +
    +  SETreturnsize_(set, size);
    +  if ((id= qh_pointid(point)) < 0)
    +    qh_fprintf(qh ferr, 7067, "qhull internal warning (point_add): unknown point %p id %d\n",
    +      point, id);
    +  else if (id >= size) {
    +    qh_fprintf(qh ferr, 6160, "qhull internal errror(point_add): point p%d is out of bounds(%d)\n",
    +             id, size);
    +    qh_errexit(qh_ERRqhull, NULL, NULL);
    +  }else
    +    SETelem_(set, id)= elem;
    +} /* point_add */
    +
    +
    +/*---------------------------------
    +
    +  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_(facets) { ... }
    +    SETelem_(facets, i)
    +
    +  design:
    +    for each facet
    +      add each vertex
    +      add each coplanar point
    +      add each outside point
    +*/
    +setT *qh_pointfacet(void /*qh.facet_list*/) {
    +  int numpoints= qh num_points + qh_setsize(qh other_points);
    +  setT *facets;
    +  facetT *facet;
    +  vertexT *vertex, **vertexp;
    +  pointT *point, **pointp;
    +
    +  facets= qh_settemp(numpoints);
    +  qh_setzero(facets, 0, numpoints);
    +  qh vertex_visit++;
    +  FORALLfacets {
    +    FOREACHvertex_(facet->vertices) {
    +      if (vertex->visitid != qh vertex_visit) {
    +        vertex->visitid= qh vertex_visit;
    +        qh_point_add(facets, vertex->point, facet);
    +      }
    +    }
    +    FOREACHpoint_(facet->coplanarset)
    +      qh_point_add(facets, point, facet);
    +    FOREACHpoint_(facet->outsideset)
    +      qh_point_add(facets, point, facet);
    +  }
    +  return facets;
    +} /* pointfacet */
    +
    +/*---------------------------------
    +
    +  qh_pointvertex(  )
    +    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_(vertices) { ... }
    +    SETelem_(vertices, i)
    +*/
    +setT *qh_pointvertex(void /*qh.facet_list*/) {
    +  int numpoints= qh num_points + qh_setsize(qh other_points);
    +  setT *vertices;
    +  vertexT *vertex;
    +
    +  vertices= qh_settemp(numpoints);
    +  qh_setzero(vertices, 0, numpoints);
    +  FORALLvertices
    +    qh_point_add(vertices, vertex->point, vertex);
    +  return vertices;
    +} /* pointvertex */
    +
    +
    +/*---------------------------------
    +
    +  qh_prependfacet( 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(facetT *facet, facetT **facetlist) {
    +  facetT *prevfacet, *list;
    +
    +
    +  trace4((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 */
    +
    +
    +/*---------------------------------
    +
    +  qh_printhashtable( fp )
    +    print hash table to fp
    +
    +  notes:
    +    not in I/O to avoid bringing io.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(FILE *fp) {
    +  facetT *facet, *neighbor;
    +  int id, facet_i, facet_n, neighbor_i= 0, neighbor_n= 0;
    +  vertexT *vertex, **vertexp;
    +
    +  FOREACHfacet_i_(qh hash_table) {
    +    if (facet) {
    +      FOREACHneighbor_i_(facet) {
    +        if (!neighbor || neighbor == qh_MERGEridge || neighbor == qh_DUPLICATEridge)
    +          break;
    +      }
    +      if (neighbor_i == neighbor_n)
    +        continue;
    +      qh_fprintf(fp, 9283, "hash %d f%d ", facet_i, facet->id);
    +      FOREACHvertex_(facet->vertices)
    +        qh_fprintf(fp, 9284, "v%d ", vertex->id);
    +      qh_fprintf(fp, 9285, "\n neighbors:");
    +      FOREACHneighbor_i_(facet) {
    +        if (neighbor == qh_MERGEridge)
    +          id= -3;
    +        else if (neighbor == qh_DUPLICATEridge)
    +          id= -2;
    +        else
    +          id= getid_(neighbor);
    +        qh_fprintf(fp, 9286, " %d", id);
    +      }
    +      qh_fprintf(fp, 9287, "\n");
    +    }
    +  }
    +} /* printhashtable */
    +
    +
    +/*---------------------------------
    +
    +  qh_printlists( fp )
    +    print out facet and vertex list for debugging (without 'f/v' tags)
    +*/
    +void qh_printlists(void) {
    +  facetT *facet;
    +  vertexT *vertex;
    +  int count= 0;
    +
    +  qh_fprintf(qh ferr, 8108, "qh_printlists: facets:");
    +  FORALLfacets {
    +    if (++count % 100 == 0)
    +      qh_fprintf(qh ferr, 8109, "\n     ");
    +    qh_fprintf(qh ferr, 8110, " %d", facet->id);
    +  }
    +  qh_fprintf(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 ferr, 8112, "\n     ");
    +    qh_fprintf(qh ferr, 8113, " %d", vertex->id);
    +  }
    +  qh_fprintf(qh ferr, 8114, "\n");
    +} /* printlists */
    +
    +/*---------------------------------
    +
    +  qh_resetlists( 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(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 */
    +
    +/*---------------------------------
    +
    +  qh_setvoronoi_all()
    +    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(void) {
    +  facetT *facet;
    +
    +  qh_clearcenters(qh_ASvoronoi);
    +  qh_vertexneighbors();
    +
    +  FORALLfacets {
    +    if (!facet->normal || !facet->upperdelaunay || qh UPPERdelaunay) {
    +      if (!facet->center)
    +        facet->center= qh_facetcenter(facet->vertices);
    +    }
    +  }
    +} /* setvoronoi_all */
    +
    +#ifndef qh_NOmerge
    +
    +/*---------------------------------
    +
    +  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(void /*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 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_ASvoronoi);
    +    qh_vertexneighbors();
    +  }
    +  qh ONLYgood= False; /* for makenew_nonsimplicial */
    +  qh visit_id++;
    +  qh NEWfacets= True;
    +  qh degen_mergeset= qh_settemp(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(facet, &new_vertex_list);
    +  }
    +  trace2((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(facet->ridges) > 0) {
    +        qh_fprintf(qh ferr, 6161, "qhull error (qh_triangulate): ridges still defined for f%d\n", facet->id);
    +        qh_errexit(qh_ERRqhull, facet, NULL);
    +      }
    +      qh_setfree(&facet->ridges);
    +    }
    +    if (SETfirst_(facet->vertices) == SETsecond_(facet->vertices)) {
    +      zinc_(Ztrinull);
    +      qh_triangulate_null(facet);
    +    }
    +  }
    +  trace2((qh ferr, 2048, "qh_triangulate: delete %d or more mirror facets -- same vertices and neighbors\n", qh_setsize(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(merge, (int)sizeof(mergeT));
    +    if (mergetype == MRGmirror) {
    +      zinc_(Ztrimirror);
    +      qh_triangulate_mirror(facet1, facet2);
    +    }
    +  }
    +  qh_settempfree(&qh degen_mergeset);
    +  trace2((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.newvertex_list, empty newfacet_list and visible_list*/);
    +  qh_resetlists(False, !qh_RESETvisible /*qh.newvertex_list, empty newfacet_list and visible_list*/);
    +
    +  trace2((qh ferr, 2050, "qh_triangulate: identify degenerate tricoplanar facets from f%d\n", getid_(new_facet_list)));
    +  trace2((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_(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 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(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 ferr, 2053, "qh_triangulate: all tricoplanar facets degenerate for non-simplicial facet f%d\n",
    +                       visible->id));
    +          qh_delfacet(visible);
    +          qh num_visible--;
    +        }
    +        visible= facet;
    +        owner= NULL;
    +      }
    +    }else if (facet->tricoplanar) {
    +      if (facet->f.triowner != visible || visible==NULL) {
    +        qh_fprintf(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_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(visible);
    +        qh num_visible--;
    +      }
    +    }
    +  }
    +  if (visible && !owner) {
    +    trace2((qh ferr, 2054, "qh_triangulate: all tricoplanar facets degenerate for last non-simplicial facet f%d\n",
    +                 visible->id));
    +    qh_delfacet(visible);
    +    qh num_visible--;
    +  }
    +  qh NEWfacets= False;
    +  qh ONLYgood= onlygood; /* restore value */
    +  if (qh CHECKfrequently)
    +    qh_checkpolygon(qh facet_list);
    +  qh hasTriangulation= True;
    +} /* triangulate */
    +
    +
    +/*---------------------------------
    +
    +  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(facetT *facetA, vertexT **first_vertex) {
    +  facetT *newfacet;
    +  facetT *neighbor, **neighborp;
    +  vertexT *apex;
    +  int numnew=0;
    +
    +  trace3((qh ferr, 3020, "qh_triangulate_facet: triangulate facet f%d\n", facetA->id));
    +
    +  if (qh IStracing >= 4)
    +    qh_printfacet(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(facetA->vertices);
    +  }
    +  qh_willdelete(facetA, NULL);
    +  qh newfacet_list= qh facet_tail;
    +  facetA->visitid= qh visit_id;
    +  apex= SETfirstt_(facetA->vertices, vertexT);
    +  qh_makenew_nonsimplicial(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 normal_size);
    +        memcpy((char *)newfacet->normal, facetA->normal, qh normal_size);
    +      }
    +      if (qh CENTERtype == qh_AScentrum)
    +        newfacet->center= qh_getcentrum(newfacet);
    +      else if (qh CENTERtype == qh_ASvoronoi && facetA->center){
    +        newfacet->center= qh_memalloc(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.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.newfacet_list, qh.empty visible_list and qh.newvertex_list*/);
    +  qh_resetlists(False, !qh_RESETvisible /*qh.newfacet_list, qh.empty visible_list and qh.newvertex_list*/);
    +} /* triangulate_facet */
    +
    +/*---------------------------------
    +
    +  qh_triangulate_link(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(facetT *oldfacetA, facetT *facetA, facetT *oldfacetB, facetT *facetB) {
    +  int errmirror= False;
    +
    +  trace3((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(facetA, facetB, MRGmirror, NULL);
    +  }else if (qh_setin(facetB->neighbors, facetA))
    +    errmirror= True;
    +  if (errmirror) {
    +    qh_fprintf(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_ERRqhull, facetA, facetB);
    +  }
    +  qh_setreplace(facetB->neighbors, oldfacetB, facetA);
    +  qh_setreplace(facetA->neighbors, oldfacetA, facetB);
    +} /* triangulate_link */
    +
    +/*---------------------------------
    +
    +  qh_triangulate_mirror(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(facetT *facetA, facetT *facetB) {
    +  facetT *neighbor, *neighborB;
    +  int neighbor_i, neighbor_n;
    +
    +  trace3((qh ferr, 3022, "qh_triangulate_mirror: delete mirrored facets f%d and f%d\n",
    +         facetA->id, facetB->id));
    +  FOREACHneighbor_i_(facetA) {
    +    neighborB= SETelemt_(facetB->neighbors, neighbor_i, facetT);
    +    if (neighbor == neighborB)
    +      continue; /* occurs twice */
    +    qh_triangulate_link(facetA, neighbor, facetB, neighborB);
    +  }
    +  qh_willdelete(facetA, NULL);
    +  qh_willdelete(facetB, NULL);
    +} /* triangulate_mirror */
    +
    +/*---------------------------------
    +
    +  qh_triangulate_null(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(facetT *facetA) {
    +  facetT *neighbor, *otherfacet;
    +
    +  trace3((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(facetA, neighbor, facetA, otherfacet);
    +  qh_willdelete(facetA, NULL);
    +} /* triangulate_null */
    +
    +#else /* qh_NOmerge */
    +void qh_triangulate(void) {
    +}
    +#endif /* qh_NOmerge */
    +
    +   /*---------------------------------
    +
    +  qh_vertexintersect( vertexsetA, vertexsetB )
    +    intersects two vertex sets (inverse id ordered)
    +    vertexsetA is a temporary set at the top of qhmem.tempstack
    +
    +  returns:
    +    replaces vertexsetA with the intersection
    +
    +  notes:
    +    could overwrite vertexsetA if currently too slow
    +*/
    +void qh_vertexintersect(setT **vertexsetA,setT *vertexsetB) {
    +  setT *intersection;
    +
    +  intersection= qh_vertexintersect_new(*vertexsetA, vertexsetB);
    +  qh_settempfree(vertexsetA);
    +  *vertexsetA= intersection;
    +  qh_settemppush(intersection);
    +} /* vertexintersect */
    +
    +/*---------------------------------
    +
    +  qh_vertexintersect_new(  )
    +    intersects two vertex sets (inverse id ordered)
    +
    +  returns:
    +    a new set
    +*/
    +setT *qh_vertexintersect_new(setT *vertexsetA,setT *vertexsetB) {
    +  setT *intersection= qh_setnew(qh hull_dim - 1);
    +  vertexT **vertexA= SETaddr_(vertexsetA, vertexT);
    +  vertexT **vertexB= SETaddr_(vertexsetB, vertexT);
    +
    +  while (*vertexA && *vertexB) {
    +    if (*vertexA  == *vertexB) {
    +      qh_setappend(&intersection, *vertexA);
    +      vertexA++; vertexB++;
    +    }else {
    +      if ((*vertexA)->id > (*vertexB)->id)
    +        vertexA++;
    +      else
    +        vertexB++;
    +    }
    +  }
    +  return intersection;
    +} /* vertexintersect_new */
    +
    +/*---------------------------------
    +
    +  qh_vertexneighbors()
    +    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(void /*qh.facet_list*/) {
    +  facetT *facet;
    +  vertexT *vertex, **vertexp;
    +
    +  if (qh VERTEXneighbors)
    +    return;
    +  trace1((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 hull_dim);
    +      }
    +      qh_setappend(&vertex->neighbors, facet);
    +    }
    +  }
    +  qh VERTEXneighbors= True;
    +} /* vertexneighbors */
    +
    +/*---------------------------------
    +
    +  qh_vertexsubset( vertexsetA, vertexsetB )
    +    returns True if vertexsetA is a subset of vertexsetB
    +    assumes vertexsets are sorted
    +
    +  note:
    +    empty set is a subset of any other set
    +*/
    +boolT qh_vertexsubset(setT *vertexsetA, setT *vertexsetB) {
    +  vertexT **vertexA= (vertexT **) SETaddr_(vertexsetA, vertexT);
    +  vertexT **vertexB= (vertexT **) SETaddr_(vertexsetB, vertexT);
    +
    +  while (True) {
    +    if (!*vertexA)
    +      return True;
    +    if (!*vertexB)
    +      return False;
    +    if ((*vertexA)->id > (*vertexB)->id)
    +      return False;
    +    if (*vertexA  == *vertexB)
    +      vertexA++;
    +    vertexB++;
    +  }
    +  return False; /* avoid warnings */
    +} /* vertexsubset */
    diff --git a/xs/src/qhull/src/libqhull/qh-geom.htm b/xs/src/qhull/src/libqhull/qh-geom.htm
    new file mode 100644
    index 000000000..6dc7465eb
    --- /dev/null
    +++ b/xs/src/qhull/src/libqhull/qh-geom.htm
    @@ -0,0 +1,295 @@
    +
    +
    +
    +
    +geom.c, geom2.c -- geometric and floating point routines
    +
    +
    +
    +
    +

    Up: Home page for Qhull
    +Up: Qhull manual: Table of Contents
    +Up: Programs +• Options +• Output +• Formats +• Geomview +• Print +• Qhull +• Precision +• Trace +• Functions
    +Up: Qhull code: Table of Contents
    +To: Qhull functions, macros, and data structures
    +To: GeomGlobal +• IoMem +• MergePoly +• QhullSet +• StatUser +

    + +
    + + +

    geom.c, geom2.c, random.c -- geometric and floating point routines

    +
    +

    Geometrically, a vertex is a point with d coordinates +and a facet is a halfspace. A halfspace is defined by an +oriented hyperplane through the facet's vertices. A hyperplane +is defined by d normalized coefficients and an offset. A +point is above a facet if its distance to the facet is +positive.

    + +

    Qhull uses floating point coordinates for input points, +vertices, halfspace equations, centrums, and an interior point.

    + +

    Qhull may be configured for single precision or double +precision floating point arithmetic (see realT +).

    + +

    Each floating point operation may incur round-off error (see +Merge). The maximum error for distance +computations is determined at initialization. The roundoff error +in halfspace computation is accounted for by computing the +distance from vertices to the halfspace.

    +
    +

    Copyright © 1995-2015 C.B. Barber

    +
    +

    » Geom + Global • +IoMem • +MergePoly • +QhullSet • +StatUser

    + +

    Index to geom.c, +geom2.c, geom.h, +random.c, random.h +

    + + + +

    »geometric data types +and constants

    + +
      +
    • coordT coordinates and +coefficients are stored as realT
    • +
    • pointT a point is an array +of DIM3 coordinates
    • +
    + +

    »mathematical macros

    + +
      +
    • fabs_ returns the absolute +value of a
    • +
    • fmax_ returns the maximum +value of a and b
    • +
    • fmin_ returns the minimum +value of a and b
    • +
    • maximize_ maximize a value +
    • +
    • minimize_ minimize a value +
    • +
    • det2_ compute a 2-d +determinate
    • +
    • det3_ compute a 3-d +determinate
    • +
    • dX, dY, dZ compute the difference +between two coordinates
    • +
    + +

    »mathematical functions

    + + + +

    »computational geometry functions

    + + + +

    »point array functions

    + + +

    »geometric facet functions

    + + +

    »geometric roundoff functions

    +
      +
    • qh_detjoggle determine +default joggle for points and distance roundoff error
    • +
    • qh_detroundoff +determine maximum roundoff error and other precision constants
    • +
    • qh_distround compute +maximum roundoff error due to a distance computation to a +normalized hyperplane
    • +
    • qh_divzero divide by a +number that is nearly zero
    • +
    • qh_maxouter return maximum outer +plane
    • +
    • qh_outerinner return actual +outer and inner planes +
    + +

    +
    +

    Up: +Home page for +Qhull
    +Up: Qhull manual: Table of Contents
    +Up: Programs +• Options +• Output +• Formats +• Geomview +• Print +• Qhull +• Precision +• Trace +• Functions
    +Up: Qhull code: Table of Contents
    +To: Qhull functions, macros, and data structures
    +To: Geom • +GlobalIo +• MemMerge +• PolyQhull +• SetStat +• User
    + + +

    +
    +

    The +Geometry Center Home Page

    +

    Comments to: qhull@qhull.org +
    +Created: May 2, 1997 --- Last modified: see top

    + + diff --git a/xs/src/qhull/src/libqhull/qh-globa.htm b/xs/src/qhull/src/libqhull/qh-globa.htm new file mode 100644 index 000000000..c87508b66 --- /dev/null +++ b/xs/src/qhull/src/libqhull/qh-globa.htm @@ -0,0 +1,165 @@ + + + + +global.c -- global variables and their functions + + + + +

    Up: Home page for Qhull
    +Up: Qhull manual: Table of Contents
    +Up: Programs +• Options +• Output +• Formats +• Geomview +• Print +• Qhull +• Precision +• Trace +• Functions
    +Up: Qhull code: Table of Contents
    +To: Qhull functions, macros, and data structures
    +To: GeomGlobal +• IoMem +• MergePoly +• QhullSet +• StatUser +

    + +
    + + +

    global.c -- global variables and their functions

    +
    +

    Qhull uses a global data structure, qh, to store +globally defined constants, lists, sets, and variables. This +allows multiple instances of Qhull to execute at the same time. +The structure may be statically allocated or +dynamically allocated with malloc(). See +QHpointer. +

    +
    +

    Copyright © 1995-2015 C.B. Barber

    +
    +

    » Geom + Global • +IoMem • +MergePoly • +QhullSet • +StatUser

    + +

    Index to global.c and +libqhull.h

    + + + +

    »Qhull's global +variables

    + + + +

    »Global variable and +initialization routines

    + + + +

    +
    +

    Up: +Home page for +Qhull
    +Up: Qhull manual: Table of Contents
    +Up: Programs +• Options +• Output +• Formats +• Geomview +• Print +• Qhull +• Precision +• Trace +• Functions
    +Up: Qhull code: Table of Contents
    +To: Qhull functions, macros, and data structures
    +To: Geom • +GlobalIo +• MemMerge +• PolyQhull +• SetStat +• User
    +

    +
    +

    The +Geometry Center Home Page

    +

    Comments to: qhull@qhull.org +
    +Created: May 2, 1997 --- Last modified: see top

    + + diff --git a/xs/src/qhull/src/libqhull/qh-io.htm b/xs/src/qhull/src/libqhull/qh-io.htm new file mode 100644 index 000000000..5cb591d87 --- /dev/null +++ b/xs/src/qhull/src/libqhull/qh-io.htm @@ -0,0 +1,305 @@ + + + + +io.c -- input and output operations + + + + +

    Up: Home page for Qhull
    +Up: Qhull manual: Table of Contents
    +Up: Programs +• Options +• Output +• Formats +• Geomview +• Print +• Qhull +• Precision +• Trace +• Functions
    +Up: Qhull code: Table of Contents
    +To: Qhull functions, macros, and data structures
    +To: GeomGlobal +• IoMem +• MergePoly +• QhullSet +• StatUser +

    +
    + +

    io.c -- input and output operations

    +
    + +

    Qhull provides a wide range of input +and output options. To organize the code, most output formats use +the same driver:

    + +
    +    qh_printbegin( fp, format, facetlist, facets, printall );
    +
    +    FORALLfacet_( facetlist )
    +      qh_printafacet( fp, format, facet, printall );
    +
    +    FOREACHfacet_( facets )
    +      qh_printafacet( fp, format, facet, printall );
    +
    +    qh_printend( fp, format );
    +
    + +

    Note the 'printall' flag. It selects whether or not +qh_skipfacet() is tested.

    + +
    +

    Copyright © 1995-2015 C.B. Barber

    +
    +

    » Geom +GlobalIo • +MemMerge • +PolyQhull • +SetStat • +User

    + +

    Index to io.c and io.h

    + + + +

    »io.h constants and types

    + +
      +
    • qh_MAXfirst maximum length +of first two lines of stdin
    • +
    • qh_WHITESPACE possible +values of white space
    • +
    • printvridgeT function to +print results of qh_printvdiagram or qh_eachvoronoi
    • +
    + +

    »User level functions

    + + + +

    »Print functions for all +output formats

    + + + +

    »Text output functions

    + + +

    »Text utility functions

    + + +

    »Geomview output functions

    + +

    »Geomview utility functions

    + +

    +

    +
    +

    Up: +Home page for +Qhull
    +Up: Qhull manual: Table of Contents
    +Up: Programs +• Options +• Output +• Formats +• Geomview +• Print +• Qhull +• Precision +• Trace +• Functions
    +Up: Qhull code: Table of Contents
    +To: Qhull functions, macros, and data structures
    +To: Geom • +GlobalIo +• MemMerge +• PolyQhull +• SetStat +• User
    +

    +

    +
    +

    The +Geometry Center Home Page

    +

    Comments to: qhull@qhull.org +
    +Created: May 2, 1997 --- Last modified: see top

    + + diff --git a/xs/src/qhull/src/libqhull/qh-mem.htm b/xs/src/qhull/src/libqhull/qh-mem.htm new file mode 100644 index 000000000..b993b2229 --- /dev/null +++ b/xs/src/qhull/src/libqhull/qh-mem.htm @@ -0,0 +1,145 @@ + + + + +mem.c -- memory operations + + + + +

    Up: Home page for Qhull
    +Up: Qhull manual: Table of Contents
    +Up: Programs +• Options +• Output +• Formats +• Geomview +• Print +• Qhull +• Precision +• Trace +• Functions
    +Up: Qhull code: Table of Contents
    +To: Qhull functions, macros, and data structures
    +To: GeomGlobal +• IoMem +• MergePoly +• QhullSet +• StatUser +

    +
    + +

    mem.c -- memory operations

    +
    +

    Qhull uses quick-fit memory allocation. It maintains a +set of free lists for a variety of small allocations. A +small request returns a block from the best fitting free +list. If the free list is empty, Qhull allocates a block +from a reserved buffer.

    +

    Use 'T5' to trace memory allocations.

    + +
    +

    Copyright © 1995-2015 C.B. Barber

    +
    +

    » Geom + Global • +IoMem +• MergePoly +• QhullSet +• StatUser +

    +

    Index to mem.c and +mem.h

    + +

    »mem.h data types and constants

    +
      +
    • ptr_intT for casting +a void* to an integer-type
    • +
    • qhmemT global memory +structure for mem.c
    • +
    • qh_NOmem disable memory allocation
    • +
    +

    »mem.h macros

    + +

    »User level +functions

    + + +

    »Initialization and +termination functions

    + + +

    +
    +

    Up: +Home page for +Qhull
    +Up: Qhull manual: Table of Contents
    +Up: Programs +• Options +• Output +• Formats +• Geomview +• Print +• Qhull +• Precision +• Trace +• Functions
    +Up: Qhull code: Table of Contents
    +To: Qhull functions, macros, and data structures
    +To: Geom • +GlobalIo +• MemMerge +• PolyQhull +• SetStat +• User
    +

    +

    +
    +

    The +Geometry Center Home Page

    +

    Comments to: qhull@qhull.org +
    +Created: May 2, 1997 --- Last modified: see top

    + + diff --git a/xs/src/qhull/src/libqhull/qh-merge.htm b/xs/src/qhull/src/libqhull/qh-merge.htm new file mode 100644 index 000000000..54b97c88e --- /dev/null +++ b/xs/src/qhull/src/libqhull/qh-merge.htm @@ -0,0 +1,366 @@ + + + + +merge.c -- facet merge operations + + + + +

    Up: Home page for Qhull
    +Up: Qhull manual: Table of Contents
    +Up: Programs +• Options +• Output +• Formats +• Geomview +• Print +• Qhull +• Precision +• Trace +• Functions
    +Up: Qhull code: Table of Contents
    +To: Qhull functions, macros, and data structures
    +To: GeomGlobal +• IoMem +• MergePoly +• QhullSet +• StatUser +

    +
    + +

    merge.c -- facet merge operations

    +
    +

    Qhull handles precision problems by merged facets or joggled input. +Except for redundant vertices, it corrects a problem by +merging two facets. When done, all facets are clearly +convex. See Imprecision in Qhull +for further information.

    +

    Users may joggle the input ('QJn') +instead of merging facets.

    +

    Qhull detects and corrects the following problems:

    +
      +
    • More than two facets meeting at a ridge. When +Qhull creates facets, it creates an even number +of facets for each ridge. A convex hull always +has two facets for each ridge. More than two +facets may be created if non-adjacent facets +share a vertex. This is called a duplicate +ridge. In 2-d, a duplicate ridge would +create a loop of facets.
    • +
    +
      +
    • A facet contained in another facet. Facet +merging may leave all vertices of one facet as a +subset of the vertices of another facet. This is +called a redundant facet.
    • +
    +
      +
    • A facet with fewer than three neighbors. Facet +merging may leave a facet with one or two +neighbors. This is called a degenerate facet. +
    • +
    +
      +
    • A facet with flipped orientation. A +facet's hyperplane may define a halfspace that +does not include the interior point.This is +called a flipped facet.
    • +
    +
      +
    • A coplanar horizon facet. A +newly processed point may be coplanar with an +horizon facet. Qhull creates a new facet without +a hyperplane. It links new facets for the same +horizon facet together. This is called a samecycle. +The new facet or samecycle is merged into the +horizon facet.
    • +
    +
      +
    • Concave facets. A facet's centrum may be +above a neighboring facet. If so, the facets meet +at a concave angle.
    • +
    +
      +
    • Coplanar facets. A facet's centrum may be +coplanar with a neighboring facet (i.e., it is +neither clearly below nor clearly above the +facet's hyperplane). Qhull removes coplanar +facets in independent sets sorted by angle.
    • +
    +
      +
    • Redundant vertex. A vertex may have fewer +than three neighboring facets. If so, it is +redundant and may be renamed to an adjacent +vertex without changing the topological +structure.This is called a redundant vertex. +
    • +
    +
    +

    Copyright © 1995-2015 C.B. Barber

    +
    +

    » Geom + Global +• IoMem +• MergePoly +• QhullSet +• StatUser +

    +

    Index to merge.c and +merge.h

    + + +

    »merge.h data +types, macros, and global sets

    +
      +
    • mergeT structure to +identify a merge of two facets
    • +
    • FOREACHmerge_ +assign 'merge' to each merge in merges
    • +
    • qh global sets +qh.facet_mergeset contains non-convex merges +while qh.degen_mergeset contains degenerate and +redundant facets
    • +
    +

    »merge.h +constants

    + +

    »top-level merge +functions

    + + +

    »functions for +identifying merges

    + + +

    »functions for +determining the best merge

    + + +

    »functions for +merging facets

    + + +

    »functions for +merging a cycle of facets

    +

    If a point is coplanar with an horizon facet, the +corresponding new facets are linked together (a samecycle) +for merging.

    + +

    »functions +for renaming a vertex

    + + +

    »functions +for identifying vertices for renaming

    + + +

    »functions for check and +trace

    + + +

    +
    +

    Up: +Home page for +Qhull
    +Up: Qhull manual: Table of Contents
    +Up: Programs +• Options +• Output +• Formats +• Geomview +• Print +• Qhull +• Precision +• Trace +• Functions
    +Up: Qhull code: Table of Contents
    +To: Qhull functions, macros, and data structures
    +To: Geom • +GlobalIo +• MemMerge +• PolyQhull +• SetStat +• User
    +

    +

    +
    +

    The +Geometry Center Home Page

    +

    Comments to: qhull@qhull.org +
    +Created: May 2, 1997 --- Last modified: see top

    + + diff --git a/xs/src/qhull/src/libqhull/qh-poly.htm b/xs/src/qhull/src/libqhull/qh-poly.htm new file mode 100644 index 000000000..c8f6b38b0 --- /dev/null +++ b/xs/src/qhull/src/libqhull/qh-poly.htm @@ -0,0 +1,485 @@ + + + + +poly.c, poly2.c -- polyhedron operations + + + + +

    Up: Home page for Qhull
    +Up: Qhull manual: Table of Contents
    +Up: Programs +• Options +• Output +• Formats +• Geomview +• Print +• Qhull +• Precision +• Trace +• Functions
    +Up: Qhull code: Table of Contents
    +To: Qhull functions, macros, and data structures
    +To: GeomGlobal +• IoMem +• MergePoly +• QhullSet +• StatUser +

    +
    + +

    poly.c, poly2.c -- polyhedron operations

    +
    + +

    Qhull uses dimension-free terminology. Qhull builds a +polyhedron in dimension d. A polyhedron is a +simplicial complex of faces with geometric information for the +top and bottom-level faces. A (d-1)-face is a facet, +a (d-2)-face is a ridge, and a 0-face +is a vertex. For example in 3-d, a facet is a polygon +and a ridge is an edge. A facet is built from a ridge (the base) +and a vertex (the apex). See +Qhull's data structures.

    + +

    Qhull's primary data structure is a polyhedron. A +polyhedron is a list of facets. Each facet has a set of +neighboring facets and a set of vertices. Each facet has a +hyperplane. For example, a tetrahedron has four facets. +If its vertices are a, b, c, d, and its facets +are 1, 2, 3, 4, the tetrahedron is

    +
    +
      +
    • facet 1
        +
      • vertices: b c d
      • +
      • neighbors: 2 3 4
      • +
      +
    • +
    • facet 2
        +
      • vertices: a c d
      • +
      • neighbors: 1 3 4
      • +
      +
    • +
    • facet 3
        +
      • vertices: a b d
      • +
      • neighbors: 1 2 4
      • +
      +
    • +
    • facet 4
        +
      • vertices: a b c
      • +
      • neighbors: 1 2 3
      • +
      +
    • +
    +
    +

    A facet may be simplicial or non-simplicial. In 3-d, a +simplicial facet has three vertices and three +neighbors. A nonsimplicial facet has more than +three vertices and more than three neighbors. A +nonsimplicial facet has a set of ridges and a centrum.

    +

    +A simplicial facet has an orientation. An orientation +is either top or bottom. +The flag, facet->toporient, +defines the orientation of the facet's vertices. For example in 3-d, +'top' is left-handed orientation (i.e., the vertex order follows the direction +of the left-hand fingers when the thumb is pointing away from the center). +Except for axis-parallel facets in 5-d and higher, topological orientation +determines the geometric orientation of the facet's hyperplane. + +

    A nonsimplicial facet is due to merging two or more +facets. The facet's ridge set determine a simplicial +decomposition of the facet. Each ridge is a 1-face (i.e., +it has two vertices and two neighboring facets). The +orientation of a ridge is determined by the order of the +neighboring facets. The flag, facet->toporient,is +ignored.

    +

    A nonsimplicial facet has a centrum for testing +convexity. A centrum is a point on the facet's +hyperplane that is near the center of the facet. Except +for large facets, it is the arithmetic average of the +facet's vertices.

    +

    A nonsimplicial facet is an approximation that is +defined by offsets from the facet's hyperplane. When +Qhull finishes, the outer plane is above all +points while the inner plane is below the facet's +vertices. This guarantees that any exact convex hull +passes between the inner and outer planes. The outer +plane is defined by facet->maxoutside while +the inner plane is computed from the facet's vertices.

    + +

    Qhull 3.1 includes triangulation of non-simplicial facets +('Qt'). +These facets, +called tricoplanar, share the same normal. centrum, and Voronoi center. +One facet (keepcentrum) owns these data structures. +While tricoplanar facets are more accurate than the simplicial facets from +joggled input, they +may have zero area or flipped orientation. + +

    +

    Copyright © 1995-2015 C.B. Barber

    +
    +

    » Geom + Global +• IoMem +• MergePoly +• QhullSet +• StatUser +

    +

    Index to poly.c, +poly2.c, poly.h, +and libqhull.h

    + +

    »Data +types and global lists for polyhedrons

    + +

    »poly.h constants

    +
      +
    • ALGORITHMfault +flag to not report errors in qh_checkconvex()
    • +
    • DATAfault flag to +report errors in qh_checkconvex()
    • +
    • DUPLICATEridge +special value for facet->neighbor to indicate +a duplicate ridge
    • +
    • MERGEridge +special value for facet->neighbor to indicate +a merged ridge
    • +
    +

    »Global FORALL +macros

    + +

    »FORALL macros

    + +

    »FOREACH macros

    + +

    »Indexed +FOREACH macros

    +
      +
    • FOREACHfacet_i_ +assign 'facet' and 'facet_i' to each facet in +facet set
    • +
    • FOREACHneighbor_i_ +assign 'neighbor' and 'neighbor_i' to each facet +in facet->neighbors or vertex->neighbors
    • +
    • FOREACHpoint_i_ +assign 'point' and 'point_i' to each point in +points set
    • +
    • FOREACHridge_i_ +assign 'ridge' and 'ridge_i' to each ridge in +ridges set
    • +
    • FOREACHvertex_i_ +assign 'vertex' and 'vertex_i' to each vertex in +vertices set
    • +
    • FOREACHvertexreverse12_ +assign 'vertex' to each vertex in vertex set; +reverse the order of first two vertices
    • +
    +

    »Other macros for polyhedrons

    +
      +
    • getid_ return ID for +a facet, ridge, or vertex
    • +
    • otherfacet_ +return neighboring facet for a ridge in a facet
    • +
    +

    »Facetlist +functions

    + +

    »Facet +functions

    + +

    »Vertex, +ridge, and point functions

    + +

    »Hashtable functions

    + +

    »Allocation and +deallocation functions

    + +

    »Check +functions

    + + + +

    +
    +

    Up: +Home page for +Qhull
    +Up: Qhull manual: Table of Contents
    +Up: Programs +• Options +• Output +• Formats +• Geomview +• Print +• Qhull +• Precision +• Trace +• Functions
    +Up: Qhull code: Table of Contents
    +To: Qhull functions, macros, and data structures
    +To: Geom • +GlobalIo +• MemMerge +• PolyQhull +• SetStat +• User
    +

    +

    +
    +

    The +Geometry Center Home Page

    +

    Comments to: qhull@qhull.org +
    +Created: May 2, 1997 --- Last modified: see top

    + + diff --git a/xs/src/qhull/src/libqhull/qh-qhull.htm b/xs/src/qhull/src/libqhull/qh-qhull.htm new file mode 100644 index 000000000..5212c6422 --- /dev/null +++ b/xs/src/qhull/src/libqhull/qh-qhull.htm @@ -0,0 +1,279 @@ + + + + +libqhull.c -- top-level functions and basic data types + + + + +

    Up: Home page for Qhull
    +Up: Qhull manual: Table of Contents
    +Up: Programs +• Options +• Output +• Formats +• Geomview +• Print +• Qhull +• Precision +• Trace +• Functions
    +Up: Qhull code: Table of Contents
    +To: Qhull functions, macros, and data structures
    +To: GeomGlobal +• IoMem +• MergePoly +• QhullSet +• StatUser +

    +
    + +

    libqhull.c -- top-level functions and basic data types

    +
    +

    Qhull implements the Quickhull algorithm for computing +the convex hull. The Quickhull algorithm combines two +well-known algorithms: the 2-d quickhull algorithm and +the n-d beneath-beyond algorithm. See +Description of Qhull.

    +

    This section provides an index to the top-level +functions and base data types. The top-level header file, libqhull.h, +contains prototypes for these functions.

    +
    +

    Copyright © 1995-2015 C.B. Barber

    +
    +

    » Geom + Global +• IoMem +• MergePoly +• QhullSet +• StatUser +

    +

    Index to libqhull.c, +libqhull.h, and +unix.c

    + + +

    »libqhull.h and unix.c +data types and constants

    +
      +
    • flagT Boolean flag as +a bit
    • +
    • boolT boolean value, +either True or False
    • +
    • CENTERtype to +distinguish facet->center
    • +
    • qh_PRINT output +formats for printing (qh.PRINTout)
    • +
    • qh_ALL argument flag +for selecting everything
    • +
    • qh_ERR Qhull exit +codes for indicating errors
    • +
    • qh_FILEstderr Fake stderr +to distinguish error output from normal output [C++ only]
    • +
    • qh_prompt version and long prompt for Qhull
    • +
    • qh_prompt2 synopsis for Qhull
    • +
    • qh_prompt3 concise prompt for Qhull
    • +
    • qh_version version stamp
    • +
    + +

    »libqhull.h other +macros

    +
      +
    • traceN print trace +message if qh.IStracing >= N.
    • +
    • QHULL_UNUSED declare an + unused variable to avoid warnings.
    • +
    + +

    »Quickhull +routines in call order

    + + +

    »Top-level routines for initializing and terminating Qhull (in other modules)

    + + +

    »Top-level routines for reading and modifying the input (in other modules)

    + + +

    »Top-level routines for calling Qhull (in other modules)

    + + +

    »Top-level routines for returning results (in other modules)

    + + +

    »Top-level routines for testing and debugging (in other modules)

    + + +

    +
    +

    Up: +Home page for +Qhull
    +Up: Qhull manual: Table of Contents
    +Up: Programs +• Options +• Output +• Formats +• Geomview +• Print +• Qhull +• Precision +• Trace +• Functions
    +Up: Qhull code: Table of Contents
    +To: Qhull functions, macros, and data structures
    +To: Geom • +GlobalIo +• MemMerge +• PolyQhull +• SetStat +• User
    +

    +

    +
    +

    The +Geometry Center Home Page

    +

    Comments to: qhull@qhull.org +
    +Created: May 2, 1997 --- Last modified: see top

    + + diff --git a/xs/src/qhull/src/libqhull/qh-set.htm b/xs/src/qhull/src/libqhull/qh-set.htm new file mode 100644 index 000000000..06e71bbc9 --- /dev/null +++ b/xs/src/qhull/src/libqhull/qh-set.htm @@ -0,0 +1,308 @@ + + + + +qset.c -- set data type and operations + + + + +

    Up: Home page for Qhull
    +Up: Qhull manual: Table of Contents
    +Up: Programs +• Options +• Output +• Formats +• Geomview +• Print +• Qhull +• Precision +• Trace +• Functions
    +Up: Qhull code: Table of Contents
    +To: Qhull functions, macros, and data structures
    +To: GeomGlobal +• IoMem +• MergePoly +• QhullSet +• StatUser +

    +
    + +

    qset.c -- set data type and operations

    +
    +

    Qhull's data structures are constructed from sets. The +functions and macros in qset.c construct, iterate, and +modify these sets. They are the most frequently called +functions in Qhull. For this reason, efficiency is the +primary concern.

    +

    In Qhull, a set is represented by an unordered +array of pointers with a maximum size and a NULL +terminator (setT). +Most sets correspond to mathematical sets +(i.e., the pointers are unique). Some sets are sorted to +enforce uniqueness. Some sets are ordered. For example, +the order of vertices in a ridge determine the ridge's +orientation. If you reverse the order of adjacent +vertices, the orientation reverses. Some sets are not +mathematical sets. They may be indexed as an array and +they may include NULL pointers.

    +

    The most common operation on a set is to iterate its +members. This is done with a 'FOREACH...' macro. Each set +has a custom macro. For example, 'FOREACHvertex_' +iterates over a set of vertices. Each vertex is assigned +to the variable 'vertex' from the pointer 'vertexp'.

    +

    Most sets are constructed by appending elements to the +set. The last element of a set is either NULL or the +index of the terminating NULL for a partially full set. +If a set is full, appending an element copies the set to +a larger array.

    + +
    +

    Copyright © 1995-2015 C.B. Barber

    +
    +

    » Geom + Global • +IoMem • +MergePoly +• QhullSet +• StatUser +

    +

    Index to qset.c and +qset.h

    + +

    »Data types and +constants

    +
      +
    • SETelemsize size +of a set element in bytes
    • +
    • setT a set with a +maximum size and a current size
    • +
    • qh global sets +global sets for temporary sets, etc.
    • +
    +

    »FOREACH macros

    + +

    »Access and +size macros

    + +

    »Internal macros

    +
      +
    • SETsizeaddr_ +return pointer to end element of a set (indicates +current size)
    • +
    + +

    »address macros

    +
      +
    • SETaddr_ return +address of a set's elements
    • +
    • SETelemaddr_ +return address of the n'th element of a set
    • +
    • SETref_ l.h.s. for +modifying the current element in a FOREACH +iteration
    • +
    + +

    »Allocation and +deallocation functions

    + + +

    »Access and +predicate functions

    + + +

    »Add functions

    + + +

    »Check and print functions

    + + +

    »Copy, compact, and zero functions

    + + +

    »Delete functions

    + + +

    »Temporary set functions

    + + +

    +
    +

    Up: +Home page for +Qhull
    +Up: Qhull manual: Table of Contents
    +Up: Programs +• Options +• Output +• Formats +• Geomview +• Print +• Qhull +• Precision +• Trace +• Functions
    +Up: Qhull code: Table of Contents
    +To: Qhull functions, macros, and data structures
    +To: Geom • +GlobalIo +• MemMerge +• PolyQhull +• SetStat +• User
    +

    +

    +
    +

    The +Geometry Center Home Page

    +

    Comments to: qhull@qhull.org +
    +Created: May 2, 1997 --- Last modified: see top

    + + diff --git a/xs/src/qhull/src/libqhull/qh-stat.htm b/xs/src/qhull/src/libqhull/qh-stat.htm new file mode 100644 index 000000000..b96854031 --- /dev/null +++ b/xs/src/qhull/src/libqhull/qh-stat.htm @@ -0,0 +1,163 @@ + + + + +stat.c -- statistical operations + + + + +

    Up: Home page for Qhull
    +Up: Qhull manual: Table of Contents
    +Up: Programs +• Options +• Output +• Formats +• Geomview +• Print +• Qhull +• Precision +• Trace +• Functions
    +Up: Qhull code: Table of Contents
    +To: Qhull functions, macros, and data structures
    +To: GeomGlobal +• IoMem +• MergePoly +• QhullSet +• StatUser +

    +
    + +

    stat.c -- statistical operations

    +
    +

    Qhull records many statistics. These functions and +macros make it inexpensive to add a statistic. +

    As with Qhull's global variables, the statistics data structure is +accessed by a macro, 'qhstat'. If qh_QHpointer is defined, the macro +is 'qh_qhstat->', otherwise the macro is 'qh_qhstat.'. +Statistics +may be turned off in user.h. If so, all but the 'zz' +statistics are ignored.

    +
    +

    Copyright © 1995-2015 C.B. Barber

    +
    +

    » Geom + Global +• IoMem +• MergePoly +• QhullSet +• StatUser +

    +

    Index to stat.c and +stat.h

    + + +

    »stat.h types

    +
      +
    • intrealT union of +integer and real
    • +
    • qhstat global data +structure for statistics
    • +
    +

    »stat.h +constants

    +
      +
    • qh_KEEPstatistics 0 turns off most statistics
    • +
    • Z..., W... integer (Z) and real (W) statistics +
    • +
    • ZZstat Z.../W... statistics that +remain defined if qh_KEEPstatistics=0 +
    • +
    • ztype zdoc, zinc, etc. +for definining statistics
    • +
    +

    »stat.h macros

    +
      +
    • MAYdebugx called +frequently for error trapping
    • +
    • zadd_/wadd_ add value +to an integer or real statistic
    • +
    • zdef_ define a +statistic
    • +
    • zinc_ increment an +integer statistic
    • +
    • zmax_/wmax_ update +integer or real maximum statistic
    • +
    • zmin_/wmin_ update +integer or real minimum statistic
    • +
    • zval_/wval_ set or +return value of a statistic
    • +
    + +

    »stat.c +functions

    + + +

    +
    +

    Up: +Home page for +Qhull
    +Up: Qhull manual: Table of Contents
    +Up: Programs +• Options +• Output +• Formats +• Geomview +• Print +• Qhull +• Precision +• Trace +• Functions
    +Up: Qhull code: Table of Contents
    +To: Qhull functions, macros, and data structures
    +To: Geom • +GlobalIo +• MemMerge +• PolyQhull +• SetStat +• User
    +

    +

    +
    +

    The +Geometry Center Home Page

    +

    Comments to: qhull@qhull.org +
    +Created: May 2, 1997 --- Last modified: see top

    + + diff --git a/xs/src/qhull/src/libqhull/qh-user.htm b/xs/src/qhull/src/libqhull/qh-user.htm new file mode 100644 index 000000000..6682f4b2f --- /dev/null +++ b/xs/src/qhull/src/libqhull/qh-user.htm @@ -0,0 +1,271 @@ + + + + +user.c -- user-definable operations + + + + +

    Up: Home page for Qhull
    +Up: Qhull manual: Table of Contents
    +Up: Programs +• Options +• Output +• Formats +• Geomview +• Print +• Qhull +• Precision +• Trace +• Functions
    +Up: Qhull code: Table of Contents
    +To: Qhull functions, macros, and data structures
    +To: GeomGlobal +• IoMem +• MergePoly +• QhullSet +• StatUser +

    +
    +

    user.c -- user-definable operations

    +
    +

    This section contains functions and constants that the +user may want to change.

    + +
    +

    Copyright © 1995-2015 C.B. Barber

    +
    +

    » Geom + Global +• IoMem +• MergePoly +• QhullSet +• StatUser +

    +

    Index to user.c, usermem.c, userprintf.c, userprintf_rbox.c and +user.h

    + + +

    »Qhull library constants

    + + + +

    »user.h data +types and configuration macros

    + + +

    »definition constants

    +
      +
    • qh_DEFAULTbox +define default box size for rbox, 'Qbb', and 'QbB' (Geomview expects 0.5)
    • +
    • qh_INFINITE on +output, indicates Voronoi center at infinity
    • +
    • qh_ORIENTclock +define convention for orienting facets
    • +
    • qh_ZEROdelaunay +define facets that are ignored in Delaunay triangulations
    • +
    + +

    »joggle constants

    + + +

    »performance +related constants

    + + +

    »memory constants

    + + +

    »conditional compilation

    +
      +
    • compiler defined symbols, +e.g., _STDC_ and _cplusplus + +
    • qh_COMPUTEfurthest + compute furthest distance to an outside point instead of storing it with the facet +
    • qh_KEEPstatistics + enable statistic gathering and reporting with option 'Ts' +
    • qh_MAXoutside +record outer plane for each facet +
    • qh_NOmerge +disable facet merging +
    • qh_NOtrace +disable tracing with option 'T4' +
    • qh_QHpointer +access global data with pointer or static structure +
    • qh_QUICKhelp +use abbreviated help messages, e.g., for degenerate inputs +
    + +

    »merge +constants

    + + +

    »user.c +functions

    + + +

    »usermem.c +functions

    +
      +
    • qh_exit exit program, same as exit(). May be redefined as throw "QH10003.." by libqhullcpp/usermem_r-cpp.cpp
    • +
    • qh_fprintf_stderr print to stderr when qh.ferr is not defined.
    • +
    • qh_free free memory, same as free().
    • +
    • qh_malloc allocate memory, same as malloc()
    • +
    + +

    »userprintf.c + and userprintf_rbox,c functions

    +
      +
    • qh_fprintf print +information from Qhull, sames as fprintf().
    • +
    • qh_fprintf_rbox print +information from Rbox, sames as fprintf().
    • +
    + +

    +
    +

    Up: +Home page for +Qhull
    +Up: Qhull manual: Table of Contents
    +Up: Programs +• Options +• Output +• Formats +• Geomview +• Print +• Qhull +• Precision +• Trace +• Functions
    +Up: Qhull code: Table of Contents
    +To: Qhull functions, macros, and data structures
    +To: Geom • +GlobalIo +• MemMerge +• PolyQhull +• SetStat +• User
    +

    +

    +
    +

    The +Geometry Center Home Page

    +

    Comments to: qhull@qhull.org +
    +Created: May 2, 1997 --- Last modified: see top

    + + diff --git a/xs/src/qhull/src/libqhull/qhull-exports.def b/xs/src/qhull/src/libqhull/qhull-exports.def new file mode 100644 index 000000000..11a42b57e --- /dev/null +++ b/xs/src/qhull/src/libqhull/qhull-exports.def @@ -0,0 +1,417 @@ +; qhull-exports.def -- msvc module-definition file +; +; Generated from depends.exe by cut-and-paste of exported symbols by mingw gcc +; [mar'11] 399 symbols +; Annotate as DATA qh_last_random qh_qh qh_qhstat qhmem rbox rbox_inuse +; Annotate as __declspec for outside access in win32 -- qh_qh qh_qhstat +; Same as ../libqhullp/qhull_p-exports.def without qh_save_qhull and qh_restore_qhull +; +; $Id: //main/2015/qhull/src/libqhull/qhull-exports.def#3 $$Change: 2047 $ +; $DateTime: 2016/01/04 22:03:18 $$Author: bbarber $ +; +; Define qhull_VERSION in CMakeLists.txt, Makefile, qhull-exports.def, qhull_p-exports.def, qhull_r-exports.def, and qhull-warn.pri +VERSION 7.0 +EXPORTS +qh_addhash +qh_addpoint +qh_all_merges +qh_allstatA +qh_allstatB +qh_allstatC +qh_allstatD +qh_allstatE +qh_allstatE2 +qh_allstatF +qh_allstatG +qh_allstatH +qh_allstatI +qh_allstatistics +qh_appendfacet +qh_appendmergeset +qh_appendprint +qh_appendvertex +qh_argv_to_command +qh_argv_to_command_size +qh_attachnewfacets +qh_backnormal +qh_basevertices +qh_build_withrestart +qh_buildhull +qh_buildtracing +qh_check_bestdist +qh_check_dupridge +qh_check_maxout +qh_check_output +qh_check_point +qh_check_points +qh_checkconnect +qh_checkconvex +qh_checkfacet +qh_checkflags +qh_checkflipped +qh_checkflipped_all +qh_checkpolygon +qh_checkvertex +qh_checkzero +qh_clear_outputflags +qh_clearcenters +qh_clock +qh_collectstatistics +qh_compare_facetarea +qh_compare_facetmerge +qh_compare_facetvisit +qh_compare_vertexpoint +qh_compareangle +qh_comparemerge +qh_comparevisit +qh_copyfilename +qh_copynonconvex +qh_copypoints +qh_countfacets +qh_createsimplex +qh_crossproduct +qh_degen_redundant_facet +qh_degen_redundant_neighbors +qh_deletevisible +qh_delfacet +qh_delridge +qh_delvertex +qh_determinant +qh_detjoggle +qh_detroundoff +qh_detsimplex +qh_detvnorm +qh_detvridge +qh_detvridge3 +qh_dfacet +qh_distnorm +qh_distplane +qh_distround +qh_divzero +qh_dvertex +qh_eachvoronoi +qh_eachvoronoi_all +qh_errexit +qh_errexit2 +qh_errexit_rbox +qh_errprint +qh_exit +qh_facet2point +qh_facet3vertex +qh_facetarea +qh_facetarea_simplex +qh_facetcenter +qh_facetintersect +qh_facetvertices +qh_find_newvertex +qh_findbest +qh_findbest_test +qh_findbestfacet +qh_findbesthorizon +qh_findbestlower +qh_findbestneighbor +qh_findbestnew +qh_findfacet_all +qh_findgood +qh_findgood_all +qh_findgooddist +qh_findhorizon +qh_flippedmerges +qh_forcedmerges +qh_fprintf +qh_fprintf_rbox +qh_fprintf_stderr +qh_free +qh_freebuffers +qh_freebuild +qh_freeqhull +qh_freeqhull2 +qh_freestatistics +qh_furthestnext +qh_furthestout +qh_gausselim +qh_geomplanes +qh_getangle +qh_getarea +qh_getcenter +qh_getcentrum +qh_getdistance +qh_gethash +qh_getmergeset +qh_getmergeset_initial +qh_gram_schmidt +qh_hashridge +qh_hashridge_find +qh_infiniteloop +qh_init_A +qh_init_B +qh_init_qhull_command +qh_initbuild +qh_initflags +qh_initialhull +qh_initialvertices +qh_initqhull_buffers +qh_initqhull_globals +qh_initqhull_mem +qh_initqhull_outputflags +qh_initqhull_start +qh_initqhull_start2 +qh_initstatistics +qh_initthresholds +qh_inthresholds +qh_isvertex +qh_joggleinput +; Mark as DATA, otherwise links a separate qh_last_random. No __declspec. +qh_last_random DATA +qh_lib_check +qh_makenew_nonsimplicial +qh_makenew_simplicial +qh_makenewfacet +qh_makenewfacets +qh_makenewplanes +qh_makeridges +qh_malloc +qh_mark_dupridges +qh_markkeep +qh_markvoronoi +qh_matchduplicates +qh_matchneighbor +qh_matchnewfacets +qh_matchvertices +qh_maxabsval +qh_maxmin +qh_maxouter +qh_maxsimplex +qh_maydropneighbor +qh_memalloc +qh_memfree +qh_memfreeshort +qh_meminit +qh_meminitbuffers +qh_memsetup +qh_memsize +qh_memstatistics +qh_memtotal +qh_merge_degenredundant +qh_merge_nonconvex +qh_mergecycle +qh_mergecycle_all +qh_mergecycle_facets +qh_mergecycle_neighbors +qh_mergecycle_ridges +qh_mergecycle_vneighbors +qh_mergefacet +qh_mergefacet2d +qh_mergeneighbors +qh_mergeridges +qh_mergesimplex +qh_mergevertex_del +qh_mergevertex_neighbors +qh_mergevertices +qh_minabsval +qh_mindiff +qh_nearcoplanar +qh_nearvertex +qh_neighbor_intersections +qh_new_qhull +qh_newfacet +qh_newhashtable +qh_newridge +qh_newstats +qh_newvertex +qh_newvertices +qh_nextfurthest +qh_nextridge3d +qh_normalize +qh_normalize2 +qh_nostatistic +qh_option +qh_order_vertexneighbors +qh_orientoutside +qh_out1 +qh_out2n +qh_out3n +qh_outcoplanar +qh_outerinner +qh_partitionall +qh_partitioncoplanar +qh_partitionpoint +qh_partitionvisible +qh_point +qh_point_add +qh_pointdist +qh_pointfacet +qh_pointid +qh_pointvertex +qh_postmerge +qh_precision +qh_premerge +qh_prepare_output +qh_prependfacet +qh_printafacet +qh_printallstatistics +qh_printbegin +qh_printcenter +qh_printcentrum +qh_printend +qh_printend4geom +qh_printextremes +qh_printextremes_2d +qh_printextremes_d +qh_printfacet +qh_printfacet2geom +qh_printfacet2geom_points +qh_printfacet2math +qh_printfacet3geom_nonsimplicial +qh_printfacet3geom_points +qh_printfacet3geom_simplicial +qh_printfacet3math +qh_printfacet3vertex +qh_printfacet4geom_nonsimplicial +qh_printfacet4geom_simplicial +qh_printfacetNvertex_nonsimplicial +qh_printfacetNvertex_simplicial +qh_printfacetheader +qh_printfacetlist +qh_printfacetridges +qh_printfacets +qh_printhashtable +qh_printhelp_degenerate +qh_printhelp_narrowhull +qh_printhelp_singular +qh_printhyperplaneintersection +qh_printline3geom +qh_printlists +qh_printmatrix +qh_printneighborhood +qh_printpoint +qh_printpoint3 +qh_printpointid +qh_printpoints +qh_printpoints_out +qh_printpointvect +qh_printpointvect2 +qh_printridge +qh_printspheres +qh_printstatistics +qh_printstatlevel +qh_printstats +qh_printsummary +qh_printvdiagram +qh_printvdiagram2 +qh_printvertex +qh_printvertexlist +qh_printvertices +qh_printvneighbors +qh_printvnorm +qh_printvoronoi +qh_printvridge +qh_produce_output +qh_produce_output2 +qh_projectdim3 +qh_projectinput +qh_projectpoint +qh_projectpoints +; Mark as DATA, otherwise links a separate qh_qh. qh_qh and qh_qhstat requires __declspec +qh_qh DATA +qh_qhstat DATA +qh_qhull +qh_rand +qh_randomfactor +qh_randommatrix +qh_rboxpoints +qh_readfeasible +qh_readpoints +qh_reducevertices +qh_redundant_vertex +qh_remove_extravertices +qh_removefacet +qh_removevertex +qh_rename_sharedvertex +qh_renameridgevertex +qh_renamevertex +qh_resetlists +qh_rotateinput +qh_rotatepoints +qh_roundi +qh_scaleinput +qh_scalelast +qh_scalepoints +qh_setaddnth +qh_setaddsorted +qh_setappend +qh_setappend2ndlast +qh_setappend_set +qh_setcheck +qh_setcompact +qh_setcopy +qh_setdel +qh_setdelaunay +qh_setdellast +qh_setdelnth +qh_setdelnthsorted +qh_setdelsorted +qh_setduplicate +qh_setequal +qh_setequal_except +qh_setequal_skip +qh_setfacetplane +qh_setfeasible +qh_setfree +qh_setfree2 +qh_setfreelong +qh_sethalfspace +qh_sethalfspace_all +qh_sethyperplane_det +qh_sethyperplane_gauss +qh_setin +qh_setindex +qh_setlarger +qh_setlast +qh_setnew +qh_setnew_delnthsorted +qh_setprint +qh_setreplace +qh_setsize +qh_settemp +qh_settempfree +qh_settempfree_all +qh_settemppop +qh_settemppush +qh_settruncate +qh_setunique +qh_setvoronoi_all +qh_setzero +qh_sharpnewfacets +qh_skipfacet +qh_skipfilename +qh_srand +qh_stddev +qh_strtod +qh_strtol +qh_test_appendmerge +qh_test_vneighbors +qh_tracemerge +qh_tracemerging +qh_triangulate +qh_triangulate_facet +qh_triangulate_link +qh_triangulate_mirror +qh_triangulate_null +qh_updatetested +qh_updatevertices +qh_user_memsizes +qh_version +qh_version2 +qh_vertexintersect +qh_vertexintersect_new +qh_vertexneighbors +qh_vertexridges +qh_vertexridges_facet +qh_vertexsubset +qh_voronoi_center +qh_willdelete +; Mark as DATA, otherwise links a separate qhmem. No __declspec +qhmem DATA +rbox DATA +rbox_inuse DATA diff --git a/xs/src/qhull/src/libqhull/qhull_a.h b/xs/src/qhull/src/libqhull/qhull_a.h new file mode 100644 index 000000000..729b72327 --- /dev/null +++ b/xs/src/qhull/src/libqhull/qhull_a.h @@ -0,0 +1,150 @@ +/*
      ---------------------------------
    +
    +   qhull_a.h
    +   all header files for compiling qhull with non-reentrant code
    +   included before C++ headers for user_r.h:QHULL_CRTDBG
    +
    +   see qh-qhull.htm
    +
    +   see libqhull.h for user-level definitions
    +
    +   see user.h for user-definable constants
    +
    +   defines internal functions for libqhull.c global.c
    +
    +   Copyright (c) 1993-2015 The Geometry Center.
    +   $Id: //main/2015/qhull/src/libqhull/qhull_a.h#4 $$Change: 2064 $
    +   $DateTime: 2016/01/18 12:36:08 $$Author: bbarber $
    +
    +   Notes:  grep for ((" and (" to catch fprintf("lkasdjf");
    +           full parens around (x?y:z)
    +           use '#include "libqhull/qhull_a.h"' to avoid name clashes
    +*/
    +
    +#ifndef qhDEFqhulla
    +#define qhDEFqhulla 1
    +
    +#include "libqhull.h"  /* Includes user_r.h and data types */
    +
    +#include "stat.h"
    +#include "random.h"
    +#include "mem.h"
    +#include "qset.h"
    +#include "geom.h"
    +#include "merge.h"
    +#include "poly.h"
    +#include "io.h"
    +
    +#include 
    +#include 
    +#include 
    +#include     /* some compilers will not need float.h */
    +#include 
    +#include 
    +#include 
    +#include 
    +#include 
    +/*** uncomment here and qset.c
    +     if string.h does not define memcpy()
    +#include 
    +*/
    +
    +#if qh_CLOCKtype == 2  /* defined in user.h from libqhull.h */
    +#include 
    +#include 
    +#include 
    +#endif
    +
    +#ifdef _MSC_VER  /* Microsoft Visual C++ -- warning level 4 */
    +#pragma warning( disable : 4100)  /* unreferenced formal parameter */
    +#pragma warning( disable : 4127)  /* conditional expression is constant */
    +#pragma warning( disable : 4706)  /* assignment within conditional function */
    +#pragma warning( disable : 4996)  /* function was declared deprecated(strcpy, localtime, etc.) */
    +#endif
    +
    +/* ======= -macros- =========== */
    +
    +/*----------------------------------
    +
    +  traceN((qh ferr, 0Nnnn, "format\n", vars));
    +    calls qh_fprintf if qh.IStracing >= N
    +
    +    Add debugging traps to the end of qh_fprintf
    +
    +  notes:
    +    removing tracing reduces code size but doesn't change execution speed
    +*/
    +#ifndef qh_NOtrace
    +#define trace0(args) {if (qh IStracing) qh_fprintf args;}
    +#define trace1(args) {if (qh IStracing >= 1) qh_fprintf args;}
    +#define trace2(args) {if (qh IStracing >= 2) qh_fprintf args;}
    +#define trace3(args) {if (qh IStracing >= 3) qh_fprintf args;}
    +#define trace4(args) {if (qh IStracing >= 4) qh_fprintf args;}
    +#define trace5(args) {if (qh IStracing >= 5) qh_fprintf args;}
    +#else /* qh_NOtrace */
    +#define trace0(args) {}
    +#define trace1(args) {}
    +#define trace2(args) {}
    +#define trace3(args) {}
    +#define trace4(args) {}
    +#define trace5(args) {}
    +#endif /* qh_NOtrace */
    +
    +/*----------------------------------
    +
    +  Define an unused variable to avoid compiler warnings
    +
    +  Derived from Qt's corelib/global/qglobal.h
    +
    +*/
    +
    +#if defined(__cplusplus) && defined(__INTEL_COMPILER) && !defined(QHULL_OS_WIN)
    +template 
    +inline void qhullUnused(T &x) { (void)x; }
    +#  define QHULL_UNUSED(x) qhullUnused(x);
    +#else
    +#  define QHULL_UNUSED(x) (void)x;
    +#endif
    +
    +/***** -libqhull.c prototypes (alphabetical after qhull) ********************/
    +
    +void    qh_qhull(void);
    +boolT   qh_addpoint(pointT *furthest, facetT *facet, boolT checkdist);
    +void    qh_buildhull(void);
    +void    qh_buildtracing(pointT *furthest, facetT *facet);
    +void    qh_build_withrestart(void);
    +void    qh_errexit2(int exitcode, facetT *facet, facetT *otherfacet);
    +void    qh_findhorizon(pointT *point, facetT *facet, int *goodvisible,int *goodhorizon);
    +pointT *qh_nextfurthest(facetT **visible);
    +void    qh_partitionall(setT *vertices, pointT *points,int npoints);
    +void    qh_partitioncoplanar(pointT *point, facetT *facet, realT *dist);
    +void    qh_partitionpoint(pointT *point, facetT *facet);
    +void    qh_partitionvisible(boolT allpoints, int *numpoints);
    +void    qh_precision(const char *reason);
    +void    qh_printsummary(FILE *fp);
    +
    +/***** -global.c internal prototypes (alphabetical) ***********************/
    +
    +void    qh_appendprint(qh_PRINT format);
    +void    qh_freebuild(boolT allmem);
    +void    qh_freebuffers(void);
    +void    qh_initbuffers(coordT *points, int numpoints, int dim, boolT ismalloc);
    +
    +/***** -stat.c internal prototypes (alphabetical) ***********************/
    +
    +void    qh_allstatA(void);
    +void    qh_allstatB(void);
    +void    qh_allstatC(void);
    +void    qh_allstatD(void);
    +void    qh_allstatE(void);
    +void    qh_allstatE2(void);
    +void    qh_allstatF(void);
    +void    qh_allstatG(void);
    +void    qh_allstatH(void);
    +void    qh_freebuffers(void);
    +void    qh_initbuffers(coordT *points, int numpoints, int dim, boolT ismalloc);
    +
    +#endif /* qhDEFqhulla */
    diff --git a/xs/src/qhull/src/libqhull/qhull_p-exports.def b/xs/src/qhull/src/libqhull/qhull_p-exports.def
    new file mode 100644
    index 000000000..cadf8a4fa
    --- /dev/null
    +++ b/xs/src/qhull/src/libqhull/qhull_p-exports.def
    @@ -0,0 +1,418 @@
    +; qhull_p-exports.def -- msvc module-definition file
    +;
    +;   Generated from depends.exe by cut-and-paste of exported symbols by mingw gcc
    +;   [mar'11] 399 symbols [jan'15] added 3 symbols
    +;   Annotate as DATA qh_last_random qh_qh qh_qhstat qhmem rbox rbox_inuse
    +;   Annotate as __declspec for outside access in win32 -- qh_qh qh_qhstat
    +;
    +; $Id: //main/2011/qhull/src/libqhull/qhull-exports.def#2 $$Change: 1368 $
    +; $DateTime: 2011/04/16 08:12:32 $$Author: bbarber $
    +;
    +; Define qhull_VERSION in CMakeLists.txt, Makefile, qhull-exports.def, qhull_p-exports.def, qhull_r-exports.def, and qhull-warn.pri
    +VERSION 7.0
    +EXPORTS
    +qh_addhash
    +qh_addpoint
    +qh_all_merges
    +qh_allstatA
    +qh_allstatB
    +qh_allstatC
    +qh_allstatD
    +qh_allstatE
    +qh_allstatE2
    +qh_allstatF
    +qh_allstatG
    +qh_allstatH
    +qh_allstatI
    +qh_allstatistics
    +qh_appendfacet
    +qh_appendmergeset
    +qh_appendprint
    +qh_appendvertex
    +qh_argv_to_command
    +qh_argv_to_command_size
    +qh_attachnewfacets
    +qh_backnormal
    +qh_basevertices
    +qh_build_withrestart
    +qh_buildhull
    +qh_buildtracing
    +qh_check_bestdist
    +qh_check_dupridge
    +qh_check_maxout
    +qh_check_output
    +qh_check_point
    +qh_check_points
    +qh_checkconnect
    +qh_checkconvex
    +qh_checkfacet
    +qh_checkflags
    +qh_checkflipped
    +qh_checkflipped_all
    +qh_checkpolygon
    +qh_checkvertex
    +qh_checkzero
    +qh_clear_outputflags
    +qh_clearcenters
    +qh_clock
    +qh_collectstatistics
    +qh_compare_facetarea
    +qh_compare_facetmerge
    +qh_compare_facetvisit
    +qh_compare_vertexpoint
    +qh_compareangle
    +qh_comparemerge
    +qh_comparevisit
    +qh_copyfilename
    +qh_copynonconvex
    +qh_copypoints
    +qh_countfacets
    +qh_createsimplex
    +qh_crossproduct
    +qh_degen_redundant_facet
    +qh_degen_redundant_neighbors
    +qh_deletevisible
    +qh_delfacet
    +qh_delridge
    +qh_delvertex
    +qh_determinant
    +qh_detjoggle
    +qh_detroundoff
    +qh_detsimplex
    +qh_detvnorm
    +qh_detvridge
    +qh_detvridge3
    +qh_dfacet
    +qh_distnorm
    +qh_distplane
    +qh_distround
    +qh_divzero
    +qh_dvertex
    +qh_eachvoronoi
    +qh_eachvoronoi_all
    +qh_errexit
    +qh_errexit2
    +qh_errexit_rbox
    +qh_errprint
    +qh_exit
    +qh_facet2point
    +qh_facet3vertex
    +qh_facetarea
    +qh_facetarea_simplex
    +qh_facetcenter
    +qh_facetintersect
    +qh_facetvertices
    +qh_find_newvertex
    +qh_findbest
    +qh_findbest_test
    +qh_findbestfacet
    +qh_findbesthorizon
    +qh_findbestlower
    +qh_findbestneighbor
    +qh_findbestnew
    +qh_findfacet_all
    +qh_findgood
    +qh_findgood_all
    +qh_findgooddist
    +qh_findhorizon
    +qh_flippedmerges
    +qh_forcedmerges
    +qh_fprintf
    +qh_fprintf_rbox
    +qh_fprintf_stderr
    +qh_free
    +qh_freebuffers
    +qh_freebuild
    +qh_freeqhull
    +qh_freeqhull2
    +qh_freestatistics
    +qh_furthestnext
    +qh_furthestout
    +qh_gausselim
    +qh_geomplanes
    +qh_getangle
    +qh_getarea
    +qh_getcenter
    +qh_getcentrum
    +qh_getdistance
    +qh_gethash
    +qh_getmergeset
    +qh_getmergeset_initial
    +qh_gram_schmidt
    +qh_hashridge
    +qh_hashridge_find
    +qh_infiniteloop
    +qh_init_A
    +qh_init_B
    +qh_init_qhull_command
    +qh_initbuild
    +qh_initflags
    +qh_initialhull
    +qh_initialvertices
    +qh_initqhull_buffers
    +qh_initqhull_globals
    +qh_initqhull_mem
    +qh_initqhull_outputflags
    +qh_initqhull_start
    +qh_initqhull_start2
    +qh_initstatistics
    +qh_initthresholds
    +qh_inthresholds
    +qh_isvertex
    +qh_joggleinput
    +; Mark as DATA, otherwise links a separate qh_last_random.  No __declspec.
    +qh_last_random      DATA
    +qh_lib_check
    +qh_makenew_nonsimplicial
    +qh_makenew_simplicial
    +qh_makenewfacet
    +qh_makenewfacets
    +qh_makenewplanes
    +qh_makeridges
    +qh_malloc
    +qh_mark_dupridges
    +qh_markkeep
    +qh_markvoronoi
    +qh_matchduplicates
    +qh_matchneighbor
    +qh_matchnewfacets
    +qh_matchvertices
    +qh_maxabsval
    +qh_maxmin
    +qh_maxouter
    +qh_maxsimplex
    +qh_maydropneighbor
    +qh_memalloc
    +qh_memfree
    +qh_memfreeshort
    +qh_meminit
    +qh_meminitbuffers
    +qh_memsetup
    +qh_memsize
    +qh_memstatistics
    +qh_memtotal
    +qh_merge_degenredundant
    +qh_merge_nonconvex
    +qh_mergecycle
    +qh_mergecycle_all
    +qh_mergecycle_facets
    +qh_mergecycle_neighbors
    +qh_mergecycle_ridges
    +qh_mergecycle_vneighbors
    +qh_mergefacet
    +qh_mergefacet2d
    +qh_mergeneighbors
    +qh_mergeridges
    +qh_mergesimplex
    +qh_mergevertex_del
    +qh_mergevertex_neighbors
    +qh_mergevertices
    +qh_minabsval
    +qh_mindiff
    +qh_nearcoplanar
    +qh_nearvertex
    +qh_neighbor_intersections
    +qh_new_qhull
    +qh_newfacet
    +qh_newhashtable
    +qh_newridge
    +qh_newstats
    +qh_newvertex
    +qh_newvertices
    +qh_nextfurthest
    +qh_nextridge3d
    +qh_normalize
    +qh_normalize2
    +qh_nostatistic
    +qh_option
    +qh_order_vertexneighbors
    +qh_orientoutside
    +qh_out1
    +qh_out2n
    +qh_out3n
    +qh_outcoplanar
    +qh_outerinner
    +qh_partitionall
    +qh_partitioncoplanar
    +qh_partitionpoint
    +qh_partitionvisible
    +qh_point
    +qh_point_add
    +qh_pointdist
    +qh_pointfacet
    +qh_pointid
    +qh_pointvertex
    +qh_postmerge
    +qh_precision
    +qh_premerge
    +qh_prepare_output
    +qh_prependfacet
    +qh_printafacet
    +qh_printallstatistics
    +qh_printbegin
    +qh_printcenter
    +qh_printcentrum
    +qh_printend
    +qh_printend4geom
    +qh_printextremes
    +qh_printextremes_2d
    +qh_printextremes_d
    +qh_printfacet
    +qh_printfacet2geom
    +qh_printfacet2geom_points
    +qh_printfacet2math
    +qh_printfacet3geom_nonsimplicial
    +qh_printfacet3geom_points
    +qh_printfacet3geom_simplicial
    +qh_printfacet3math
    +qh_printfacet3vertex
    +qh_printfacet4geom_nonsimplicial
    +qh_printfacet4geom_simplicial
    +qh_printfacetNvertex_nonsimplicial
    +qh_printfacetNvertex_simplicial
    +qh_printfacetheader
    +qh_printfacetlist
    +qh_printfacetridges
    +qh_printfacets
    +qh_printhashtable
    +qh_printhelp_degenerate
    +qh_printhelp_narrowhull
    +qh_printhelp_singular
    +qh_printhyperplaneintersection
    +qh_printline3geom
    +qh_printlists
    +qh_printmatrix
    +qh_printneighborhood
    +qh_printpoint
    +qh_printpoint3
    +qh_printpointid
    +qh_printpoints
    +qh_printpoints_out
    +qh_printpointvect
    +qh_printpointvect2
    +qh_printridge
    +qh_printspheres
    +qh_printstatistics
    +qh_printstatlevel
    +qh_printstats
    +qh_printsummary
    +qh_printvdiagram
    +qh_printvdiagram2
    +qh_printvertex
    +qh_printvertexlist
    +qh_printvertices
    +qh_printvneighbors
    +qh_printvnorm
    +qh_printvoronoi
    +qh_printvridge
    +qh_produce_output
    +qh_produce_output2
    +qh_projectdim3
    +qh_projectinput
    +qh_projectpoint
    +qh_projectpoints
    +; Mark as DATA, otherwise links a separate qh_qh.  qh_qh and qh_qhstat requires __declspec
    +qh_qh               DATA
    +qh_qhstat           DATA
    +qh_qhull
    +qh_rand
    +qh_randomfactor
    +qh_randommatrix
    +qh_rboxpoints
    +qh_readfeasible
    +qh_readpoints
    +qh_reducevertices
    +qh_redundant_vertex
    +qh_remove_extravertices
    +qh_removefacet
    +qh_removevertex
    +qh_rename_sharedvertex
    +qh_renameridgevertex
    +qh_renamevertex
    +qh_resetlists
    +qh_restore_qhull
    +qh_rotateinput
    +qh_rotatepoints
    +qh_roundi
    +qh_save_qhull
    +qh_scaleinput
    +qh_scalelast
    +qh_scalepoints
    +qh_setaddnth
    +qh_setaddsorted
    +qh_setappend
    +qh_setappend2ndlast
    +qh_setappend_set
    +qh_setcheck
    +qh_setcompact
    +qh_setcopy
    +qh_setdel
    +qh_setdelaunay
    +qh_setdellast
    +qh_setdelnth
    +qh_setdelnthsorted
    +qh_setdelsorted
    +qh_setduplicate
    +qh_setequal
    +qh_setequal_except
    +qh_setequal_skip
    +qh_setfacetplane
    +qh_setfeasible
    +qh_setfree
    +qh_setfree2
    +qh_setfreelong
    +qh_sethalfspace
    +qh_sethalfspace_all
    +qh_sethyperplane_det
    +qh_sethyperplane_gauss
    +qh_setin
    +qh_setindex
    +qh_setlarger
    +qh_setlast
    +qh_setnew
    +qh_setnew_delnthsorted
    +qh_setprint
    +qh_setreplace
    +qh_setsize
    +qh_settemp
    +qh_settempfree
    +qh_settempfree_all
    +qh_settemppop
    +qh_settemppush
    +qh_settruncate
    +qh_setunique
    +qh_setvoronoi_all
    +qh_setzero
    +qh_sharpnewfacets
    +qh_skipfacet
    +qh_skipfilename
    +qh_srand
    +qh_stddev
    +qh_strtod
    +qh_strtol
    +qh_test_appendmerge
    +qh_test_vneighbors
    +qh_tracemerge
    +qh_tracemerging
    +qh_triangulate
    +qh_triangulate_facet
    +qh_triangulate_link
    +qh_triangulate_mirror
    +qh_triangulate_null
    +qh_updatetested
    +qh_updatevertices
    +qh_user_memsizes
    +qh_version
    +qh_version2
    +qh_vertexintersect
    +qh_vertexintersect_new
    +qh_vertexneighbors
    +qh_vertexridges
    +qh_vertexridges_facet
    +qh_vertexsubset
    +qh_voronoi_center
    +qh_willdelete
    +; Mark as DATA, otherwise links a separate qhmem.  No __declspec
    +qhmem                   DATA
    +rbox			DATA
    +rbox_inuse              DATA
    diff --git a/xs/src/qhull/src/libqhull/qset.c b/xs/src/qhull/src/libqhull/qset.c
    new file mode 100644
    index 000000000..a969252a7
    --- /dev/null
    +++ b/xs/src/qhull/src/libqhull/qset.c
    @@ -0,0 +1,1340 @@
    +/*
      ---------------------------------
    +
    +   qset.c
    +   implements set manipulations needed for quickhull
    +
    +   see qh-set.htm and qset.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/qset.c#3 $$Change: 2062 $
    +   $DateTime: 2016/01/17 13:13:18 $$Author: bbarber $
    +*/
    +
    +#include "user.h" /* for QHULL_CRTDBG */
    +#include "qset.h"
    +#include "mem.h"
    +#include 
    +#include 
    +/*** uncomment here and qhull_a.h
    +     if string.h does not define memcpy()
    +#include 
    +*/
    +
    +#ifndef qhDEFlibqhull
    +typedef struct ridgeT ridgeT;
    +typedef struct facetT facetT;
    +void    qh_errexit(int exitcode, facetT *, ridgeT *);
    +void    qh_fprintf(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 ===================*/
    +
    +/*----------------------------------
    +
    +  qh_setaddnth( 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(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(setp);
    +    sizep= SETsizeaddr_(*setp);
    +  }
    +  oldsize= sizep->i - 1;
    +  if (nth < 0 || nth > oldsize) {
    +    qh_fprintf(qhmem.ferr, 6171, "qhull internal error (qh_setaddnth): nth %d is out-of-bounds for set:\n", nth);
    +    qh_setprint(qhmem.ferr, "", *setp);
    +    qh_errexit(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 */
    +
    +
    +/*----------------------------------
    +
    +  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(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(setp, newindex, newelem);
    +} /* setaddsorted */
    +
    +
    +/*---------------------------------
    +
    +  qh_setappend( 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(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(setp);
    +    sizep= SETsizeaddr_(*setp);
    +  }
    +  count= (sizep->i)++ - 1;
    +  endp= (setelemT *)SETelemaddr_(*setp, count, void);
    +  (endp++)->p= newelem;
    +  endp->p= NULL;
    +} /* setappend */
    +
    +/*---------------------------------
    +
    +  qh_setappend_set( 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(setT **setp, setT *setA) {
    +  int sizeA, size;
    +  setT *oldset;
    +  setelemT *sizep;
    +
    +  if (!setA)
    +    return;
    +  SETreturnsize_(setA, sizeA);
    +  if (!*setp)
    +    *setp= qh_setnew(sizeA);
    +  sizep= SETsizeaddr_(*setp);
    +  if (!(size= sizep->i))
    +    size= (*setp)->maxsize;
    +  else
    +    size--;
    +  if (size + sizeA > (*setp)->maxsize) {
    +    oldset= *setp;
    +    *setp= qh_setcopy(oldset, sizeA);
    +    qh_setfree(&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 */
    +
    +
    +/*---------------------------------
    +
    +  qh_setappend2ndlast( 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(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(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 */
    +
    +/*---------------------------------
    +
    +  qh_setcheck( 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(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(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(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(qhmem.ferr, "ERRONEOUS", set);
    +    qh_errexit(qhmem_ERRqhull, NULL, NULL);
    +  }
    +} /* setcheck */
    +
    +
    +/*---------------------------------
    +
    +  qh_setcompact( 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(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(set, (int)(destp-firstp));   /* WARN64 */
    +} /* setcompact */
    +
    +
    +/*---------------------------------
    +
    +  qh_setcopy( 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(setT *set, int extra) {
    +  setT *newset;
    +  int size;
    +
    +  if (extra < 0)
    +    extra= 0;
    +  SETreturnsize_(set, size);
    +  newset= qh_setnew(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 */
    +
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +
    +/*---------------------------------
    +
    +  qh_setdelnth( 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(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(qhmem.ferr, 6174, "qhull internal error (qh_setdelnth): nth %d is out-of-bounds for set:\n", nth);
    +    qh_setprint(qhmem.ferr, "", set);
    +    qh_errexit(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 */
    +
    +/*---------------------------------
    +
    +  qh_setdelnthsorted( 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(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(qhmem.ferr, 6175, "qhull internal error (qh_setdelnthsorted): nth %d is out-of-bounds for set:\n", nth);
    +    qh_setprint(qhmem.ferr, "", set);
    +    qh_errexit(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 */
    +
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +
    +/*---------------------------------
    +
    +  qh_setduplicate( 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(setT *set, int elemsize) {
    +  void          *elem, **elemp, *newElem;
    +  setT          *newSet;
    +  int           size;
    +
    +  if (!(size= qh_setsize(set)))
    +    return NULL;
    +  newSet= qh_setnew(size);
    +  FOREACHelem_(set) {
    +    newElem= qh_memalloc(elemsize);
    +    memcpy(newElem, elem, (size_t)elemsize);
    +    qh_setappend(&newSet, newElem);
    +  }
    +  return newSet;
    +} /* setduplicate */
    +
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +
    +/*---------------------------------
    +
    +  qh_setfree( 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(setT **setp) {
    +  int size;
    +  void **freelistp;  /* used if !qh_NOmem by qh_memfree_() */
    +
    +  if (*setp) {
    +    size= sizeof(setT) + ((*setp)->maxsize)*SETelemsize;
    +    if (size <= qhmem.LASTsize) {
    +      qh_memfree_(*setp, size, freelistp);
    +    }else
    +      qh_memfree(*setp, size);
    +    *setp= NULL;
    +  }
    +} /* setfree */
    +
    +
    +/*---------------------------------
    +
    +  qh_setfree2( 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(setT **setp, int elemsize) {
    +  void          *elem, **elemp;
    +
    +  FOREACHelem_(*setp)
    +    qh_memfree(elem, elemsize);
    +  qh_setfree(setp);
    +} /* setfree2 */
    +
    +
    +
    +/*---------------------------------
    +
    +  qh_setfreelong( 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(setT **setp) {
    +  int size;
    +
    +  if (*setp) {
    +    size= sizeof(setT) + ((*setp)->maxsize)*SETelemsize;
    +    if (size > qhmem.LASTsize) {
    +      qh_memfree(*setp, size);
    +      *setp= NULL;
    +    }
    +  }
    +} /* setfreelong */
    +
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +
    +/*---------------------------------
    +
    +  qh_setlarger( oldsetp )
    +    returns a larger set that contains all elements of *oldsetp
    +
    +  notes:
    +    the set is at least twice as large
    +    if temp set, updates 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(setT **oldsetp) {
    +  int size= 1;
    +  setT *newset, *set, **setp, *oldset;
    +  setelemT *sizep;
    +  setelemT *newp, *oldp;
    +
    +  if (*oldsetp) {
    +    oldset= *oldsetp;
    +    SETreturnsize_(oldset, size);
    +    qhmem.cntlarger++;
    +    qhmem.totlarger += size+1;
    +    newset= qh_setnew(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 *)qhmem.tempstack) {
    +      if (set == oldset)
    +        *(setp-1)= newset;
    +    }
    +    qh_setfree(oldsetp);
    +  }else
    +    newset= qh_setnew(3);
    +  *oldsetp= newset;
    +} /* setlarger */
    +
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +
    +/*---------------------------------
    +
    +  qh_setnew( 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(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 <= qhmem.LASTsize) {
    +    qh_memalloc_(size, freelistp, set, setT);
    +#ifndef qh_NOmem
    +    sizereceived= qhmem.sizetable[ qhmem.indextable[size]];
    +    if (sizereceived > size)
    +      setsize += (sizereceived - size)/SETelemsize;
    +#endif
    +  }else
    +    set= (setT*)qh_memalloc(size);
    +  set->maxsize= setsize;
    +  set->e[setsize].i= 1;
    +  set->e[0].p= NULL;
    +  return(set);
    +} /* setnew */
    +
    +
    +/*---------------------------------
    +
    +  qh_setnew_delnthsorted( 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(setT *set, int size, int nth, int prepend) {
    +  setT *newset;
    +  void **oldp, **newp;
    +  int tailsize= size - nth -1, newsize;
    +
    +  if (tailsize < 0) {
    +    qh_fprintf(qhmem.ferr, 6176, "qhull internal error (qh_setnew_delnthsorted): nth %d is out-of-bounds for set:\n", nth);
    +    qh_setprint(qhmem.ferr, "", set);
    +    qh_errexit(qhmem_ERRqhull, NULL, NULL);
    +  }
    +  newsize= size-1 + prepend;
    +  newset= qh_setnew(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 */
    +
    +
    +/*---------------------------------
    +
    +  qh_setprint( fp, string, set )
    +    print set elements to fp with identifying string
    +
    +  notes:
    +    never errors
    +*/
    +void qh_setprint(FILE *fp, const char* string, setT *set) {
    +  int size, k;
    +
    +  if (!set)
    +    qh_fprintf(fp, 9346, "%s set is null\n", string);
    +  else {
    +    SETreturnsize_(set, size);
    +    qh_fprintf(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(fp, 9348, " %p", set->e[k].p);
    +    qh_fprintf(fp, 9349, "\n");
    +  }
    +} /* setprint */
    +
    +/*---------------------------------
    +
    +  qh_setreplace( 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(setT *set, void *oldelem, void *newelem) {
    +  void **elemp;
    +
    +  elemp= SETaddr_(set, void);
    +  while (*elemp != oldelem && *elemp)
    +    elemp++;
    +  if (*elemp)
    +    *elemp= newelem;
    +  else {
    +    qh_fprintf(qhmem.ferr, 6177, "qhull internal error (qh_setreplace): elem %p not found in set\n",
    +       oldelem);
    +    qh_setprint(qhmem.ferr, "", set);
    +    qh_errexit(qhmem_ERRqhull, NULL, NULL);
    +  }
    +} /* setreplace */
    +
    +
    +/*---------------------------------
    +
    +  qh_setsize( 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.c] and QhullSetBase::count
    +
    +  design:
    +    determine actual size of set from maxsize
    +*/
    +int qh_setsize(setT *set) {
    +  int size;
    +  setelemT *sizep;
    +
    +  if (!set)
    +    return(0);
    +  sizep= SETsizeaddr_(set);
    +  if ((size= sizep->i)) {
    +    size--;
    +    if (size > set->maxsize) {
    +      qh_fprintf(qhmem.ferr, 6178, "qhull internal error (qh_setsize): current set size %d is greater than maximum size %d\n",
    +               size, set->maxsize);
    +      qh_setprint(qhmem.ferr, "set: ", set);
    +      qh_errexit(qhmem_ERRqhull, NULL, NULL);
    +    }
    +  }else
    +    size= set->maxsize;
    +  return size;
    +} /* setsize */
    +
    +/*---------------------------------
    +
    +  qh_settemp( setsize )
    +    return a stacked, temporary set of upto setsize elements
    +
    +  notes:
    +    use settempfree or settempfree_all to release from qhmem.tempstack
    +    see also qh_setnew
    +
    +  design:
    +    allocate set
    +    append to qhmem.tempstack
    +
    +*/
    +setT *qh_settemp(int setsize) {
    +  setT *newset;
    +
    +  newset= qh_setnew(setsize);
    +  qh_setappend(&qhmem.tempstack, newset);
    +  if (qhmem.IStracing >= 5)
    +    qh_fprintf(qhmem.ferr, 8123, "qh_settemp: temp set %p of %d elements, depth %d\n",
    +       newset, newset->maxsize, qh_setsize(qhmem.tempstack));
    +  return newset;
    +} /* settemp */
    +
    +/*---------------------------------
    +
    +  qh_settempfree( set )
    +    free temporary set at top of 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 qhmem.tempstack
    +    free it
    +*/
    +void qh_settempfree(setT **set) {
    +  setT *stackedset;
    +
    +  if (!*set)
    +    return;
    +  stackedset= qh_settemppop();
    +  if (stackedset != *set) {
    +    qh_settemppush(stackedset);
    +    qh_fprintf(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(*set), qh_setsize(qhmem.tempstack)+1,
    +             stackedset, qh_setsize(stackedset));
    +    qh_errexit(qhmem_ERRqhull, NULL, NULL);
    +  }
    +  qh_setfree(set);
    +} /* settempfree */
    +
    +/*---------------------------------
    +
    +  qh_settempfree_all(  )
    +    free all temporary sets in qhmem.tempstack
    +
    +  design:
    +    for each set in tempstack
    +      free set
    +    free qhmem.tempstack
    +*/
    +void qh_settempfree_all(void) {
    +  setT *set, **setp;
    +
    +  FOREACHset_(qhmem.tempstack)
    +    qh_setfree(&set);
    +  qh_setfree(&qhmem.tempstack);
    +} /* settempfree_all */
    +
    +/*---------------------------------
    +
    +  qh_settemppop(  )
    +    pop and return temporary set from qhmem.tempstack
    +
    +  notes:
    +    the returned set is permanent
    +
    +  design:
    +    pop and check top of qhmem.tempstack
    +*/
    +setT *qh_settemppop(void) {
    +  setT *stackedset;
    +
    +  stackedset= (setT*)qh_setdellast(qhmem.tempstack);
    +  if (!stackedset) {
    +    qh_fprintf(qhmem.ferr, 6180, "qhull internal error (qh_settemppop): pop from empty temporary stack\n");
    +    qh_errexit(qhmem_ERRqhull, NULL, NULL);
    +  }
    +  if (qhmem.IStracing >= 5)
    +    qh_fprintf(qhmem.ferr, 8124, "qh_settemppop: depth %d temp set %p of %d elements\n",
    +       qh_setsize(qhmem.tempstack)+1, stackedset, qh_setsize(stackedset));
    +  return stackedset;
    +} /* settemppop */
    +
    +/*---------------------------------
    +
    +  qh_settemppush( set )
    +    push temporary set unto qhmem.tempstack (makes it temporary)
    +
    +  notes:
    +    duplicates settemp() for tracing
    +
    +  design:
    +    append set to tempstack
    +*/
    +void qh_settemppush(setT *set) {
    +  if (!set) {
    +    qh_fprintf(qhmem.ferr, 6267, "qhull error (qh_settemppush): can not push a NULL temp\n");
    +    qh_errexit(qhmem_ERRqhull, NULL, NULL);
    +  }
    +  qh_setappend(&qhmem.tempstack, set);
    +  if (qhmem.IStracing >= 5)
    +    qh_fprintf(qhmem.ferr, 8125, "qh_settemppush: depth %d temp set %p of %d elements\n",
    +      qh_setsize(qhmem.tempstack), set, qh_setsize(set));
    +} /* settemppush */
    +
    +
    +/*---------------------------------
    +
    +  qh_settruncate( 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(setT *set, int size) {
    +
    +  if (size < 0 || size > set->maxsize) {
    +    qh_fprintf(qhmem.ferr, 6181, "qhull internal error (qh_settruncate): size %d out of bounds for set:\n", size);
    +    qh_setprint(qhmem.ferr, "", set);
    +    qh_errexit(qhmem_ERRqhull, NULL, NULL);
    +  }
    +  set->e[set->maxsize].i= size+1;   /* maybe overwritten */
    +  set->e[size].p= NULL;
    +} /* settruncate */
    +
    +/*---------------------------------
    +
    +  qh_setunique( 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(setT **set, void *elem) {
    +
    +  if (!qh_setin(*set, elem)) {
    +    qh_setappend(set, elem);
    +    return 1;
    +  }
    +  return 0;
    +} /* setunique */
    +
    +/*---------------------------------
    +
    +  qh_setzero( 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(setT *set, int idx, int size) {
    +  int count;
    +
    +  if (idx < 0 || idx >= size || size > set->maxsize) {
    +    qh_fprintf(qhmem.ferr, 6182, "qhull internal error (qh_setzero): index %d or size %d out of bounds for set:\n", idx, size);
    +    qh_setprint(qhmem.ferr, "", set);
    +    qh_errexit(qhmem_ERRqhull, NULL, NULL);
    +  }
    +  set->e[set->maxsize].i=  size+1;  /* may be overwritten */
    +  count= size - idx + 1;   /* +1 for NULL terminator */
    +  memset((char *)SETelemaddr_(set, idx, void), 0, (size_t)count * SETelemsize);
    +} /* setzero */
    +
    +
    diff --git a/xs/src/qhull/src/libqhull/qset.h b/xs/src/qhull/src/libqhull/qset.h
    new file mode 100644
    index 000000000..7e4e7d14f
    --- /dev/null
    +++ b/xs/src/qhull/src/libqhull/qset.h
    @@ -0,0 +1,490 @@
    +/*
      ---------------------------------
    +
    +   qset.h
    +     header file for qset.c that implements set
    +
    +   see qh-set.htm and qset.c
    +
    +   only uses mem.c, malloc/free
    +
    +   for error handling, writes message and calls
    +      qh_errexit(qhmem_ERRqhull, NULL, NULL);
    +
    +   set operations satisfy the following properties:
    +    - sets have a max size, the actual size (if different) is stored at the end
    +    - every set is NULL terminated
    +    - sets may be sorted or unsorted, the caller must distinguish this
    +
    +   Copyright (c) 1993-2015 The Geometry Center.
    +   $Id: //main/2015/qhull/src/libqhull/qset.h#2 $$Change: 2062 $
    +   $DateTime: 2016/01/17 13:13:18 $$Author: bbarber $
    +*/
    +
    +#ifndef qhDEFset
    +#define qhDEFset 1
    +
    +#include 
    +
    +/*================= -structures- ===============*/
    +
    +#ifndef DEFsetT
    +#define DEFsetT 1
    +typedef struct setT setT;   /* a set is a sorted or unsorted array of pointers */
    +#endif
    +
    +/* [jan'15] Decided not to use countT.  Most sets are small.  The code uses signed tests */
    +
    +/*------------------------------------------
    +
    +setT
    +  a set or list of pointers with maximum size and actual size.
    +
    +variations:
    +  unsorted, unique   -- a list of unique pointers with NULL terminator
    +                           user guarantees uniqueness
    +  sorted             -- a sorted list of unique pointers with NULL terminator
    +                           qset.c guarantees uniqueness
    +  unsorted           -- a list of pointers terminated with NULL
    +  indexed            -- an array of pointers with NULL elements
    +
    +structure for set of n elements:
    +
    +        --------------
    +        |  maxsize
    +        --------------
    +        |  e[0] - a pointer, may be NULL for indexed sets
    +        --------------
    +        |  e[1]
    +
    +        --------------
    +        |  ...
    +        --------------
    +        |  e[n-1]
    +        --------------
    +        |  e[n] = NULL
    +        --------------
    +        |  ...
    +        --------------
    +        |  e[maxsize] - n+1 or NULL (determines actual size of set)
    +        --------------
    +
    +*/
    +
    +/*-- setelemT -- internal type to allow both pointers and indices
    +*/
    +typedef union setelemT setelemT;
    +union setelemT {
    +  void    *p;
    +  int      i;         /* integer used for e[maxSize] */
    +};
    +
    +struct setT {
    +  int maxsize;          /* maximum number of elements (except NULL) */
    +  setelemT e[1];        /* array of pointers, tail is NULL */
    +                        /* last slot (unless NULL) is actual size+1
    +                           e[maxsize]==NULL or e[e[maxsize]-1]==NULL */
    +                        /* this may generate a warning since e[] contains
    +                           maxsize elements */
    +};
    +
    +/*=========== -constants- =========================*/
    +
    +/*-------------------------------------
    +
    +  SETelemsize
    +    size of a set element in bytes
    +*/
    +#define SETelemsize ((int)sizeof(setelemT))
    +
    +
    +/*=========== -macros- =========================*/
    +
    +/*-------------------------------------
    +
    +   FOREACHsetelement_(type, set, variable)
    +     define FOREACH iterator
    +
    +   declare:
    +     assumes *variable and **variablep are declared
    +     no space in "variable)" [DEC Alpha cc compiler]
    +
    +   each iteration:
    +     variable is set element
    +     variablep is one beyond variable.
    +
    +   to repeat an element:
    +     variablep--; / *repeat* /
    +
    +   at exit:
    +     variable is NULL at end of loop
    +
    +   example:
    +     #define FOREACHfacet_( facets ) FOREACHsetelement_( facetT, facets, facet )
    +
    +   notes:
    +     use FOREACHsetelement_i_() if need index or include NULLs
    +
    +   WARNING:
    +     nested loops can't use the same variable (define another FOREACH)
    +
    +     needs braces if nested inside another FOREACH
    +     this includes intervening blocks, e.g. FOREACH...{ if () FOREACH...} )
    +*/
    +#define FOREACHsetelement_(type, set, variable) \
    +        if (((variable= NULL), set)) for (\
    +          variable##p= (type **)&((set)->e[0].p); \
    +          (variable= *variable##p++);)
    +
    +/*------------------------------------------
    +
    +   FOREACHsetelement_i_(type, set, variable)
    +     define indexed FOREACH iterator
    +
    +   declare:
    +     type *variable, variable_n, variable_i;
    +
    +   each iteration:
    +     variable is set element, may be NULL
    +     variable_i is index, variable_n is qh_setsize()
    +
    +   to repeat an element:
    +     variable_i--; variable_n-- repeats for deleted element
    +
    +   at exit:
    +     variable==NULL and variable_i==variable_n
    +
    +   example:
    +     #define FOREACHfacet_i_( facets ) FOREACHsetelement_i_( facetT, facets, facet )
    +
    +   WARNING:
    +     nested loops can't use the same variable (define another FOREACH)
    +
    +     needs braces if nested inside another FOREACH
    +     this includes intervening blocks, e.g. FOREACH...{ if () FOREACH...} )
    +*/
    +#define FOREACHsetelement_i_(type, set, variable) \
    +        if (((variable= NULL), set)) for (\
    +          variable##_i= 0, variable= (type *)((set)->e[0].p), \
    +                   variable##_n= qh_setsize(set);\
    +          variable##_i < variable##_n;\
    +          variable= (type *)((set)->e[++variable##_i].p) )
    +
    +/*----------------------------------------
    +
    +   FOREACHsetelementreverse_(type, set, variable)-
    +     define FOREACH iterator in reverse order
    +
    +   declare:
    +     assumes *variable and **variablep are declared
    +     also declare 'int variabletemp'
    +
    +   each iteration:
    +     variable is set element
    +
    +   to repeat an element:
    +     variabletemp++; / *repeat* /
    +
    +   at exit:
    +     variable is NULL
    +
    +   example:
    +     #define FOREACHvertexreverse_( vertices ) FOREACHsetelementreverse_( vertexT, vertices, vertex )
    +
    +   notes:
    +     use FOREACHsetelementreverse12_() to reverse first two elements
    +     WARNING: needs braces if nested inside another FOREACH
    +*/
    +#define FOREACHsetelementreverse_(type, set, variable) \
    +        if (((variable= NULL), set)) for (\
    +           variable##temp= qh_setsize(set)-1, variable= qh_setlast(set);\
    +           variable; variable= \
    +           ((--variable##temp >= 0) ? SETelemt_(set, variable##temp, type) : NULL))
    +
    +/*-------------------------------------
    +
    +   FOREACHsetelementreverse12_(type, set, variable)-
    +     define FOREACH iterator with e[1] and e[0] reversed
    +
    +   declare:
    +     assumes *variable and **variablep are declared
    +
    +   each iteration:
    +     variable is set element
    +     variablep is one after variable.
    +
    +   to repeat an element:
    +     variablep--; / *repeat* /
    +
    +   at exit:
    +     variable is NULL at end of loop
    +
    +   example
    +     #define FOREACHvertexreverse12_( vertices ) FOREACHsetelementreverse12_( vertexT, vertices, vertex )
    +
    +   notes:
    +     WARNING: needs braces if nested inside another FOREACH
    +*/
    +#define FOREACHsetelementreverse12_(type, set, variable) \
    +        if (((variable= NULL), set)) for (\
    +          variable##p= (type **)&((set)->e[1].p); \
    +          (variable= *variable##p); \
    +          variable##p == ((type **)&((set)->e[0].p))?variable##p += 2: \
    +              (variable##p == ((type **)&((set)->e[1].p))?variable##p--:variable##p++))
    +
    +/*-------------------------------------
    +
    +   FOREACHelem_( set )-
    +     iterate elements in a set
    +
    +   declare:
    +     void *elem, *elemp;
    +
    +   each iteration:
    +     elem is set element
    +     elemp is one beyond
    +
    +   to repeat an element:
    +     elemp--; / *repeat* /
    +
    +   at exit:
    +     elem == NULL at end of loop
    +
    +   example:
    +     FOREACHelem_(set) {
    +
    +   notes:
    +     WARNING: needs braces if nested inside another FOREACH
    +*/
    +#define FOREACHelem_(set) FOREACHsetelement_(void, set, elem)
    +
    +/*-------------------------------------
    +
    +   FOREACHset_( set )-
    +     iterate a set of sets
    +
    +   declare:
    +     setT *set, **setp;
    +
    +   each iteration:
    +     set is set element
    +     setp is one beyond
    +
    +   to repeat an element:
    +     setp--; / *repeat* /
    +
    +   at exit:
    +     set == NULL at end of loop
    +
    +   example
    +     FOREACHset_(sets) {
    +
    +   notes:
    +     WARNING: needs braces if nested inside another FOREACH
    +*/
    +#define FOREACHset_(sets) FOREACHsetelement_(setT, sets, set)
    +
    +/*-------------------------------------------
    +
    +   SETindex_( set, elem )
    +     return index of elem in set
    +
    +   notes:
    +     for use with FOREACH iteration
    +     WARN64 -- Maximum set size is 2G
    +
    +   example:
    +     i= SETindex_(ridges, ridge)
    +*/
    +#define SETindex_(set, elem) ((int)((void **)elem##p - (void **)&(set)->e[1].p))
    +
    +/*-----------------------------------------
    +
    +   SETref_( elem )
    +     l.h.s. for modifying the current element in a FOREACH iteration
    +
    +   example:
    +     SETref_(ridge)= anotherridge;
    +*/
    +#define SETref_(elem) (elem##p[-1])
    +
    +/*-----------------------------------------
    +
    +   SETelem_(set, n)
    +     return the n'th element of set
    +
    +   notes:
    +      assumes that n is valid [0..size] and that set is defined
    +      use SETelemt_() for type cast
    +*/
    +#define SETelem_(set, n)           ((set)->e[n].p)
    +
    +/*-----------------------------------------
    +
    +   SETelemt_(set, n, type)
    +     return the n'th element of set as a type
    +
    +   notes:
    +      assumes that n is valid [0..size] and that set is defined
    +*/
    +#define SETelemt_(set, n, type)    ((type*)((set)->e[n].p))
    +
    +/*-----------------------------------------
    +
    +   SETelemaddr_(set, n, type)
    +     return address of the n'th element of a set
    +
    +   notes:
    +      assumes that n is valid [0..size] and set is defined
    +*/
    +#define SETelemaddr_(set, n, type) ((type **)(&((set)->e[n].p)))
    +
    +/*-----------------------------------------
    +
    +   SETfirst_(set)
    +     return first element of set
    +
    +*/
    +#define SETfirst_(set)             ((set)->e[0].p)
    +
    +/*-----------------------------------------
    +
    +   SETfirstt_(set, type)
    +     return first element of set as a type
    +
    +*/
    +#define SETfirstt_(set, type)      ((type*)((set)->e[0].p))
    +
    +/*-----------------------------------------
    +
    +   SETsecond_(set)
    +     return second element of set
    +
    +*/
    +#define SETsecond_(set)            ((set)->e[1].p)
    +
    +/*-----------------------------------------
    +
    +   SETsecondt_(set, type)
    +     return second element of set as a type
    +*/
    +#define SETsecondt_(set, type)     ((type*)((set)->e[1].p))
    +
    +/*-----------------------------------------
    +
    +   SETaddr_(set, type)
    +       return address of set's elements
    +*/
    +#define SETaddr_(set,type)         ((type **)(&((set)->e[0].p)))
    +
    +/*-----------------------------------------
    +
    +   SETreturnsize_(set, size)
    +     return size of a set
    +
    +   notes:
    +      set must be defined
    +      use qh_setsize(set) unless speed is critical
    +*/
    +#define SETreturnsize_(set, size) (((size)= ((set)->e[(set)->maxsize].i))?(--(size)):((size)= (set)->maxsize))
    +
    +/*-----------------------------------------
    +
    +   SETempty_(set)
    +     return true(1) if set is empty
    +
    +   notes:
    +      set may be NULL
    +*/
    +#define SETempty_(set)            (!set || (SETfirst_(set) ? 0 : 1))
    +
    +/*---------------------------------
    +
    +  SETsizeaddr_(set)
    +    return pointer to 'actual size+1' of set (set CANNOT be NULL!!)
    +    Its type is setelemT* for strict aliasing
    +    All SETelemaddr_ must be cast to setelemT
    +
    +
    +  notes:
    +    *SETsizeaddr==NULL or e[*SETsizeaddr-1].p==NULL
    +*/
    +#define SETsizeaddr_(set) (&((set)->e[(set)->maxsize]))
    +
    +/*-----------------------------------------
    +
    +   SETtruncate_(set, size)
    +     truncate set to size
    +
    +   see:
    +     qh_settruncate()
    +
    +*/
    +#define SETtruncate_(set, size) {set->e[set->maxsize].i= size+1; /* maybe overwritten */ \
    +      set->e[size].p= NULL;}
    +
    +/*======= prototypes in alphabetical order ============*/
    +
    +void  qh_setaddsorted(setT **setp, void *elem);
    +void  qh_setaddnth(setT **setp, int nth, void *newelem);
    +void  qh_setappend(setT **setp, void *elem);
    +void  qh_setappend_set(setT **setp, setT *setA);
    +void  qh_setappend2ndlast(setT **setp, void *elem);
    +void  qh_setcheck(setT *set, const char *tname, unsigned id);
    +void  qh_setcompact(setT *set);
    +setT *qh_setcopy(setT *set, int extra);
    +void *qh_setdel(setT *set, void *elem);
    +void *qh_setdellast(setT *set);
    +void *qh_setdelnth(setT *set, int nth);
    +void *qh_setdelnthsorted(setT *set, int nth);
    +void *qh_setdelsorted(setT *set, void *newelem);
    +setT *qh_setduplicate( setT *set, int elemsize);
    +void **qh_setendpointer(setT *set);
    +int   qh_setequal(setT *setA, setT *setB);
    +int   qh_setequal_except(setT *setA, void *skipelemA, setT *setB, void *skipelemB);
    +int   qh_setequal_skip(setT *setA, int skipA, setT *setB, int skipB);
    +void  qh_setfree(setT **set);
    +void  qh_setfree2( setT **setp, int elemsize);
    +void  qh_setfreelong(setT **set);
    +int   qh_setin(setT *set, void *setelem);
    +int   qh_setindex(setT *set, void *setelem);
    +void  qh_setlarger(setT **setp);
    +void *qh_setlast(setT *set);
    +setT *qh_setnew(int size);
    +setT *qh_setnew_delnthsorted(setT *set, int size, int nth, int prepend);
    +void  qh_setprint(FILE *fp, const char* string, setT *set);
    +void  qh_setreplace(setT *set, void *oldelem, void *newelem);
    +int   qh_setsize(setT *set);
    +setT *qh_settemp(int setsize);
    +void  qh_settempfree(setT **set);
    +void  qh_settempfree_all(void);
    +setT *qh_settemppop(void);
    +void  qh_settemppush(setT *set);
    +void  qh_settruncate(setT *set, int size);
    +int   qh_setunique(setT **set, void *elem);
    +void  qh_setzero(setT *set, int idx, int size);
    +
    +
    +#endif /* qhDEFset */
    diff --git a/xs/src/qhull/src/libqhull/random.c b/xs/src/qhull/src/libqhull/random.c
    new file mode 100644
    index 000000000..176d697ae
    --- /dev/null
    +++ b/xs/src/qhull/src/libqhull/random.c
    @@ -0,0 +1,245 @@
    +/*
      ---------------------------------
    +
    +   random.c and utilities
    +     Park & Miller's minimimal standard random number generator
    +     argc/argv conversion
    +
    +     Used by rbox.  Do not use 'qh' 
    +*/
    +
    +#include "libqhull.h"
    +#include "random.h"
    +
    +#include 
    +#include 
    +#include 
    +
    +#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
    +
    +/*---------------------------------
    +
    + 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 */
    +
    +/*---------------------------------
    +
    +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; i0 && strchr(argv[i], ' ')) {
    +        count += 2;  /* quote delimiters */
    +        for (s=argv[i]; *s; s++) {
    +          if (*s == '"') {
    +            count++;
    +          }
    +        }
    +      }
    +    }
    +    return count;
    +} /* argv_to_command_size */
    +
    +/*---------------------------------
    +
    +  qh_rand()
    +  qh_srand( 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 */
    +
    +int qh_last_random= 1;  /* define as global variable instead of using qh */
    +
    +#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( void) {
    +    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( 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 */
    +
    +/*---------------------------------
    +
    +qh_randomfactor( scale, offset )
    +  return a random factor r * scale + offset
    +
    +notes:
    +  qh.RANDOMa/b are defined in global.c
    +*/
    +realT qh_randomfactor(realT scale, realT offset) {
    +    realT randr;
    +
    +    randr= qh_RANDOMint;
    +    return randr * scale + offset;
    +} /* randomfactor */
    +
    +/*---------------------------------
    +
    +qh_randommatrix( 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
    +*/
    +void qh_randommatrix(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 */
    +
    +/*---------------------------------
    +
    +  qh_strtol( s, endp) qh_strtod( s, endp)
    +    internal versions of strtol() and strtod()
    +    does not skip trailing spaces
    +  notes:
    +    some implementations of strtol()/strtod() skip trailing spaces
    +*/
    +double qh_strtod(const char *s, char **endp) {
    +  double result;
    +
    +  result= strtod(s, endp);
    +  if (s < (*endp) && (*endp)[-1] == ' ')
    +    (*endp)--;
    +  return result;
    +} /* strtod */
    +
    +int qh_strtol(const char *s, char **endp) {
    +  int result;
    +
    +  result= (int) strtol(s, endp, 10);     /* WARN64 */
    +  if (s< (*endp) && (*endp)[-1] == ' ')
    +    (*endp)--;
    +  return result;
    +} /* strtol */
    diff --git a/xs/src/qhull/src/libqhull/random.h b/xs/src/qhull/src/libqhull/random.h
    new file mode 100644
    index 000000000..0c6896b76
    --- /dev/null
    +++ b/xs/src/qhull/src/libqhull/random.h
    @@ -0,0 +1,34 @@
    +/*
      ---------------------------------
    +
    +  random.h
    +    header file for random and utility routines
    +
    +   see qh-geom.htm and random.c
    +
    +   Copyright (c) 1993-2015 The Geometry Center.
    +   $Id: //main/2015/qhull/src/libqhull/random.h#2 $$Change: 2026 $
    +   $DateTime: 2015/11/07 22:44:39 $$Author: bbarber $
    +*/
    +
    +#ifndef qhDEFrandom
    +#define qhDEFrandom 1
    +
    +#include "libqhull.h"
    +
    +/*============= prototypes in alphabetical order ======= */
    +
    +
    +int     qh_argv_to_command(int argc, char *argv[], char* command, int max_size);
    +int     qh_argv_to_command_size(int argc, char *argv[]);
    +int     qh_rand( void);
    +void    qh_srand( int seed);
    +realT   qh_randomfactor(realT scale, realT offset);
    +void    qh_randommatrix(realT *buffer, int dim, realT **row);
    +int     qh_strtol(const char *s, char **endp);
    +double  qh_strtod(const char *s, char **endp);
    +
    +#endif /* qhDEFrandom */
    +
    +
    +
    diff --git a/xs/src/qhull/src/libqhull/rboxlib.c b/xs/src/qhull/src/libqhull/rboxlib.c
    new file mode 100644
    index 000000000..f945133fa
    --- /dev/null
    +++ b/xs/src/qhull/src/libqhull/rboxlib.c
    @@ -0,0 +1,870 @@
    +/*
      ---------------------------------
    +
    +   rboxlib.c
    +     Generate input points
    +
    +   notes:
    +     For documentation, see prompt[] of rbox.c
    +     50 points generated for 'rbox D4'
    +
    +   WARNING:
    +     incorrect range if qh_RANDOMmax is defined wrong (user.h)
    +*/
    +
    +#include "libqhull.h"  /* First for user.h */
    +#include "random.h"
    +
    +#include 
    +#include 
    +#include 
    +#include 
    +#include 
    +#include 
    +#include 
    +#include 
    +
    +#ifdef _MSC_VER  /* Microsoft Visual C++ */
    +#pragma warning( disable : 4706)  /* assignment within conditional expression. */
    +#pragma warning( disable : 4996)  /* this function (strncat,sprintf,strcpy) or variable may be unsafe. */
    +#endif
    +
    +#define MAXdim 200
    +#define PI 3.1415926535897932384
    +
    +/* ------------------------------ prototypes ----------------*/
    +int qh_roundi( double a);
    +void qh_out1( double a);
    +void qh_out2n( double a, double b);
    +void qh_out3n( double a, double b, double c);
    +void qh_outcoord(int iscdd, double *coord, int dim);
    +void qh_outcoincident(int coincidentpoints, double radius, int iscdd, double *coord, int dim);
    +
    +void    qh_fprintf_rbox(FILE *fp, int msgcode, const char *fmt, ... );
    +void    qh_free(void *mem);
    +void   *qh_malloc(size_t size);
    +int     qh_rand( void);
    +void    qh_srand( int seed);
    +
    +
    +/* ------------------------------ globals -------------------*/
    +
    +/* No state is carried between rbox requests */
    +typedef struct rboxT rboxT;
    +struct rboxT {
    +  FILE *fout;
    +  FILE *ferr;
    +  int isinteger;
    +  double out_offset;
    +  jmp_buf errexit;        /* exit label for rboxpoints, defined by setjmp(), called by qh_errexit_rbox() */
    +  char  jmpXtra[40];      /* extra bytes in case jmp_buf is defined wrong by compiler */
    +};
    +
    +
    +int rbox_inuse= 0;
    +rboxT rbox;
    +
    +/*---------------------------------
    +
    +  qh_rboxpoints( fout, ferr, rbox_command )
    +    Generate points to fout according to rbox options
    +    Report errors on ferr
    +
    +  returns:
    +    0 (qh_ERRnone) on success
    +    1 (qh_ERRinput) on input error
    +    4 (qh_ERRmem) on memory error
    +    5 (qh_ERRqhull) on internal error
    +
    +  notes:
    +    To avoid using stdio, redefine qh_malloc, qh_free, and qh_fprintf_rbox (user.c)
    +
    +  design:
    +    Straight line code (consider defining a struct and functions):
    +
    +    Parse arguments into variables
    +    Determine the number of points
    +    Generate the points
    +*/
    +int qh_rboxpoints(FILE* fout, FILE* ferr, char* rbox_command) {
    +  int i,j,k;
    +  int gendim;
    +  int coincidentcount=0, coincidenttotal=0, coincidentpoints=0;
    +  int cubesize, diamondsize, seed=0, count, apex;
    +  int dim=3, numpoints=0, totpoints, addpoints=0;
    +  int issphere=0, isaxis=0,  iscdd=0, islens=0, isregular=0, iswidth=0, addcube=0;
    +  int isgap=0, isspiral=0, NOcommand=0, adddiamond=0;
    +  int israndom=0, istime=0;
    +  int isbox=0, issimplex=0, issimplex2=0, ismesh=0;
    +  double width=0.0, gap=0.0, radius=0.0, coincidentradius=0.0;
    +  double coord[MAXdim], offset, meshm=3.0, meshn=4.0, meshr=5.0;
    +  double *coordp, *simplex= NULL, *simplexp;
    +  int nthroot, mult[MAXdim];
    +  double norm, factor, randr, rangap, lensangle=0, lensbase=1;
    +  double anglediff, angle, x, y, cube=0.0, diamond=0.0;
    +  double box= qh_DEFAULTbox; /* scale all numbers before output */
    +  double randmax= qh_RANDOMmax;
    +  char command[200], seedbuf[200];
    +  char *s= command, *t, *first_point= NULL;
    +  time_t timedata;
    +  int exitcode;
    +
    +  if (rbox_inuse) {
    +    qh_fprintf_rbox(rbox.ferr, 6188, "rbox error: rbox in use by another process.  Please lock calls to rbox.\n");
    +    return qh_ERRqhull;
    +  }
    +  rbox_inuse= True;
    +  rbox.ferr= ferr;
    +  rbox.fout= fout;
    +
    +  exitcode= setjmp(rbox.errexit);
    +  if (exitcode) {
    +    /* same code for error exit and normal return.  qh.NOerrexit is set */
    +    if (simplex)
    +        qh_free(simplex);
    +    rbox_inuse= False;
    +    return exitcode;
    +  }
    +
    +  *command= '\0';
    +  strncat(command, rbox_command, sizeof(command)-strlen(command)-1);
    +
    +  while (*s && !isspace(*s))  /* skip program name */
    +    s++;
    +  while (*s) {
    +    while (*s && isspace(*s))
    +      s++;
    +    if (*s == '-')
    +      s++;
    +    if (!*s)
    +      break;
    +    if (isdigit(*s)) {
    +      numpoints= qh_strtol(s, &s);
    +      continue;
    +    }
    +    /* ============= read flags =============== */
    +    switch (*s++) {
    +    case 'c':
    +      addcube= 1;
    +      t= s;
    +      while (isspace(*t))
    +        t++;
    +      if (*t == 'G')
    +        cube= qh_strtod(++t, &s);
    +      break;
    +    case 'd':
    +      adddiamond= 1;
    +      t= s;
    +      while (isspace(*t))
    +        t++;
    +      if (*t == 'G')
    +        diamond= qh_strtod(++t, &s);
    +      break;
    +    case 'h':
    +      iscdd= 1;
    +      break;
    +    case 'l':
    +      isspiral= 1;
    +      break;
    +    case 'n':
    +      NOcommand= 1;
    +      break;
    +    case 'r':
    +      isregular= 1;
    +      break;
    +    case 's':
    +      issphere= 1;
    +      break;
    +    case 't':
    +      istime= 1;
    +      if (isdigit(*s)) {
    +        seed= qh_strtol(s, &s);
    +        israndom= 0;
    +      }else
    +        israndom= 1;
    +      break;
    +    case 'x':
    +      issimplex= 1;
    +      break;
    +    case 'y':
    +      issimplex2= 1;
    +      break;
    +    case 'z':
    +      rbox.isinteger= 1;
    +      break;
    +    case 'B':
    +      box= qh_strtod(s, &s);
    +      isbox= 1;
    +      break;
    +    case 'C':
    +      if (*s)
    +        coincidentpoints=  qh_strtol(s, &s);
    +      if (*s == ',') {
    +        ++s;
    +        coincidentradius=  qh_strtod(s, &s);
    +      }
    +      if (*s == ',') {
    +        ++s;
    +        coincidenttotal=  qh_strtol(s, &s);
    +      }
    +      if (*s && !isspace(*s)) {
    +        qh_fprintf_rbox(rbox.ferr, 7080, "rbox error: arguments for 'Cn,r,m' are not 'int', 'float', and 'int'.  Remaining string is '%s'\n", s);
    +        qh_errexit_rbox(qh_ERRinput);
    +      }
    +      if (coincidentpoints==0){
    +        qh_fprintf_rbox(rbox.ferr, 6268, "rbox error: missing arguments for 'Cn,r,m' where n is the number of coincident points, r is the radius (default 0.0), and m is the number of points\n");
    +        qh_errexit_rbox(qh_ERRinput);
    +      }
    +      if (coincidentpoints<0 || coincidenttotal<0 || coincidentradius<0.0){
    +        qh_fprintf_rbox(rbox.ferr, 6269, "rbox error: negative arguments for 'Cn,m,r' where n (%d) is the number of coincident points, m (%d) is the number of points, and r (%.2g) is the radius (default 0.0)\n", coincidentpoints, coincidenttotal, coincidentradius);
    +        qh_errexit_rbox(qh_ERRinput);
    +      }
    +      break;
    +    case 'D':
    +      dim= qh_strtol(s, &s);
    +      if (dim < 1
    +      || dim > MAXdim) {
    +        qh_fprintf_rbox(rbox.ferr, 6189, "rbox error: dimension, D%d, out of bounds (>=%d or <=0)", dim, MAXdim);
    +        qh_errexit_rbox(qh_ERRinput);
    +      }
    +      break;
    +    case 'G':
    +      if (isdigit(*s))
    +        gap= qh_strtod(s, &s);
    +      else
    +        gap= 0.5;
    +      isgap= 1;
    +      break;
    +    case 'L':
    +      if (isdigit(*s))
    +        radius= qh_strtod(s, &s);
    +      else
    +        radius= 10;
    +      islens= 1;
    +      break;
    +    case 'M':
    +      ismesh= 1;
    +      if (*s)
    +        meshn= qh_strtod(s, &s);
    +      if (*s == ',') {
    +        ++s;
    +        meshm= qh_strtod(s, &s);
    +      }else
    +        meshm= 0.0;
    +      if (*s == ',') {
    +        ++s;
    +        meshr= qh_strtod(s, &s);
    +      }else
    +        meshr= sqrt(meshn*meshn + meshm*meshm);
    +      if (*s && !isspace(*s)) {
    +        qh_fprintf_rbox(rbox.ferr, 7069, "rbox warning: assuming 'M3,4,5' since mesh args are not integers or reals\n");
    +        meshn= 3.0, meshm=4.0, meshr=5.0;
    +      }
    +      break;
    +    case 'O':
    +      rbox.out_offset= qh_strtod(s, &s);
    +      break;
    +    case 'P':
    +      if (!first_point)
    +        first_point= s-1;
    +      addpoints++;
    +      while (*s && !isspace(*s))   /* read points later */
    +        s++;
    +      break;
    +    case 'W':
    +      width= qh_strtod(s, &s);
    +      iswidth= 1;
    +      break;
    +    case 'Z':
    +      if (isdigit(*s))
    +        radius= qh_strtod(s, &s);
    +      else
    +        radius= 1.0;
    +      isaxis= 1;
    +      break;
    +    default:
    +      qh_fprintf_rbox(rbox.ferr, 7070, "rbox error: unknown flag at %s.\nExecute 'rbox' without arguments for documentation.\n", s);
    +      qh_errexit_rbox(qh_ERRinput);
    +    }
    +    if (*s && !isspace(*s)) {
    +      qh_fprintf_rbox(rbox.ferr, 7071, "rbox error: missing space between flags at %s.\n", s);
    +      qh_errexit_rbox(qh_ERRinput);
    +    }
    +  }
    +
    +  /* ============= defaults, constants, and sizes =============== */
    +  if (rbox.isinteger && !isbox)
    +    box= qh_DEFAULTzbox;
    +  if (addcube) {
    +    cubesize= (int)floor(ldexp(1.0,dim)+0.5);
    +    if (cube == 0.0)
    +      cube= box;
    +  }else
    +    cubesize= 0;
    +  if (adddiamond) {
    +    diamondsize= 2*dim;
    +    if (diamond == 0.0)
    +      diamond= box;
    +  }else
    +    diamondsize= 0;
    +  if (islens) {
    +    if (isaxis) {
    +        qh_fprintf_rbox(rbox.ferr, 6190, "rbox error: can not combine 'Ln' with 'Zn'\n");
    +        qh_errexit_rbox(qh_ERRinput);
    +    }
    +    if (radius <= 1.0) {
    +        qh_fprintf_rbox(rbox.ferr, 6191, "rbox error: lens radius %.2g should be greater than 1.0\n",
    +               radius);
    +        qh_errexit_rbox(qh_ERRinput);
    +    }
    +    lensangle= asin(1.0/radius);
    +    lensbase= radius * cos(lensangle);
    +  }
    +
    +  if (!numpoints) {
    +    if (issimplex2)
    +        ; /* ok */
    +    else if (isregular + issimplex + islens + issphere + isaxis + isspiral + iswidth + ismesh) {
    +        qh_fprintf_rbox(rbox.ferr, 6192, "rbox error: missing count\n");
    +        qh_errexit_rbox(qh_ERRinput);
    +    }else if (adddiamond + addcube + addpoints)
    +        ; /* ok */
    +    else {
    +        numpoints= 50;  /* ./rbox D4 is the test case */
    +        issphere= 1;
    +    }
    +  }
    +  if ((issimplex + islens + isspiral + ismesh > 1)
    +  || (issimplex + issphere + isspiral + ismesh > 1)) {
    +    qh_fprintf_rbox(rbox.ferr, 6193, "rbox error: can only specify one of 'l', 's', 'x', 'Ln', or 'Mn,m,r' ('Ln s' is ok).\n");
    +    qh_errexit_rbox(qh_ERRinput);
    +  }
    +  if (coincidentpoints>0 && (numpoints == 0 || coincidenttotal > numpoints)) {
    +    qh_fprintf_rbox(rbox.ferr, 6270, "rbox error: 'Cn,r,m' requested n coincident points for each of m points.  Either there is no points or m (%d) is greater than the number of points (%d).\n", coincidenttotal, numpoints);
    +    qh_errexit_rbox(qh_ERRinput);
    +  }
    +  if (coincidenttotal == 0)
    +    coincidenttotal= numpoints;
    +
    +  /* ============= print header with total points =============== */
    +  if (issimplex || ismesh)
    +    totpoints= numpoints;
    +  else if (issimplex2)
    +    totpoints= numpoints+dim+1;
    +  else if (isregular) {
    +    totpoints= numpoints;
    +    if (dim == 2) {
    +        if (islens)
    +          totpoints += numpoints - 2;
    +    }else if (dim == 3) {
    +        if (islens)
    +          totpoints += 2 * numpoints;
    +      else if (isgap)
    +        totpoints += 1 + numpoints;
    +      else
    +        totpoints += 2;
    +    }
    +  }else
    +    totpoints= numpoints + isaxis;
    +  totpoints += cubesize + diamondsize + addpoints;
    +  totpoints += coincidentpoints*coincidenttotal;
    +
    +  /* ============= seed randoms =============== */
    +  if (istime == 0) {
    +    for (s=command; *s; s++) {
    +      if (issimplex2 && *s == 'y') /* make 'y' same seed as 'x' */
    +        i= 'x';
    +      else
    +        i= *s;
    +      seed= 11*seed + i;
    +    }
    +  }else if (israndom) {
    +    seed= (int)time(&timedata);
    +    sprintf(seedbuf, " t%d", seed);  /* appends an extra t, not worth removing */
    +    strncat(command, seedbuf, sizeof(command)-strlen(command)-1);
    +    t= strstr(command, " t ");
    +    if (t)
    +      strcpy(t+1, t+3); /* remove " t " */
    +  } /* else, seed explicitly set to n */
    +  qh_RANDOMseed_(seed);
    +
    +  /* ============= print header =============== */
    +
    +  if (iscdd)
    +      qh_fprintf_rbox(rbox.fout, 9391, "%s\nbegin\n        %d %d %s\n",
    +      NOcommand ? "" : command,
    +      totpoints, dim+1,
    +      rbox.isinteger ? "integer" : "real");
    +  else if (NOcommand)
    +      qh_fprintf_rbox(rbox.fout, 9392, "%d\n%d\n", dim, totpoints);
    +  else
    +      /* qh_fprintf_rbox special cases 9393 to append 'command' to the RboxPoints.comment() */
    +      qh_fprintf_rbox(rbox.fout, 9393, "%d %s\n%d\n", dim, command, totpoints);
    +
    +  /* ============= explicit points =============== */
    +  if ((s= first_point)) {
    +    while (s && *s) { /* 'P' */
    +      count= 0;
    +      if (iscdd)
    +        qh_out1( 1.0);
    +      while (*++s) {
    +        qh_out1( qh_strtod(s, &s));
    +        count++;
    +        if (isspace(*s) || !*s)
    +          break;
    +        if (*s != ',') {
    +          qh_fprintf_rbox(rbox.ferr, 6194, "rbox error: missing comma after coordinate in %s\n\n", s);
    +          qh_errexit_rbox(qh_ERRinput);
    +        }
    +      }
    +      if (count < dim) {
    +        for (k=dim-count; k--; )
    +          qh_out1( 0.0);
    +      }else if (count > dim) {
    +        qh_fprintf_rbox(rbox.ferr, 6195, "rbox error: %d coordinates instead of %d coordinates in %s\n\n",
    +                  count, dim, s);
    +        qh_errexit_rbox(qh_ERRinput);
    +      }
    +      qh_fprintf_rbox(rbox.fout, 9394, "\n");
    +      while ((s= strchr(s, 'P'))) {
    +        if (isspace(s[-1]))
    +          break;
    +      }
    +    }
    +  }
    +
    +  /* ============= simplex distribution =============== */
    +  if (issimplex+issimplex2) {
    +    if (!(simplex= (double*)qh_malloc( dim * (dim+1) * sizeof(double)))) {
    +      qh_fprintf_rbox(rbox.ferr, 6196, "rbox error: insufficient memory for simplex\n");
    +      qh_errexit_rbox(qh_ERRmem); /* qh_ERRmem */
    +    }
    +    simplexp= simplex;
    +    if (isregular) {
    +      for (i=0; i randmax/2)
    +          coord[dim-1]= -coord[dim-1];
    +      /* ============= project 'Wn' point toward boundary =============== */
    +      }else if (iswidth && !issphere) {
    +        j= qh_RANDOMint % gendim;
    +        if (coord[j] < 0)
    +          coord[j]= -1.0 - coord[j] * width;
    +        else
    +          coord[j]= 1.0 - coord[j] * width;
    +      }
    +      /* ============= scale point to box =============== */
    +      for (k=0; k=0; k--) {
    +        if (j & ( 1 << k))
    +          qh_out1( cube);
    +        else
    +          qh_out1( -cube);
    +      }
    +      qh_fprintf_rbox(rbox.fout, 9400, "\n");
    +    }
    +  }
    +
    +  /* ============= write diamond vertices =============== */
    +  if (adddiamond) {
    +    for (j=0; j=0; k--) {
    +        if (j/2 != k)
    +          qh_out1( 0.0);
    +        else if (j & 0x1)
    +          qh_out1( diamond);
    +        else
    +          qh_out1( -diamond);
    +      }
    +      qh_fprintf_rbox(rbox.fout, 9401, "\n");
    +    }
    +  }
    +
    +  if (iscdd)
    +    qh_fprintf_rbox(rbox.fout, 9402, "end\nhull\n");
    +
    +  /* same code for error exit and normal return */
    +  qh_free(simplex);
    +  rbox_inuse= False;
    +  return qh_ERRnone;
    +} /* rboxpoints */
    +
    +/*------------------------------------------------
    +outxxx - output functions for qh_rboxpoints
    +*/
    +int qh_roundi( double a) {
    +  if (a < 0.0) {
    +    if (a - 0.5 < INT_MIN) {
    +      qh_fprintf_rbox(rbox.ferr, 6200, "rbox input error: negative coordinate %2.2g is too large.  Reduce 'Bn'\n", a);
    +      qh_errexit_rbox(qh_ERRinput);
    +    }
    +    return (int)(a - 0.5);
    +  }else {
    +    if (a + 0.5 > INT_MAX) {
    +      qh_fprintf_rbox(rbox.ferr, 6201, "rbox input error: coordinate %2.2g is too large.  Reduce 'Bn'\n", a);
    +      qh_errexit_rbox(qh_ERRinput);
    +    }
    +    return (int)(a + 0.5);
    +  }
    +} /* qh_roundi */
    +
    +void qh_out1(double a) {
    +
    +  if (rbox.isinteger)
    +    qh_fprintf_rbox(rbox.fout, 9403, "%d ", qh_roundi( a+rbox.out_offset));
    +  else
    +    qh_fprintf_rbox(rbox.fout, 9404, qh_REAL_1, a+rbox.out_offset);
    +} /* qh_out1 */
    +
    +void qh_out2n( double a, double b) {
    +
    +  if (rbox.isinteger)
    +    qh_fprintf_rbox(rbox.fout, 9405, "%d %d\n", qh_roundi(a+rbox.out_offset), qh_roundi(b+rbox.out_offset));
    +  else
    +    qh_fprintf_rbox(rbox.fout, 9406, qh_REAL_2n, a+rbox.out_offset, b+rbox.out_offset);
    +} /* qh_out2n */
    +
    +void qh_out3n( double a, double b, double c) {
    +
    +  if (rbox.isinteger)
    +    qh_fprintf_rbox(rbox.fout, 9407, "%d %d %d\n", qh_roundi(a+rbox.out_offset), qh_roundi(b+rbox.out_offset), qh_roundi(c+rbox.out_offset));
    +  else
    +    qh_fprintf_rbox(rbox.fout, 9408, qh_REAL_3n, a+rbox.out_offset, b+rbox.out_offset, c+rbox.out_offset);
    +} /* qh_out3n */
    +
    +void qh_outcoord(int iscdd, double *coord, int dim) {
    +    double *p= coord;
    +    int k;
    +
    +    if (iscdd)
    +      qh_out1( 1.0);
    +    for (k=0; k < dim; k++)
    +      qh_out1(*(p++));
    +    qh_fprintf_rbox(rbox.fout, 9396, "\n");
    +} /* qh_outcoord */
    +
    +void qh_outcoincident(int coincidentpoints, double radius, int iscdd, double *coord, int dim) {
    +  double *p;
    +  double randr, delta;
    +  int i,k;
    +  double randmax= qh_RANDOMmax;
    +
    +  for (i= 0; i
      ---------------------------------
    +
    +   stat.c
    +   contains all statistics that are collected for qhull
    +
    +   see qh-stat.htm and stat.h
    +
    +   Copyright (c) 1993-2015 The Geometry Center.
    +   $Id: //main/2015/qhull/src/libqhull/stat.c#5 $$Change: 2062 $
    +   $DateTime: 2016/01/17 13:13:18 $$Author: bbarber $
    +*/
    +
    +#include "qhull_a.h"
    +
    +/*============ global data structure ==========*/
    +
    +#if qh_QHpointer
    +qhstatT *qh_qhstat=NULL;  /* global data structure */
    +#else
    +qhstatT qh_qhstat;   /* add "={0}" if this causes a compiler error */
    +#endif
    +
    +/*========== functions in alphabetic order ================*/
    +
    +/*---------------------------------
    +
    +  qh_allstatA()
    +    define statistics in groups of 20
    +
    +  notes:
    +    (otherwise, 'gcc -O2' uses too much memory)
    +    uses qhstat.next
    +*/
    +void qh_allstatA(void) {
    +
    +   /* 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);
    +
    +  qhstat precision= 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(void) {
    +  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(void) {
    +  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(void) {
    +  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(void) {
    +  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(void) {
    +  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(void) {
    +  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(void) {
    +  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(void) {
    +  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 and outside and coplanar sets",-1);
    +  zdef_(zadd, Zmemridges, "for ridges and their vertex sets", -1);
    +} /* allstat */
    +
    +void qh_allstatI(void) {
    +  qhstat vridges= 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 */
    +
    +/*---------------------------------
    +
    +  qh_allstatistics()
    +    reset printed flag for all statistics
    +*/
    +void qh_allstatistics(void) {
    +  int i;
    +
    +  for(i=ZEND; i--; )
    +    qhstat printed[i]= False;
    +} /* allstatistics */
    +
    +#if qh_KEEPstatistics
    +/*---------------------------------
    +
    +  qh_collectstatistics()
    +    collect statistics for qh.facet_list
    +
    +*/
    +void qh_collectstatistics(void) {
    +  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) + sizeof(qhstatT);
    +  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 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(facet->vertices);
    +    sizneighbors= qh_setsize(facet->neighbors);
    +    sizridges= qh_setsize(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(facet->outsideset));
    +    if (facet->coplanarset)
    +      zadd_(Zmempoints, sizeof(setT) + SETelemsize * qh_setsize(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(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(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(vertex->neighbors);
    +      zadd_(Znumvneighbors, sizneighbors);
    +      zmax_(Zmaxvneighbors, sizneighbors);
    +      zadd_(Zmemvertices, sizeof(vertexT) + SETelemsize * sizneighbors);
    +    }
    +  }
    +  qh RANDOMdist= qh old_randomdist;
    +} /* collectstatistics */
    +#endif /* qh_KEEPstatistics */
    +
    +/*---------------------------------
    +
    +  qh_freestatistics(  )
    +    free memory used for statistics
    +*/
    +void qh_freestatistics(void) {
    +
    +#if qh_QHpointer
    +  qh_free(qh_qhstat);
    +  qh_qhstat= NULL;
    +#endif
    +} /* freestatistics */
    +
    +/*---------------------------------
    +
    +  qh_initstatistics(  )
    +    allocate and initialize statistics
    +
    +  notes:
    +    uses qh_malloc() instead of qh_memalloc() since mem.c not set up yet
    +    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(void) {
    +  int i;
    +  realT realx;
    +  int intx;
    +
    +#if qh_QHpointer
    +  if(qh_qhstat){  /* qh_initstatistics may be called from Qhull::resetStatistics() */
    +      qh_free(qh_qhstat);
    +      qh_qhstat= 0;
    +  }
    +  if (!(qh_qhstat= (qhstatT *)qh_malloc(sizeof(qhstatT)))) {
    +    qh_fprintf_stderr(6183, "qhull error (qh_initstatistics): insufficient memory\n");
    +    qh_exit(qh_ERRmem);  /* can not use qh_errexit() */
    +  }
    +#endif
    +
    +  qhstat next= 0;
    +  qh_allstatA();
    +  qh_allstatB();
    +  qh_allstatC();
    +  qh_allstatD();
    +  qh_allstatE();
    +  qh_allstatE2();
    +  qh_allstatF();
    +  qh_allstatG();
    +  qh_allstatH();
    +  qh_allstatI();
    +  if (qhstat next > (int)sizeof(qhstat id)) {
    +    qh_fprintf(qhmem.ferr, 6184, "qhull error (qh_initstatistics): increase size of qhstat.id[].\n\
    +      qhstat.next %d should be <= sizeof(qhstat id) %d\n", qhstat next, (int)sizeof(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 (qhstat id[i] == qhstat id[j]) {
    +          qh_fprintf(qhmem.ferr, 6185, "qhull error (qh_initstatistics): duplicated statistic %d at indices %d and %d\n",
    +              qhstat id[i], i, j);
    +        }
    +      }
    +    }
    +#endif
    +    qh_exit(qh_ERRqhull);  /* can not use qh_errexit() */
    +  }
    +  qhstat init[zinc].i= 0;
    +  qhstat init[zadd].i= 0;
    +  qhstat init[zmin].i= INT_MAX;
    +  qhstat init[zmax].i= INT_MIN;
    +  qhstat init[wadd].r= 0;
    +  qhstat init[wmin].r= REALmax;
    +  qhstat init[wmax].r= -REALmax;
    +  for(i=0; i < ZEND; i++) {
    +    if (qhstat type[i] > ZTYPEreal) {
    +      realx= qhstat init[(unsigned char)(qhstat type[i])].r;
    +      qhstat stats[i].r= realx;
    +    }else if (qhstat type[i] != zdoc) {
    +      intx= qhstat init[(unsigned char)(qhstat type[i])].i;
    +      qhstat stats[i].i= intx;
    +    }
    +  }
    +} /* initstatistics */
    +
    +/*---------------------------------
    +
    +  qh_newstats(  )
    +    returns True if statistics for zdoc
    +
    +  returns:
    +    next zdoc
    +*/
    +boolT qh_newstats(int idx, int *nextindex) {
    +  boolT isnew= False;
    +  int start, i;
    +
    +  if (qhstat type[qhstat id[idx]] == zdoc)
    +    start= idx+1;
    +  else
    +    start= idx;
    +  for(i= start; i < qhstat next && qhstat type[qhstat id[i]] != zdoc; i++) {
    +    if (!qh_nostatistic(qhstat id[i]) && !qhstat printed[qhstat id[i]])
    +        isnew= True;
    +  }
    +  *nextindex= i;
    +  return isnew;
    +} /* newstats */
    +
    +/*---------------------------------
    +
    +  qh_nostatistic( index )
    +    true if no statistic to print
    +*/
    +boolT qh_nostatistic(int i) {
    +
    +  if ((qhstat type[i] > ZTYPEreal
    +       &&qhstat stats[i].r == qhstat init[(unsigned char)(qhstat type[i])].r)
    +      || (qhstat type[i] < ZTYPEreal
    +          &&qhstat stats[i].i == qhstat init[(unsigned char)(qhstat type[i])].i))
    +    return True;
    +  return False;
    +} /* nostatistic */
    +
    +#if qh_KEEPstatistics
    +/*---------------------------------
    +
    +  qh_printallstatistics( fp, string )
    +    print all statistics with header 'string'
    +*/
    +void qh_printallstatistics(FILE *fp, const char *string) {
    +
    +  qh_allstatistics();
    +  qh_collectstatistics();
    +  qh_printstatistics(fp, string);
    +  qh_memstatistics(fp);
    +}
    +
    +
    +/*---------------------------------
    +
    +  qh_printstatistics( 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(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(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(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(fp, 9352, "\
    + %6.2g max. distance for near-inside points\n", qh NEARinside);
    +  if (qh premerge_cos < REALmax/2) qh_fprintf(fp, 9353, "\
    + %6.2g max. cosine for pre-merge angle\n", qh premerge_cos);
    +  if (qh PREmerge) qh_fprintf(fp, 9354, "\
    + %6.2g radius of pre-merge centrum\n", qh premerge_centrum);
    +  if (qh postmerge_cos < REALmax/2) qh_fprintf(fp, 9355, "\
    + %6.2g max. cosine for post-merge angle\n", qh postmerge_cos);
    +  if (qh POSTmerge) qh_fprintf(fp, 9356, "\
    + %6.2g radius of post-merge centrum\n", qh postmerge_centrum);
    +  qh_fprintf(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(fp, 9358, "%6.2e ", qh NEARzero[k]);
    +  qh_fprintf(fp, 9359, "\n\n");
    +  for(i=0 ; i < qhstat next; )
    +    qh_printstats(fp, i, &i);
    +} /* printstatistics */
    +#endif /* qh_KEEPstatistics */
    +
    +/*---------------------------------
    +
    +  qh_printstatlevel( fp, id )
    +    print level information for a statistic
    +
    +  notes:
    +    nop if id >= ZEND, printed, or same as initial value
    +*/
    +void qh_printstatlevel(FILE *fp, int id) {
    +#define NULLfield "       "
    +
    +  if (id >= ZEND || qhstat printed[id])
    +    return;
    +  if (qhstat type[id] == zdoc) {
    +    qh_fprintf(fp, 9360, "%s\n", qhstat doc[id]);
    +    return;
    +  }
    +  if (qh_nostatistic(id) || !qhstat doc[id])
    +    return;
    +  qhstat printed[id]= True;
    +  if (qhstat count[id] != -1
    +      && qhstat stats[(unsigned char)(qhstat count[id])].i == 0)
    +    qh_fprintf(fp, 9361, " *0 cnt*");
    +  else if (qhstat type[id] >= ZTYPEreal && qhstat count[id] == -1)
    +    qh_fprintf(fp, 9362, "%7.2g", qhstat stats[id].r);
    +  else if (qhstat type[id] >= ZTYPEreal && qhstat count[id] != -1)
    +    qh_fprintf(fp, 9363, "%7.2g", qhstat stats[id].r/ qhstat stats[(unsigned char)(qhstat count[id])].i);
    +  else if (qhstat type[id] < ZTYPEreal && qhstat count[id] == -1)
    +    qh_fprintf(fp, 9364, "%7d", qhstat stats[id].i);
    +  else if (qhstat type[id] < ZTYPEreal && qhstat count[id] != -1)
    +    qh_fprintf(fp, 9365, "%7.3g", (realT) qhstat stats[id].i / qhstat stats[(unsigned char)(qhstat count[id])].i);
    +  qh_fprintf(fp, 9366, " %s\n", qhstat doc[id]);
    +} /* printstatlevel */
    +
    +
    +/*---------------------------------
    +
    +  qh_printstats( fp, index, nextindex )
    +    print statistics for a zdoc group
    +
    +  returns:
    +    next zdoc if non-null
    +*/
    +void qh_printstats(FILE *fp, int idx, int *nextindex) {
    +  int j, nexti;
    +
    +  if (qh_newstats(idx, &nexti)) {
    +    qh_fprintf(fp, 9367, "\n");
    +    for (j=idx; j--------------------------------
    +
    +  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(void) {}
    +void    qh_printallstatistics(FILE *fp, char *string) {};
    +void    qh_printstatistics(FILE *fp, char *string) {}
    +#endif
    +
    diff --git a/xs/src/qhull/src/libqhull/stat.h b/xs/src/qhull/src/libqhull/stat.h
    new file mode 100644
    index 000000000..d86fc0a87
    --- /dev/null
    +++ b/xs/src/qhull/src/libqhull/stat.h
    @@ -0,0 +1,543 @@
    +/*
      ---------------------------------
    +
    +   stat.h
    +     contains all statistics that are collected for qhull
    +
    +   see qh-stat.htm and stat.c
    +
    +   Copyright (c) 1993-2015 The Geometry Center.
    +   $Id: //main/2015/qhull/src/libqhull/stat.h#4 $$Change: 2062 $
    +   $DateTime: 2016/01/17 13:13:18 $$Author: bbarber $
    +
    +   recompile qhull if you change this file
    +
    +   Integer statistics are Z* while real statistics are W*.
    +
    +   define maydebugx to call a routine at every statistic event
    +
    +*/
    +
    +#ifndef qhDEFstat
    +#define qhDEFstat 1
    +
    +#include "libqhull.h"
    +
    +/*---------------------------------
    +
    +  qh_KEEPstatistics
    +    0 turns off statistic gathering (except zzdef/zzinc/zzadd/zzval/wwval)
    +*/
    +#ifndef qh_KEEPstatistics
    +#define qh_KEEPstatistics 1
    +#endif
    +
    +/*---------------------------------
    +
    +  Zxxx for integers, Wxxx for reals
    +
    +  notes:
    +    be sure that all statistics are defined in stat.c
    +      otherwise initialization may core dump
    +    can pick up all statistics by:
    +      grep '[zw].*_[(][ZW]' *.c >z.x
    +    remove trailers with query">-
    +    remove leaders with  query-replace-regexp [ ^I]+  (
    +*/
    +#if qh_KEEPstatistics
    +enum qh_statistics {     /* alphabetical after Z/W */
    +    Zacoplanar,
    +    Wacoplanarmax,
    +    Wacoplanartot,
    +    Zangle,
    +    Wangle,
    +    Wanglemax,
    +    Wanglemin,
    +    Zangletests,
    +    Wareatot,
    +    Wareamax,
    +    Wareamin,
    +    Zavoidold,
    +    Wavoidoldmax,
    +    Wavoidoldtot,
    +    Zback0,
    +    Zbestcentrum,
    +    Zbestdist,
    +    Zbestlower,
    +    Zbestlowerall,
    +    Zbestloweralln,
    +    Zbestlowerv,
    +    Zcentrumtests,
    +    Zcheckpart,
    +    Zcomputefurthest,
    +    Zconcave,
    +    Wconcavemax,
    +    Wconcavetot,
    +    Zconcaveridges,
    +    Zconcaveridge,
    +    Zcoplanar,
    +    Wcoplanarmax,
    +    Wcoplanartot,
    +    Zcoplanarangle,
    +    Zcoplanarcentrum,
    +    Zcoplanarhorizon,
    +    Zcoplanarinside,
    +    Zcoplanarpart,
    +    Zcoplanarridges,
    +    Wcpu,
    +    Zcyclefacetmax,
    +    Zcyclefacettot,
    +    Zcyclehorizon,
    +    Zcyclevertex,
    +    Zdegen,
    +    Wdegenmax,
    +    Wdegentot,
    +    Zdegenvertex,
    +    Zdelfacetdup,
    +    Zdelridge,
    +    Zdelvertextot,
    +    Zdelvertexmax,
    +    Zdetsimplex,
    +    Zdistcheck,
    +    Zdistconvex,
    +    Zdistgood,
    +    Zdistio,
    +    Zdistplane,
    +    Zdiststat,
    +    Zdistvertex,
    +    Zdistzero,
    +    Zdoc1,
    +    Zdoc2,
    +    Zdoc3,
    +    Zdoc4,
    +    Zdoc5,
    +    Zdoc6,
    +    Zdoc7,
    +    Zdoc8,
    +    Zdoc9,
    +    Zdoc10,
    +    Zdoc11,
    +    Zdoc12,
    +    Zdropdegen,
    +    Zdropneighbor,
    +    Zdupflip,
    +    Zduplicate,
    +    Wduplicatemax,
    +    Wduplicatetot,
    +    Zdupridge,
    +    Zdupsame,
    +    Zflipped,
    +    Wflippedmax,
    +    Wflippedtot,
    +    Zflippedfacets,
    +    Zfindbest,
    +    Zfindbestmax,
    +    Zfindbesttot,
    +    Zfindcoplanar,
    +    Zfindfail,
    +    Zfindhorizon,
    +    Zfindhorizonmax,
    +    Zfindhorizontot,
    +    Zfindjump,
    +    Zfindnew,
    +    Zfindnewmax,
    +    Zfindnewtot,
    +    Zfindnewjump,
    +    Zfindnewsharp,
    +    Zgauss0,
    +    Zgoodfacet,
    +    Zhashlookup,
    +    Zhashridge,
    +    Zhashridgetest,
    +    Zhashtests,
    +    Zinsidevisible,
    +    Zintersect,
    +    Zintersectfail,
    +    Zintersectmax,
    +    Zintersectnum,
    +    Zintersecttot,
    +    Zmaxneighbors,
    +    Wmaxout,
    +    Wmaxoutside,
    +    Zmaxridges,
    +    Zmaxvertex,
    +    Zmaxvertices,
    +    Zmaxvneighbors,
    +    Zmemfacets,
    +    Zmempoints,
    +    Zmemridges,
    +    Zmemvertices,
    +    Zmergeflipdup,
    +    Zmergehorizon,
    +    Zmergeinittot,
    +    Zmergeinitmax,
    +    Zmergeinittot2,
    +    Zmergeintohorizon,
    +    Zmergenew,
    +    Zmergesettot,
    +    Zmergesetmax,
    +    Zmergesettot2,
    +    Zmergesimplex,
    +    Zmergevertex,
    +    Wmindenom,
    +    Wminvertex,
    +    Zminnorm,
    +    Zmultiridge,
    +    Znearlysingular,
    +    Zneighbor,
    +    Wnewbalance,
    +    Wnewbalance2,
    +    Znewfacettot,
    +    Znewfacetmax,
    +    Znewvertex,
    +    Wnewvertex,
    +    Wnewvertexmax,
    +    Znoarea,
    +    Znonsimplicial,
    +    Znowsimplicial,
    +    Znotgood,
    +    Znotgoodnew,
    +    Znotmax,
    +    Znumfacets,
    +    Znummergemax,
    +    Znummergetot,
    +    Znumneighbors,
    +    Znumridges,
    +    Znumvertices,
    +    Znumvisibility,
    +    Znumvneighbors,
    +    Zonehorizon,
    +    Zpartangle,
    +    Zpartcoplanar,
    +    Zpartflip,
    +    Zparthorizon,
    +    Zpartinside,
    +    Zpartition,
    +    Zpartitionall,
    +    Zpartnear,
    +    Zpbalance,
    +    Wpbalance,
    +    Wpbalance2,
    +    Zpostfacets,
    +    Zpremergetot,
    +    Zprocessed,
    +    Zremvertex,
    +    Zremvertexdel,
    +    Zrenameall,
    +    Zrenamepinch,
    +    Zrenameshare,
    +    Zretry,
    +    Wretrymax,
    +    Zridge,
    +    Wridge,
    +    Wridgemax,
    +    Zridge0,
    +    Wridge0,
    +    Wridge0max,
    +    Zridgemid,
    +    Wridgemid,
    +    Wridgemidmax,
    +    Zridgeok,
    +    Wridgeok,
    +    Wridgeokmax,
    +    Zsearchpoints,
    +    Zsetplane,
    +    Ztestvneighbor,
    +    Ztotcheck,
    +    Ztothorizon,
    +    Ztotmerge,
    +    Ztotpartcoplanar,
    +    Ztotpartition,
    +    Ztotridges,
    +    Ztotvertices,
    +    Ztotvisible,
    +    Ztricoplanar,
    +    Ztricoplanarmax,
    +    Ztricoplanartot,
    +    Ztridegen,
    +    Ztrimirror,
    +    Ztrinull,
    +    Wvertexmax,
    +    Wvertexmin,
    +    Zvertexridge,
    +    Zvertexridgetot,
    +    Zvertexridgemax,
    +    Zvertices,
    +    Zvisfacettot,
    +    Zvisfacetmax,
    +    Zvisit,
    +    Zvisit2max,
    +    Zvisvertextot,
    +    Zvisvertexmax,
    +    Zvvisit,
    +    Zvvisit2max,
    +    Zwidefacet,
    +    Zwidevertices,
    +    ZEND};
    +
    +/*---------------------------------
    +
    +  Zxxx/Wxxx statistics that remain defined if qh_KEEPstatistics=0
    +
    +  notes:
    +    be sure to use zzdef, zzinc, etc. with these statistics (no double checking!)
    +*/
    +#else
    +enum qh_statistics {     /* for zzdef etc. macros */
    +  Zback0,
    +  Zbestdist,
    +  Zcentrumtests,
    +  Zcheckpart,
    +  Zconcaveridges,
    +  Zcoplanarhorizon,
    +  Zcoplanarpart,
    +  Zcoplanarridges,
    +  Zcyclefacettot,
    +  Zcyclehorizon,
    +  Zdelvertextot,
    +  Zdistcheck,
    +  Zdistconvex,
    +  Zdistzero,
    +  Zdoc1,
    +  Zdoc2,
    +  Zdoc3,
    +  Zdoc11,
    +  Zflippedfacets,
    +  Zgauss0,
    +  Zminnorm,
    +  Zmultiridge,
    +  Znearlysingular,
    +  Wnewvertexmax,
    +  Znumvisibility,
    +  Zpartcoplanar,
    +  Zpartition,
    +  Zpartitionall,
    +  Zprocessed,
    +  Zretry,
    +  Zridge,
    +  Wridge,
    +  Wridgemax,
    +  Zridge0,
    +  Wridge0,
    +  Wridge0max,
    +  Zridgemid,
    +  Wridgemid,
    +  Wridgemidmax,
    +  Zridgeok,
    +  Wridgeok,
    +  Wridgeokmax,
    +  Zsetplane,
    +  Ztotcheck,
    +  Ztotmerge,
    +    ZEND};
    +#endif
    +
    +/*---------------------------------
    +
    +  ztype
    +    the type of a statistic sets its initial value.
    +
    +  notes:
    +    The type should be the same as the macro for collecting the statistic
    +*/
    +enum ztypes {zdoc,zinc,zadd,zmax,zmin,ZTYPEreal,wadd,wmax,wmin,ZTYPEend};
    +
    +/*========== macros and constants =============*/
    +
    +/*----------------------------------
    +
    +  MAYdebugx
    +    define as maydebug() to be called frequently for error trapping
    +*/
    +#define MAYdebugx
    +
    +/*----------------------------------
    +
    +  zzdef_, zdef_( type, name, doc, -1)
    +    define a statistic (assumes 'qhstat.next= 0;')
    +
    +  zdef_( type, name, doc, count)
    +    define an averaged statistic
    +    printed as name/count
    +*/
    +#define zzdef_(stype,name,string,cnt) qhstat id[qhstat next++]=name; \
    +   qhstat doc[name]= string; qhstat count[name]= cnt; qhstat type[name]= stype
    +#if qh_KEEPstatistics
    +#define zdef_(stype,name,string,cnt) qhstat id[qhstat next++]=name; \
    +   qhstat doc[name]= string; qhstat count[name]= cnt; qhstat type[name]= stype
    +#else
    +#define zdef_(type,name,doc,count)
    +#endif
    +
    +/*----------------------------------
    +
    +  zzinc_( name ), zinc_( name)
    +    increment an integer statistic
    +*/
    +#define zzinc_(id) {MAYdebugx; qhstat stats[id].i++;}
    +#if qh_KEEPstatistics
    +#define zinc_(id) {MAYdebugx; qhstat stats[id].i++;}
    +#else
    +#define zinc_(id) {}
    +#endif
    +
    +/*----------------------------------
    +
    +  zzadd_( name, value ), zadd_( name, value ), wadd_( name, value )
    +    add value to an integer or real statistic
    +*/
    +#define zzadd_(id, val) {MAYdebugx; qhstat stats[id].i += (val);}
    +#define wwadd_(id, val) {MAYdebugx; qhstat stats[id].r += (val);}
    +#if qh_KEEPstatistics
    +#define zadd_(id, val) {MAYdebugx; qhstat stats[id].i += (val);}
    +#define wadd_(id, val) {MAYdebugx; qhstat stats[id].r += (val);}
    +#else
    +#define zadd_(id, val) {}
    +#define wadd_(id, val) {}
    +#endif
    +
    +/*----------------------------------
    +
    +  zzval_( name ), zval_( name ), wwval_( name )
    +    set or return value of a statistic
    +*/
    +#define zzval_(id) ((qhstat stats[id]).i)
    +#define wwval_(id) ((qhstat stats[id]).r)
    +#if qh_KEEPstatistics
    +#define zval_(id) ((qhstat stats[id]).i)
    +#define wval_(id) ((qhstat stats[id]).r)
    +#else
    +#define zval_(id) qhstat tempi
    +#define wval_(id) qhstat tempr
    +#endif
    +
    +/*----------------------------------
    +
    +  zmax_( id, val ), wmax_( id, value )
    +    maximize id with val
    +*/
    +#define wwmax_(id, val) {MAYdebugx; maximize_(qhstat stats[id].r,(val));}
    +#if qh_KEEPstatistics
    +#define zmax_(id, val) {MAYdebugx; maximize_(qhstat stats[id].i,(val));}
    +#define wmax_(id, val) {MAYdebugx; maximize_(qhstat stats[id].r,(val));}
    +#else
    +#define zmax_(id, val) {}
    +#define wmax_(id, val) {}
    +#endif
    +
    +/*----------------------------------
    +
    +  zmin_( id, val ), wmin_( id, value )
    +    minimize id with val
    +*/
    +#if qh_KEEPstatistics
    +#define zmin_(id, val) {MAYdebugx; minimize_(qhstat stats[id].i,(val));}
    +#define wmin_(id, val) {MAYdebugx; minimize_(qhstat stats[id].r,(val));}
    +#else
    +#define zmin_(id, val) {}
    +#define wmin_(id, val) {}
    +#endif
    +
    +/*================== stat.h types ==============*/
    +
    +
    +/*----------------------------------
    +
    +  intrealT
    +    union of integer and real, used for statistics
    +*/
    +typedef union intrealT intrealT;    /* union of int and realT */
    +union intrealT {
    +    int i;
    +    realT r;
    +};
    +
    +/*----------------------------------
    +
    +  qhstat
    +    global data structure for statistics, similar to qh and qhrbox
    +
    +  notes:
    +   access to qh_qhstat is via the "qhstat" macro.  There are two choices
    +   qh_QHpointer = 1     access globals via a pointer
    +                        enables qh_saveqhull() and qh_restoreqhull()
    +                = 0     qh_qhstat is a static data structure
    +                        only one instance of qhull() can be active at a time
    +                        default value
    +   qh_QHpointer is defined in libqhull.h
    +   qh_QHpointer_dllimport and qh_dllimport define qh_qh as __declspec(dllimport) [libqhull.h]
    +
    +   allocated in stat.c using qh_malloc()
    +*/
    +#ifndef DEFqhstatT
    +#define DEFqhstatT 1
    +typedef struct qhstatT qhstatT;
    +#endif
    +
    +#if qh_QHpointer_dllimport
    +#define qhstat qh_qhstat->
    +__declspec(dllimport) extern qhstatT *qh_qhstat;
    +#elif qh_QHpointer
    +#define qhstat qh_qhstat->
    +extern qhstatT *qh_qhstat;
    +#elif qh_dllimport
    +#define qhstat qh_qhstat.
    +__declspec(dllimport) extern qhstatT qh_qhstat;
    +#else
    +#define qhstat qh_qhstat.
    +extern qhstatT qh_qhstat;
    +#endif
    +struct qhstatT {
    +  intrealT   stats[ZEND];     /* integer and real statistics */
    +  unsigned   char id[ZEND+10]; /* id's in print order */
    +  const char *doc[ZEND];       /* array of documentation strings */
    +  short int  count[ZEND];     /* -1 if none, else index of count to use */
    +  char       type[ZEND];      /* type, see ztypes above */
    +  char       printed[ZEND];   /* true, if statistic has been printed */
    +  intrealT   init[ZTYPEend];  /* initial values by types, set initstatistics */
    +
    +  int        next;            /* next index for zdef_ */
    +  int        precision;       /* index for precision problems */
    +  int        vridges;         /* index for Voronoi ridges */
    +  int        tempi;
    +  realT      tempr;
    +};
    +
    +/*========== function prototypes ===========*/
    +
    +void    qh_allstatA(void);
    +void    qh_allstatB(void);
    +void    qh_allstatC(void);
    +void    qh_allstatD(void);
    +void    qh_allstatE(void);
    +void    qh_allstatE2(void);
    +void    qh_allstatF(void);
    +void    qh_allstatG(void);
    +void    qh_allstatH(void);
    +void    qh_allstatI(void);
    +void    qh_allstatistics(void);
    +void    qh_collectstatistics(void);
    +void    qh_freestatistics(void);
    +void    qh_initstatistics(void);
    +boolT   qh_newstats(int idx, int *nextindex);
    +boolT   qh_nostatistic(int i);
    +void    qh_printallstatistics(FILE *fp, const char *string);
    +void    qh_printstatistics(FILE *fp, const char *string);
    +void    qh_printstatlevel(FILE *fp, int id);
    +void    qh_printstats(FILE *fp, int idx, int *nextindex);
    +realT   qh_stddev(int num, realT tot, realT tot2, realT *ave);
    +
    +#endif   /* qhDEFstat */
    diff --git a/xs/src/qhull/src/libqhull/user.c b/xs/src/qhull/src/libqhull/user.c
    new file mode 100644
    index 000000000..d4726eaa3
    --- /dev/null
    +++ b/xs/src/qhull/src/libqhull/user.c
    @@ -0,0 +1,538 @@
    +/*
      ---------------------------------
    +
    +   user.c
    +   user redefinable functions
    +
    +   see user2.c for qh_fprintf, qh_malloc, qh_free
    +
    +   see README.txt  see COPYING.txt for copyright information.
    +
    +   see libqhull.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.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.c for an
    +   example.  We recommend keeping io.o for the extra debugging
    +   information it supplies.
    +*/
    +
    +#include "qhull_a.h"
    +
    +#include 
    +
    +/*---------------------------------
    +
    +  Qhull-template
    +    Template for calling qhull from inside your program
    +
    +  returns:
    +    exit code(see qh_ERR... in libqhull.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()
    +                               use NULL to skip qh_produce_output() */
    +  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 */
    +
    +  QHULL_LIB_CHECK /* Check for compatible library */
    +
    +#if qh_QHpointer  /* see user.h */
    +  if (qh_qh){ /* should be NULL */
    +      qh_printf_stderr(6238, "Qhull link error.  The global variable qh_qh was not initialized\n\
    +              to NULL by global.c.  Please compile this program with -Dqh_QHpointer_dllimport\n\
    +              as well as -Dqh_QHpointer, or use libqhullstatic, or use a different tool chain.\n\n");
    +      exit(1);
    +  }
    +#endif
    +
    +  /* initialize dim, numpoints, points[], ismalloc here */
    +  exitcode= qh_new_qhull(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_ALL);
    +  qh_memfreeshort(&curlong, &totlong);
    +  if (curlong || totlong)
    +    qh_fprintf(errfile, 7068, "qhull internal warning (main): did not free %d bytes of long memory(%d pieces)\n", totlong, curlong);
    +}
    +#endif
    +
    +/*---------------------------------
    +
    +  qh_new_qhull( dim, numpoints, points, ismalloc, qhull_cmd, outfile, errfile )
    +    build new qhull data structure and return exitcode (0 if no errors)
    +    if numpoints=0 and points=NULL, initializes qhull
    +
    +  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().
    +
    +    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')
    +
    +
    +  To allow multiple, concurrent calls to qhull()
    +    - set qh_QHpointer in user.h
    +    - use qh_save_qhull and qh_restore_qhull to swap the global data structure between calls.
    +    - use qh_freeqhull(qh_ALL) to free intermediate convex hulls
    +
    +  see:
    +      Qhull-template at the beginning of this file.
    +      An example of using qh_new_qhull is user_eg.c
    +*/
    +int qh_new_qhull(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;
    +  static boolT firstcall = True;
    +  coordT *new_points;
    +  if(!errfile){
    +      errfile= stderr;
    +  }
    +  if (firstcall) {
    +    qh_meminit(errfile);
    +    firstcall= False;
    +  } else {
    +    qh_memcheck();
    +  }
    +  if (strncmp(qhull_cmd, "qhull ", (size_t)6)) {
    +    qh_fprintf(errfile, 6186, "qhull error (qh_new_qhull): start qhull_cmd argument with \"qhull \"\n");
    +    return qh_ERRinput;
    +  }
    +  qh_initqhull_start(NULL, outfile, errfile);
    +  if(numpoints==0 && points==NULL){
    +      trace1((qh ferr, 1047, "qh_new_qhull: initialize Qhull\n"));
    +      return 0;
    +  }
    +  trace1((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(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(hulldim);
    +      new_points= qh_sethalfspace_all(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(new_points, numpoints, hulldim, new_ismalloc);
    +    qh_qhull();
    +    qh_check_output();
    +    if (outfile) {
    +      qh_produce_output();
    +    }else {
    +      qh_prepare_output();
    +    }
    +    if (qh VERIFYoutput && !qh STOPpoint && !qh STOPcone)
    +      qh_check_points();
    +  }
    +  qh NOerrexit = True;
    +  return exitcode;
    +} /* new_qhull */
    +
    +/*---------------------------------
    +
    +  qh_errexit( 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.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(int exitcode, facetT *facet, ridgeT *ridge) {
    +
    +  if (qh ERREXITcalled) {
    +    qh_fprintf(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("ERRONEOUS", facet, NULL, ridge, NULL);
    +  qh_fprintf(qh ferr, 8127, "\nWhile executing: %s | %s\n", qh rbox_command, qh qhull_command);
    +  qh_fprintf(qh ferr, 8128, "Options selected for Qhull %s:\n%s\n", qh_version, qh qhull_options);
    +  if (qh furthest_id >= 0) {
    +    qh_fprintf(qh ferr, 8129, "Last point added to hull was p%d.", qh furthest_id);
    +    if (zzval_(Ztotmerge))
    +      qh_fprintf(qh ferr, 8130, "  Last merge was #%d.", zzval_(Ztotmerge));
    +    if (qh QHULLfinished)
    +      qh_fprintf(qh ferr, 8131, "\nQhull has finished constructing the hull.");
    +    else if (qh POSTmerging)
    +      qh_fprintf(qh ferr, 8132, "\nQhull has started post-merging.");
    +    qh_fprintf(qh ferr, 8133, "\n");
    +  }
    +  if (qh FORCEoutput && (qh QHULLfinished || (!facet && !ridge)))
    +    qh_produce_output();
    +  else if (exitcode != qh_ERRinput) {
    +    if (exitcode != qh_ERRsingular && zzval_(Zsetplane) > qh hull_dim+1) {
    +      qh_fprintf(qh ferr, 8134, "\nAt error exit:\n");
    +      qh_printsummary(qh ferr);
    +      if (qh PRINTstatistics) {
    +        qh_collectstatistics();
    +        qh_printstatistics(qh ferr, "at error exit");
    +        qh_memstatistics(qh ferr);
    +      }
    +    }
    +    if (qh PRINTprecision)
    +      qh_printstats(qh ferr, qhstat precision, NULL);
    +  }
    +  if (!exitcode)
    +    exitcode= qh_ERRqhull;
    +  else if (exitcode == qh_ERRsingular)
    +    qh_printhelp_singular(qh ferr);
    +  else if (exitcode == qh_ERRprec && !qh PREmerge)
    +    qh_printhelp_degenerate(qh ferr);
    +  if (qh NOerrexit) {
    +    qh_fprintf(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 */
    +
    +
    +/*---------------------------------
    +
    +  qh_errprint( 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(const char *string, facetT *atfacet, facetT *otherfacet, ridgeT *atridge, vertexT *atvertex) {
    +  int i;
    +
    +  if (atfacet) {
    +    qh_fprintf(qh ferr, 8135, "%s FACET:\n", string);
    +    qh_printfacet(qh ferr, atfacet);
    +  }
    +  if (otherfacet) {
    +    qh_fprintf(qh ferr, 8136, "%s OTHER FACET:\n", string);
    +    qh_printfacet(qh ferr, otherfacet);
    +  }
    +  if (atridge) {
    +    qh_fprintf(qh ferr, 8137, "%s RIDGE:\n", string);
    +    qh_printridge(qh ferr, atridge);
    +    if (atridge->top && atridge->top != atfacet && atridge->top != otherfacet)
    +      qh_printfacet(qh ferr, atridge->top);
    +    if (atridge->bottom
    +        && atridge->bottom != atfacet && atridge->bottom != otherfacet)
    +      qh_printfacet(qh ferr, atridge->bottom);
    +    if (!atfacet)
    +      atfacet= atridge->top;
    +    if (!otherfacet)
    +      otherfacet= otherfacet_(atridge, atfacet);
    +  }
    +  if (atvertex) {
    +    qh_fprintf(qh ferr, 8138, "%s VERTEX:\n", string);
    +    qh_printvertex(qh ferr, atvertex);
    +  }
    +  if (qh fout && qh FORCEoutput && atfacet && !qh QHULLfinished && !qh IStracing) {
    +    qh_fprintf(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 fout, qh PRINTout[i], atfacet, otherfacet,
    +                            !qh_ALL);
    +  }
    +} /* errprint */
    +
    +
    +/*---------------------------------
    +
    +  qh_printfacetlist( 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(facetT *facetlist, setT *facets, boolT printall) {
    +  facetT *facet, **facetp;
    +
    +  qh_printbegin(qh ferr, qh_PRINTfacets, facetlist, facets, printall);
    +  FORALLfacet_(facetlist)
    +    qh_printafacet(qh ferr, qh_PRINTfacets, facet, printall);
    +  FOREACHfacet_(facets)
    +    qh_printafacet(qh ferr, qh_PRINTfacets, facet, printall);
    +  qh_printend(qh ferr, qh_PRINTfacets, facetlist, facets, printall);
    +} /* printfacetlist */
    +
    +
    +/*---------------------------------
    +
    +  qh_printhelp_degenerate( fp )
    +    prints descriptive message for precision error
    +
    +  notes:
    +    no message if qh_QUICKhelp
    +*/
    +void qh_printhelp_degenerate(FILE *fp) {
    +
    +  if (qh MERGEexact || qh PREmerge || qh JOGGLEmax < REALmax/2)
    +    qh_fprintf(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(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(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(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(fp, 9372, "\
    +When computing the Delaunay triangulation:\n\
    +  - use 'Qz' to add a point at-infinity.  This reduces precision problems.\n");
    +
    +    qh_fprintf(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(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 */
    +
    +
    +/*---------------------------------
    +
    +  qh_printhelp_narrowhull( minangle )
    +    Warn about a narrow hull
    +
    +  notes:
    +    Alternatively, reduce qh_WARNnarrow in user.h
    +
    +*/
    +void qh_printhelp_narrowhull(FILE *fp, realT minangle) {
    +
    +    qh_fprintf(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 */
    +
    +/*---------------------------------
    +
    +  qh_printhelp_singular( fp )
    +    prints descriptive message for singular input
    +*/
    +void qh_printhelp_singular(FILE *fp) {
    +  facetT *facet;
    +  vertexT *vertex, **vertexp;
    +  realT min, max, *coord, dist;
    +  int i,k;
    +
    +  qh_fprintf(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(fp, "", qh facet_list, NULL, qh_ALL);
    +  if (!qh_QUICKhelp)
    +    qh_fprintf(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(fp, "center point", qh hull_dim, qh interior_point, qh_IDunknown);
    +  qh_fprintf(fp, 9378, "\n");
    +  FORALLfacets {
    +    qh_fprintf(fp, 9379, "facet");
    +    FOREACHvertex_(facet->vertices)
    +      qh_fprintf(fp, 9380, " p%d", qh_pointid(vertex->point));
    +    zinc_(Zdistio);
    +    qh_distplane(qh interior_point, facet, &dist);
    +    qh_fprintf(fp, 9381, " distance= %4.2g\n", dist);
    +  }
    +  if (!qh_QUICKhelp) {
    +    if (qh HALFspace)
    +      qh_fprintf(fp, 9382, "\n\
    +These points are the dual of the given halfspaces.  They indicate that\n\
    +the intersection is degenerate.\n");
    +    qh_fprintf(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(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(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(fp, 9386, "  %d:  %8.4g  %8.4g  difference= %4.4g\n", k, min, max, max-min);
    +  }
    +  if (!qh_QUICKhelp) {
    +    qh_fprintf(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(fp, 9388, "\
    +  - recompile qhull for realT precision(#define REALfloat 0 in libqhull.h).\n");
    +#endif
    +    qh_fprintf(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 */
    +
    +/*---------------------------------
    +
    +  qh_user_memsizes()
    +    allocate up to 10 additional, quick allocation sizes
    +
    +  notes:
    +    increase maximum number of allocations in qh_initqhull_mem()
    +*/
    +void qh_user_memsizes(void) {
    +
    +  /* qh_memsize(size); */
    +} /* user_memsizes */
    +
    +
    diff --git a/xs/src/qhull/src/libqhull/user.h b/xs/src/qhull/src/libqhull/user.h
    new file mode 100644
    index 000000000..523aa7b4e
    --- /dev/null
    +++ b/xs/src/qhull/src/libqhull/user.h
    @@ -0,0 +1,909 @@
    +/*
      ---------------------------------
    +
    +   user.h
    +   user redefinable constants
    +
    +   for each source file, user.h is included first
    +   see qh-user.htm.  see COPYING for copyright information.
    +
    +   See user.c for sample code.
    +
    +   before reading any code, review libqhull.h for data structure definitions and
    +   the "qh" macro.
    +
    +Sections:
    +   ============= qhull library constants ======================
    +   ============= data types and configuration macros ==========
    +   ============= performance related constants ================
    +   ============= memory constants =============================
    +   ============= joggle constants =============================
    +   ============= conditional compilation ======================
    +   ============= -merge constants- ============================
    +
    +Code flags --
    +  NOerrors -- the code does not call qh_errexit()
    +  WARN64 -- the code may be incompatible with 64-bit pointers
    +
    +*/
    +
    +#include 
    +
    +#ifndef qhDEFuser
    +#define qhDEFuser 1
    +
    +/* Derived from Qt's corelib/global/qglobal.h */
    +#if !defined(SAG_COM) && !defined(__CYGWIN__) && (defined(WIN64) || defined(_WIN64) || defined(__WIN64__) || defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__))
    +#   define QHULL_OS_WIN
    +#elif defined(__MWERKS__) && defined(__INTEL__) /* Metrowerks discontinued before the release of Intel Macs */
    +#   define QHULL_OS_WIN
    +#endif
    +/*============================================================*/
    +/*============= qhull library constants ======================*/
    +/*============================================================*/
    +
    +/*----------------------------------
    +
    +  FILENAMElen -- max length for TI and TO filenames
    +
    +*/
    +
    +#define qh_FILENAMElen 500
    +
    +/*----------------------------------
    +
    +  msgcode -- Unique message codes for qh_fprintf
    +
    +  If add new messages, assign these values and increment in user.h and user_r.h
    +  See QhullError.h for 10000 errors.
    +
    +  def counters =  [27, 1048, 2059, 3026, 4068, 5003,
    +     6273, 7081, 8147, 9411, 10000, 11029]
    +
    +  See: qh_ERR* [libqhull.h]
    +*/
    +
    +#define MSG_TRACE0 0
    +#define MSG_TRACE1 1000
    +#define MSG_TRACE2 2000
    +#define MSG_TRACE3 3000
    +#define MSG_TRACE4 4000
    +#define MSG_TRACE5 5000
    +#define MSG_ERROR  6000   /* errors written to qh.ferr */
    +#define MSG_WARNING 7000
    +#define MSG_STDERR  8000  /* log messages Written to qh.ferr */
    +#define MSG_OUTPUT  9000
    +#define MSG_QHULL_ERROR 10000 /* errors thrown by QhullError.cpp (QHULLlastError is in QhullError.h) */
    +#define MSG_FIXUP  11000  /* FIXUP QH11... */
    +#define MSG_MAXLEN  3000 /* qh_printhelp_degenerate() in user.c */
    +
    +
    +/*----------------------------------
    +
    +  qh_OPTIONline -- max length of an option line 'FO'
    +*/
    +#define qh_OPTIONline 80
    +
    +/*============================================================*/
    +/*============= data types and configuration macros ==========*/
    +/*============================================================*/
    +
    +/*----------------------------------
    +
    +  realT
    +    set the size of floating point numbers
    +
    +  qh_REALdigits
    +    maximimum number of significant digits
    +
    +  qh_REAL_1, qh_REAL_2n, qh_REAL_3n
    +    format strings for printf
    +
    +  qh_REALmax, qh_REALmin
    +    maximum and minimum (near zero) values
    +
    +  qh_REALepsilon
    +    machine roundoff.  Maximum roundoff error for addition and multiplication.
    +
    +  notes:
    +   Select whether to store floating point numbers in single precision (float)
    +   or double precision (double).
    +
    +   Use 'float' to save about 8% in time and 25% in space.  This is particularly
    +   helpful if high-d where convex hulls are space limited.  Using 'float' also
    +   reduces the printed size of Qhull's output since numbers have 8 digits of
    +   precision.
    +
    +   Use 'double' when greater arithmetic precision is needed.  This is needed
    +   for Delaunay triangulations and Voronoi diagrams when you are not merging
    +   facets.
    +
    +   If 'double' gives insufficient precision, your data probably includes
    +   degeneracies.  If so you should use facet merging (done by default)
    +   or exact arithmetic (see imprecision section of manual, qh-impre.htm).
    +   You may also use option 'Po' to force output despite precision errors.
    +
    +   You may use 'long double', but many format statements need to be changed
    +   and you may need a 'long double' square root routine.  S. Grundmann
    +   (sg@eeiwzb.et.tu-dresden.de) has done this.  He reports that the code runs
    +   much slower with little gain in precision.
    +
    +   WARNING: on some machines,    int f(){realT a= REALmax;return (a == REALmax);}
    +      returns False.  Use (a > REALmax/2) instead of (a == REALmax).
    +
    +   REALfloat =   1      all numbers are 'float' type
    +             =   0      all numbers are 'double' type
    +*/
    +#define REALfloat 0
    +
    +#if (REALfloat == 1)
    +#define realT float
    +#define REALmax FLT_MAX
    +#define REALmin FLT_MIN
    +#define REALepsilon FLT_EPSILON
    +#define qh_REALdigits 8   /* maximum number of significant digits */
    +#define qh_REAL_1 "%6.8g "
    +#define qh_REAL_2n "%6.8g %6.8g\n"
    +#define qh_REAL_3n "%6.8g %6.8g %6.8g\n"
    +
    +#elif (REALfloat == 0)
    +#define realT double
    +#define REALmax DBL_MAX
    +#define REALmin DBL_MIN
    +#define REALepsilon DBL_EPSILON
    +#define qh_REALdigits 16    /* maximum number of significant digits */
    +#define qh_REAL_1 "%6.16g "
    +#define qh_REAL_2n "%6.16g %6.16g\n"
    +#define qh_REAL_3n "%6.16g %6.16g %6.16g\n"
    +
    +#else
    +#error unknown float option
    +#endif
    +
    +/*----------------------------------
    +
    +  qh_CPUclock
    +    define the clock() function for reporting the total time spent by Qhull
    +    returns CPU ticks as a 'long int'
    +    qh_CPUclock is only used for reporting the total time spent by Qhull
    +
    +  qh_SECticks
    +    the number of clock ticks per second
    +
    +  notes:
    +    looks for CLOCKS_PER_SEC, CLOCKS_PER_SECOND, or assumes microseconds
    +    to define a custom clock, set qh_CLOCKtype to 0
    +
    +    if your system does not use clock() to return CPU ticks, replace
    +    qh_CPUclock with the corresponding function.  It is converted
    +    to 'unsigned long' to prevent wrap-around during long runs.  By default,
    +     defines clock_t as 'long'
    +
    +   Set qh_CLOCKtype to
    +
    +     1          for CLOCKS_PER_SEC, CLOCKS_PER_SECOND, or microsecond
    +                Note:  may fail if more than 1 hour elapsed time
    +
    +     2          use qh_clock() with POSIX times() (see global.c)
    +*/
    +#define qh_CLOCKtype 1  /* change to the desired number */
    +
    +#if (qh_CLOCKtype == 1)
    +
    +#if defined(CLOCKS_PER_SECOND)
    +#define qh_CPUclock    ((unsigned long)clock())  /* return CPU clock */
    +#define qh_SECticks CLOCKS_PER_SECOND
    +
    +#elif defined(CLOCKS_PER_SEC)
    +#define qh_CPUclock    ((unsigned long)clock())  /* return CPU clock */
    +#define qh_SECticks CLOCKS_PER_SEC
    +
    +#elif defined(CLK_TCK)
    +#define qh_CPUclock    ((unsigned long)clock())  /* return CPU clock */
    +#define qh_SECticks CLK_TCK
    +
    +#else
    +#define qh_CPUclock    ((unsigned long)clock())  /* return CPU clock */
    +#define qh_SECticks 1E6
    +#endif
    +
    +#elif (qh_CLOCKtype == 2)
    +#define qh_CPUclock    qh_clock()  /* return CPU clock */
    +#define qh_SECticks 100
    +
    +#else /* qh_CLOCKtype == ? */
    +#error unknown clock option
    +#endif
    +
    +/*----------------------------------
    +
    +  qh_RANDOMtype, qh_RANDOMmax, qh_RANDOMseed
    +    define random number generator
    +
    +    qh_RANDOMint generates a random integer between 0 and qh_RANDOMmax.
    +    qh_RANDOMseed sets the random number seed for qh_RANDOMint
    +
    +  Set qh_RANDOMtype (default 5) to:
    +    1       for random() with 31 bits (UCB)
    +    2       for rand() with RAND_MAX or 15 bits (system 5)
    +    3       for rand() with 31 bits (Sun)
    +    4       for lrand48() with 31 bits (Solaris)
    +    5       for qh_rand() with 31 bits (included with Qhull)
    +
    +  notes:
    +    Random numbers are used by rbox to generate point sets.  Random
    +    numbers are used by Qhull to rotate the input ('QRn' option),
    +    simulate a randomized algorithm ('Qr' option), and to simulate
    +    roundoff errors ('Rn' option).
    +
    +    Random number generators differ between systems.  Most systems provide
    +    rand() but the period varies.  The period of rand() is not critical
    +    since qhull does not normally use random numbers.
    +
    +    The default generator is Park & Miller's minimal standard random
    +    number generator [CACM 31:1195 '88].  It is included with Qhull.
    +
    +    If qh_RANDOMmax is wrong, qhull will report a warning and Geomview
    +    output will likely be invisible.
    +*/
    +#define qh_RANDOMtype 5   /* *** change to the desired number *** */
    +
    +#if (qh_RANDOMtype == 1)
    +#define qh_RANDOMmax ((realT)0x7fffffffUL)  /* 31 bits, random()/MAX */
    +#define qh_RANDOMint random()
    +#define qh_RANDOMseed_(seed) srandom(seed);
    +
    +#elif (qh_RANDOMtype == 2)
    +#ifdef RAND_MAX
    +#define qh_RANDOMmax ((realT)RAND_MAX)
    +#else
    +#define qh_RANDOMmax ((realT)32767)   /* 15 bits (System 5) */
    +#endif
    +#define qh_RANDOMint  rand()
    +#define qh_RANDOMseed_(seed) srand((unsigned)seed);
    +
    +#elif (qh_RANDOMtype == 3)
    +#define qh_RANDOMmax ((realT)0x7fffffffUL)  /* 31 bits, Sun */
    +#define qh_RANDOMint  rand()
    +#define qh_RANDOMseed_(seed) srand((unsigned)seed);
    +
    +#elif (qh_RANDOMtype == 4)
    +#define qh_RANDOMmax ((realT)0x7fffffffUL)  /* 31 bits, lrand38()/MAX */
    +#define qh_RANDOMint lrand48()
    +#define qh_RANDOMseed_(seed) srand48(seed);
    +
    +#elif (qh_RANDOMtype == 5)
    +#define qh_RANDOMmax ((realT)2147483646UL)  /* 31 bits, qh_rand/MAX */
    +#define qh_RANDOMint qh_rand()
    +#define qh_RANDOMseed_(seed) qh_srand(seed);
    +/* unlike rand(), never returns 0 */
    +
    +#else
    +#error: unknown random option
    +#endif
    +
    +/*----------------------------------
    +
    +  qh_ORIENTclock
    +    0 for inward pointing normals by Geomview convention
    +*/
    +#define qh_ORIENTclock 0
    +
    +
    +/*============================================================*/
    +/*============= joggle constants =============================*/
    +/*============================================================*/
    +
    +/*----------------------------------
    +
    +qh_JOGGLEdefault
    +default qh.JOGGLEmax is qh.DISTround * qh_JOGGLEdefault
    +
    +notes:
    +rbox s r 100 | qhull QJ1e-15 QR0 generates 90% faults at distround 7e-16
    +rbox s r 100 | qhull QJ1e-14 QR0 generates 70% faults
    +rbox s r 100 | qhull QJ1e-13 QR0 generates 35% faults
    +rbox s r 100 | qhull QJ1e-12 QR0 generates 8% faults
    +rbox s r 100 | qhull QJ1e-11 QR0 generates 1% faults
    +rbox s r 100 | qhull QJ1e-10 QR0 generates 0% faults
    +rbox 1000 W0 | qhull QJ1e-12 QR0 generates 86% faults
    +rbox 1000 W0 | qhull QJ1e-11 QR0 generates 20% faults
    +rbox 1000 W0 | qhull QJ1e-10 QR0 generates 2% faults
    +the later have about 20 points per facet, each of which may interfere
    +
    +pick a value large enough to avoid retries on most inputs
    +*/
    +#define qh_JOGGLEdefault 30000.0
    +
    +/*----------------------------------
    +
    +qh_JOGGLEincrease
    +factor to increase qh.JOGGLEmax on qh_JOGGLEretry or qh_JOGGLEagain
    +*/
    +#define qh_JOGGLEincrease 10.0
    +
    +/*----------------------------------
    +
    +qh_JOGGLEretry
    +if ZZretry = qh_JOGGLEretry, increase qh.JOGGLEmax
    +
    +notes:
    +try twice at the original value in case of bad luck the first time
    +*/
    +#define qh_JOGGLEretry 2
    +
    +/*----------------------------------
    +
    +qh_JOGGLEagain
    +every following qh_JOGGLEagain, increase qh.JOGGLEmax
    +
    +notes:
    +1 is OK since it's already failed qh_JOGGLEretry times
    +*/
    +#define qh_JOGGLEagain 1
    +
    +/*----------------------------------
    +
    +qh_JOGGLEmaxincrease
    +maximum qh.JOGGLEmax due to qh_JOGGLEincrease
    +relative to qh.MAXwidth
    +
    +notes:
    +qh.joggleinput will retry at this value until qh_JOGGLEmaxretry
    +*/
    +#define qh_JOGGLEmaxincrease 1e-2
    +
    +/*----------------------------------
    +
    +qh_JOGGLEmaxretry
    +stop after qh_JOGGLEmaxretry attempts
    +*/
    +#define qh_JOGGLEmaxretry 100
    +
    +/*============================================================*/
    +/*============= performance related constants ================*/
    +/*============================================================*/
    +
    +/*----------------------------------
    +
    +  qh_HASHfactor
    +    total hash slots / used hash slots.  Must be at least 1.1.
    +
    +  notes:
    +    =2 for at worst 50% occupancy for qh.hash_table and normally 25% occupancy
    +*/
    +#define qh_HASHfactor 2
    +
    +/*----------------------------------
    +
    +  qh_VERIFYdirect
    +    with 'Tv' verify all points against all facets if op count is smaller
    +
    +  notes:
    +    if greater, calls qh_check_bestdist() instead
    +*/
    +#define qh_VERIFYdirect 1000000
    +
    +/*----------------------------------
    +
    +  qh_INITIALsearch
    +     if qh_INITIALmax, search points up to this dimension
    +*/
    +#define qh_INITIALsearch 6
    +
    +/*----------------------------------
    +
    +  qh_INITIALmax
    +    if dim >= qh_INITIALmax, use min/max coordinate points for initial simplex
    +
    +  notes:
    +    from points with non-zero determinants
    +    use option 'Qs' to override (much slower)
    +*/
    +#define qh_INITIALmax 8
    +
    +/*============================================================*/
    +/*============= memory constants =============================*/
    +/*============================================================*/
    +
    +/*----------------------------------
    +
    +  qh_MEMalign
    +    memory alignment for qh_meminitbuffers() in global.c
    +
    +  notes:
    +    to avoid bus errors, memory allocation must consider alignment requirements.
    +    malloc() automatically takes care of alignment.   Since mem.c manages
    +    its own memory, we need to explicitly specify alignment in
    +    qh_meminitbuffers().
    +
    +    A safe choice is sizeof(double).  sizeof(float) may be used if doubles
    +    do not occur in data structures and pointers are the same size.  Be careful
    +    of machines (e.g., DEC Alpha) with large pointers.
    +
    +    If using gcc, best alignment is  [fmax_() is defined in geom_r.h]
    +              #define qh_MEMalign fmax_(__alignof__(realT),__alignof__(void *))
    +*/
    +#define qh_MEMalign ((int)(fmax_(sizeof(realT), sizeof(void *))))
    +
    +/*----------------------------------
    +
    +  qh_MEMbufsize
    +    size of additional memory buffers
    +
    +  notes:
    +    used for qh_meminitbuffers() in global.c
    +*/
    +#define qh_MEMbufsize 0x10000       /* allocate 64K memory buffers */
    +
    +/*----------------------------------
    +
    +  qh_MEMinitbuf
    +    size of initial memory buffer
    +
    +  notes:
    +    use for qh_meminitbuffers() in global.c
    +*/
    +#define qh_MEMinitbuf 0x20000      /* initially allocate 128K buffer */
    +
    +/*----------------------------------
    +
    +  qh_INFINITE
    +    on output, indicates Voronoi center at infinity
    +*/
    +#define qh_INFINITE  -10.101
    +
    +/*----------------------------------
    +
    +  qh_DEFAULTbox
    +    default box size (Geomview expects 0.5)
    +
    +  qh_DEFAULTbox
    +    default box size for integer coorindate (rbox only)
    +*/
    +#define qh_DEFAULTbox 0.5
    +#define qh_DEFAULTzbox 1e6
    +
    +/*============================================================*/
    +/*============= conditional compilation ======================*/
    +/*============================================================*/
    +
    +/*----------------------------------
    +
    +  __cplusplus
    +    defined by C++ compilers
    +
    +  __MSC_VER
    +    defined by Microsoft Visual C++
    +
    +  __MWERKS__ && __INTEL__
    +    defined by Metrowerks when compiling for Windows (not Intel-based Macintosh)
    +
    +  __MWERKS__ && __POWERPC__
    +    defined by Metrowerks when compiling for PowerPC-based Macintosh
    +  __STDC__
    +    defined for strict ANSI C
    +*/
    +
    +/*----------------------------------
    +
    +  qh_COMPUTEfurthest
    +    compute furthest distance to an outside point instead of storing it with the facet
    +    =1 to compute furthest
    +
    +  notes:
    +    computing furthest saves memory but costs time
    +      about 40% more distance tests for partitioning
    +      removes facet->furthestdist
    +*/
    +#define qh_COMPUTEfurthest 0
    +
    +/*----------------------------------
    +
    +  qh_KEEPstatistics
    +    =0 removes most of statistic gathering and reporting
    +
    +  notes:
    +    if 0, code size is reduced by about 4%.
    +*/
    +#define qh_KEEPstatistics 1
    +
    +/*----------------------------------
    +
    +  qh_MAXoutside
    +    record outer plane for each facet
    +    =1 to record facet->maxoutside
    +
    +  notes:
    +    this takes a realT per facet and slightly slows down qhull
    +    it produces better outer planes for geomview output
    +*/
    +#define qh_MAXoutside 1
    +
    +/*----------------------------------
    +
    +  qh_NOmerge
    +    disables facet merging if defined
    +
    +  notes:
    +    This saves about 10% space.
    +
    +    Unless 'Q0'
    +      qh_NOmerge sets 'QJ' to avoid precision errors
    +
    +    #define qh_NOmerge
    +
    +  see:
    +    qh_NOmem in mem.c
    +
    +    see user.c/user_eg.c for removing io.o
    +*/
    +
    +/*----------------------------------
    +
    +  qh_NOtrace
    +    no tracing if defined
    +
    +  notes:
    +    This saves about 5% space.
    +
    +    #define qh_NOtrace
    +*/
    +
    +/*----------------------------------
    +
    +  qh_QHpointer
    +    access global data with pointer or static structure
    +
    +  qh_QHpointer  = 1     access globals via a pointer to allocated memory
    +                        enables qh_saveqhull() and qh_restoreqhull()
    +                        [2010, gcc] costs about 4% in time and 4% in space
    +                        [2003, msvc] costs about 8% in time and 2% in space
    +
    +                = 0     qh_qh and qh_qhstat are static data structures
    +                        only one instance of qhull() can be active at a time
    +                        default value
    +
    +  qh_QHpointer_dllimport and qh_dllimport define qh_qh as __declspec(dllimport) [libqhull.h]
    +  It is required for msvc-2005.  It is not needed for gcc.
    +
    +  notes:
    +    [jan'16] qh_QHpointer is deprecated for Qhull.  Use libqhull_r instead.
    +    all global variables for qhull are in qh, qhmem, and qhstat
    +    qh is defined in libqhull.h
    +    qhmem is defined in mem.h
    +    qhstat is defined in stat.h
    +
    +*/
    +#ifdef qh_QHpointer
    +#if qh_dllimport
    +#error QH6207 Qhull error: Use qh_QHpointer_dllimport instead of qh_dllimport with qh_QHpointer
    +#endif
    +#else
    +#define qh_QHpointer 0
    +#if qh_QHpointer_dllimport
    +#error QH6234 Qhull error: Use qh_dllimport instead of qh_QHpointer_dllimport when qh_QHpointer is not defined
    +#endif
    +#endif
    +#if 0  /* sample code */
    +    qhT *oldqhA, *oldqhB;
    +
    +    exitcode= qh_new_qhull(dim, numpoints, points, ismalloc,
    +                      flags, outfile, errfile);
    +    /* use results from first call to qh_new_qhull */
    +    oldqhA= qh_save_qhull();
    +    exitcode= qh_new_qhull(dimB, numpointsB, pointsB, ismalloc,
    +                      flags, outfile, errfile);
    +    /* use results from second call to qh_new_qhull */
    +    oldqhB= qh_save_qhull();
    +    qh_restore_qhull(&oldqhA);
    +    /* use results from first call to qh_new_qhull */
    +    qh_freeqhull(qh_ALL);  /* frees all memory used by first call */
    +    qh_restore_qhull(&oldqhB);
    +    /* use results from second call to qh_new_qhull */
    +    qh_freeqhull(!qh_ALL); /* frees long memory used by second call */
    +    qh_memfreeshort(&curlong, &totlong);  /* frees short memory and memory allocator */
    +#endif
    +
    +/*----------------------------------
    +
    +  qh_QUICKhelp
    +    =1 to use abbreviated help messages, e.g., for degenerate inputs
    +*/
    +#define qh_QUICKhelp    0
    +
    +/*============================================================*/
    +/*============= -merge constants- ============================*/
    +/*============================================================*/
    +/*
    +   These constants effect facet merging.  You probably will not need
    +   to modify them.  They effect the performance of facet merging.
    +*/
    +
    +/*----------------------------------
    +
    +  qh_DIMmergeVertex
    +    max dimension for vertex merging (it is not effective in high-d)
    +*/
    +#define qh_DIMmergeVertex 6
    +
    +/*----------------------------------
    +
    +  qh_DIMreduceBuild
    +     max dimension for vertex reduction during build (slow in high-d)
    +*/
    +#define qh_DIMreduceBuild 5
    +
    +/*----------------------------------
    +
    +  qh_BESTcentrum
    +     if > 2*dim+n vertices, qh_findbestneighbor() tests centrums (faster)
    +     else, qh_findbestneighbor() tests all vertices (much better merges)
    +
    +  qh_BESTcentrum2
    +     if qh_BESTcentrum2 * DIM3 + BESTcentrum < #vertices tests centrums
    +*/
    +#define qh_BESTcentrum 20
    +#define qh_BESTcentrum2 2
    +
    +/*----------------------------------
    +
    +  qh_BESTnonconvex
    +    if > dim+n neighbors, qh_findbestneighbor() tests nonconvex ridges.
    +
    +  notes:
    +    It is needed because qh_findbestneighbor is slow for large facets
    +*/
    +#define qh_BESTnonconvex 15
    +
    +/*----------------------------------
    +
    +  qh_MAXnewmerges
    +    if >n newmerges, qh_merge_nonconvex() calls qh_reducevertices_centrums.
    +
    +  notes:
    +    It is needed because postmerge can merge many facets at once
    +*/
    +#define qh_MAXnewmerges 2
    +
    +/*----------------------------------
    +
    +  qh_MAXnewcentrum
    +    if <= dim+n vertices (n approximates the number of merges),
    +      reset the centrum in qh_updatetested() and qh_mergecycle_facets()
    +
    +  notes:
    +    needed to reduce cost and because centrums may move too much if
    +    many vertices in high-d
    +*/
    +#define qh_MAXnewcentrum 5
    +
    +/*----------------------------------
    +
    +  qh_COPLANARratio
    +    for 3-d+ merging, qh.MINvisible is n*premerge_centrum
    +
    +  notes:
    +    for non-merging, it's DISTround
    +*/
    +#define qh_COPLANARratio 3
    +
    +/*----------------------------------
    +
    +  qh_DISToutside
    +    When is a point clearly outside of a facet?
    +    Stops search in qh_findbestnew or qh_partitionall
    +    qh_findbest uses qh.MINoutside since since it is only called if no merges.
    +
    +  notes:
    +    'Qf' always searches for best facet
    +    if !qh.MERGING, same as qh.MINoutside.
    +    if qh_USEfindbestnew, increase value since neighboring facets may be ill-behaved
    +      [Note: Zdelvertextot occurs normally with interior points]
    +            RBOX 1000 s Z1 G1e-13 t1001188774 | QHULL Tv
    +    When there is a sharp edge, need to move points to a
    +    clearly good facet; otherwise may be lost in another partitioning.
    +    if too big then O(n^2) behavior for partitioning in cone
    +    if very small then important points not processed
    +    Needed in qh_partitionall for
    +      RBOX 1000 s Z1 G1e-13 t1001032651 | QHULL Tv
    +    Needed in qh_findbestnew for many instances of
    +      RBOX 1000 s Z1 G1e-13 t | QHULL Tv
    +
    +  See:
    +    qh_DISToutside -- when is a point clearly outside of a facet
    +    qh_SEARCHdist -- when is facet coplanar with the best facet?
    +    qh_USEfindbestnew -- when to use qh_findbestnew for qh_partitionpoint()
    +*/
    +#define qh_DISToutside ((qh_USEfindbestnew ? 2 : 1) * \
    +     fmax_((qh MERGING ? 2 : 1)*qh MINoutside, qh max_outside))
    +
    +/*----------------------------------
    +
    +  qh_RATIOnearinside
    +    ratio of qh.NEARinside to qh.ONEmerge for retaining inside points for
    +    qh_check_maxout().
    +
    +  notes:
    +    This is overkill since do not know the correct value.
    +    It effects whether 'Qc' reports all coplanar points
    +    Not used for 'd' since non-extreme points are coplanar
    +*/
    +#define qh_RATIOnearinside 5
    +
    +/*----------------------------------
    +
    +  qh_SEARCHdist
    +    When is a facet coplanar with the best facet?
    +    qh_findbesthorizon: all coplanar facets of the best facet need to be searched.
    +
    +  See:
    +    qh_DISToutside -- when is a point clearly outside of a facet
    +    qh_SEARCHdist -- when is facet coplanar with the best facet?
    +    qh_USEfindbestnew -- when to use qh_findbestnew for qh_partitionpoint()
    +*/
    +#define qh_SEARCHdist ((qh_USEfindbestnew ? 2 : 1) * \
    +      (qh max_outside + 2 * qh DISTround + fmax_( qh MINvisible, qh MAXcoplanar)));
    +
    +/*----------------------------------
    +
    +  qh_USEfindbestnew
    +     Always use qh_findbestnew for qh_partitionpoint, otherwise use
    +     qh_findbestnew if merged new facet or sharpnewfacets.
    +
    +  See:
    +    qh_DISToutside -- when is a point clearly outside of a facet
    +    qh_SEARCHdist -- when is facet coplanar with the best facet?
    +    qh_USEfindbestnew -- when to use qh_findbestnew for qh_partitionpoint()
    +*/
    +#define qh_USEfindbestnew (zzval_(Ztotmerge) > 50)
    +
    +/*----------------------------------
    +
    +  qh_WIDEcoplanar
    +    n*MAXcoplanar or n*MINvisible for a WIDEfacet
    +
    +    if vertex is further than qh.WIDEfacet from the hyperplane
    +    then its ridges are not counted in computing the area, and
    +    the facet's centrum is frozen.
    +
    +  notes:
    +   qh.WIDEfacet= max(qh.MAXoutside,qh_WIDEcoplanar*qh.MAXcoplanar,
    +      qh_WIDEcoplanar * qh.MINvisible);
    +*/
    +#define qh_WIDEcoplanar 6
    +
    +/*----------------------------------
    +
    +  qh_WIDEduplicate
    +    Merge ratio for errexit from qh_forcedmerges due to duplicate ridge
    +    Override with option Q12 no-wide-duplicate
    +
    +    Notes:
    +      Merging a duplicate ridge can lead to very wide facets.
    +      A future release of qhull will avoid duplicate ridges by removing duplicate sub-ridges from the horizon
    +*/
    +#define qh_WIDEduplicate 100
    +
    +/*----------------------------------
    +
    +  qh_MAXnarrow
    +    max. cosine in initial hull that sets qh.NARROWhull
    +
    +  notes:
    +    If qh.NARROWhull, the initial partition does not make
    +    coplanar points.  If narrow, a coplanar point can be
    +    coplanar to two facets of opposite orientations and
    +    distant from the exact convex hull.
    +
    +    Conservative estimate.  Don't actually see problems until it is -1.0
    +*/
    +#define qh_MAXnarrow -0.99999999
    +
    +/*----------------------------------
    +
    +  qh_WARNnarrow
    +    max. cosine in initial hull to warn about qh.NARROWhull
    +
    +  notes:
    +    this is a conservative estimate.
    +    Don't actually see problems until it is -1.0.  See qh-impre.htm
    +*/
    +#define qh_WARNnarrow -0.999999999999999
    +
    +/*----------------------------------
    +
    +  qh_ZEROdelaunay
    +    a zero Delaunay facet occurs for input sites coplanar with their convex hull
    +    the last normal coefficient of a zero Delaunay facet is within
    +        qh_ZEROdelaunay * qh.ANGLEround of 0
    +
    +  notes:
    +    qh_ZEROdelaunay does not allow for joggled input ('QJ').
    +
    +    You can avoid zero Delaunay facets by surrounding the input with a box.
    +
    +    Use option 'PDk:-n' to explicitly define zero Delaunay facets
    +      k= dimension of input sites (e.g., 3 for 3-d Delaunay triangulation)
    +      n= the cutoff for zero Delaunay facets (e.g., 'PD3:-1e-12')
    +*/
    +#define qh_ZEROdelaunay 2
    +
    +/*============================================================*/
    +/*============= Microsoft DevStudio ==========================*/
    +/*============================================================*/
    +
    +/*
    +   Finding Memory Leaks Using the CRT Library
    +   https://msdn.microsoft.com/en-us/library/x98tx3cf(v=vs.100).aspx
    +
    +   Reports enabled in qh_lib_check for Debug window and stderr
    +
    +   From 2005=>msvcr80d, 2010=>msvcr100d, 2012=>msvcr110d
    +
    +   Watch: {,,msvcr80d.dll}_crtBreakAlloc  Value from {n} in the leak report
    +   _CrtSetBreakAlloc(689); // qh_lib_check() [global_r.c]
    +
    +   Examples
    +     http://free-cad.sourceforge.net/SrcDocu/d2/d7f/MemDebug_8cpp_source.html
    +     https://github.com/illlust/Game/blob/master/library/MemoryLeak.cpp
    +*/
    +#if 0   /* off (0) by default for QHULL_CRTDBG */
    +#define QHULL_CRTDBG
    +#endif
    +
    +#if defined(_MSC_VER) && defined(_DEBUG) && defined(QHULL_CRTDBG)
    +#define _CRTDBG_MAP_ALLOC
    +#include 
    +#include 
    +#endif
    +#endif /* qh_DEFuser */
    +
    +
    +
    diff --git a/xs/src/qhull/src/libqhull/usermem.c b/xs/src/qhull/src/libqhull/usermem.c
    new file mode 100644
    index 000000000..0e99e8f66
    --- /dev/null
    +++ b/xs/src/qhull/src/libqhull/usermem.c
    @@ -0,0 +1,94 @@
    +/*
      ---------------------------------
    +
    +   usermem.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.h for data structures, macros, and user-callable functions.
    +   See user.c for qhull-related, redefinable functions
    +   see user.h for user-definable constants
    +   See userprintf.c for qh_fprintf and userprintf_rbox.c for qh_fprintf_rbox
    +
    +   Please report any errors that you fix to qhull@qhull.org
    +*/
    +
    +#include "libqhull.h"
    +
    +#include 
    +#include 
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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.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 */
    +
    +/*---------------------------------
    +
    +  qh_free( mem )
    +    free memory
    +
    +  notes:
    +    same as free()
    +    No calls to qh_errexit() 
    +*/
    +void qh_free(void *mem) {
    +    free(mem);
    +} /* free */
    +
    +/*---------------------------------
    +
    +    qh_malloc( mem )
    +      allocate memory
    +
    +    notes:
    +      same as malloc()
    +*/
    +void *qh_malloc(size_t size) {
    +    return malloc(size);
    +} /* malloc */
    +
    +
    diff --git a/xs/src/qhull/src/libqhull/userprintf.c b/xs/src/qhull/src/libqhull/userprintf.c
    new file mode 100644
    index 000000000..190d7cd79
    --- /dev/null
    +++ b/xs/src/qhull/src/libqhull/userprintf.c
    @@ -0,0 +1,66 @@
    +/*
      ---------------------------------
    +
    +   userprintf.c
    +   qh_fprintf()
    +
    +   see README.txt  see COPYING.txt for copyright information.
    +
    +   If you recompile and load this file, then userprintf.o will not be loaded
    +   from qhull.a or qhull.lib
    +
    +   See libqhull.h for data structures, macros, and user-callable functions.
    +   See user.c for qhull-related, redefinable functions
    +   see user.h for user-definable constants
    +   See usermem.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.h"
    +#include "mem.h"
    +
    +#include 
    +#include 
    +#include 
    +
    +/*---------------------------------
    +
    +   qh_fprintf(fp, msgcode, format, list of args )
    +     print arguments to *fp according to format
    +     Use qh_fprintf_rbox() for rboxlib.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(FILE *fp, int msgcode, const char *fmt, ... ) {
    +    va_list args;
    +
    +    if (!fp) {
    +        /* could use qhmem.ferr, but probably better to be cautious */
    +        qh_fprintf_stderr(6232, "Qhull internal error (userprintf.c): fp is 0.  Wrong qh_fprintf called.\n");
    +        qh_errexit(6232, NULL, NULL);
    +    }
    +    va_start(args, fmt);
    +#if qh_QHpointer
    +    if (qh_qh && qh ANNOTATEoutput) {
    +#else
    +    if (qh ANNOTATEoutput) {
    +#endif
    +      fprintf(fp, "[QH%.4d]", msgcode);
    +    }else if (msgcode >= MSG_ERROR && msgcode < MSG_STDERR ) {
    +      fprintf(fp, "QH%.4d ", msgcode);
    +    }
    +    vfprintf(fp, fmt, args);
    +    va_end(args);
    +
    +    /* Place debugging traps here. Use with option 'Tn' */
    +
    +} /* qh_fprintf */
    +
    diff --git a/xs/src/qhull/src/libqhull/userprintf_rbox.c b/xs/src/qhull/src/libqhull/userprintf_rbox.c
    new file mode 100644
    index 000000000..8edd2001a
    --- /dev/null
    +++ b/xs/src/qhull/src/libqhull/userprintf_rbox.c
    @@ -0,0 +1,53 @@
    +/*
      ---------------------------------
    +
    +   userprintf_rbox.c
    +   qh_fprintf_rbox()
    +
    +   see README.txt  see COPYING.txt for copyright information.
    +
    +   If you recompile and load this file, then userprintf_rbox.o will not be loaded
    +   from qhull.a or qhull.lib
    +
    +   See libqhull.h for data structures, macros, and user-callable functions.
    +   See user.c for qhull-related, redefinable functions
    +   see user.h for user-definable constants
    +   See usermem.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.h"
    +
    +#include 
    +#include 
    +#include 
    +
    +/*---------------------------------
    +
    +   qh_fprintf_rbox(fp, msgcode, format, list of args )
    +     print arguments to *fp according to format
    +     Use qh_fprintf_rbox() for rboxlib.c
    +
    +   notes:
    +     same as fprintf()
    +     fgets() is not trapped like fprintf()
    +     exit qh_fprintf_rbox via qh_errexit_rbox()
    +*/
    +
    +void qh_fprintf_rbox(FILE *fp, int msgcode, const char *fmt, ... ) {
    +    va_list args;
    +
    +    if (!fp) {
    +        qh_fprintf_stderr(6231, "Qhull internal error (userprintf_rbox.c): fp is 0.  Wrong qh_fprintf_rbox called.\n");
    +        qh_errexit_rbox(6231);
    +    }
    +    if (msgcode >= MSG_ERROR && msgcode < MSG_STDERR)
    +      fprintf(fp, "QH%.4d ", msgcode);
    +    va_start(args, fmt);
    +    vfprintf(fp, fmt, args);
    +    va_end(args);
    +} /* qh_fprintf_rbox */
    +
    diff --git a/xs/src/qhull/src/libqhull_r/Makefile b/xs/src/qhull/src/libqhull_r/Makefile
    new file mode 100644
    index 000000000..5c40969e0
    --- /dev/null
    +++ b/xs/src/qhull/src/libqhull_r/Makefile
    @@ -0,0 +1,240 @@
    +# Simple gcc Makefile for reentrant qhull and rbox (default gcc/g++)
    +#
    +#   make help
    +#   See README.txt and ../../Makefile
    +#       
    +# Variables
    +#   BINDIR         directory where to copy executables
    +#   DESTDIR        destination directory for 'make install'
    +#   DOCDIR         directory where to copy html documentation
    +#   INCDIR         directory where to copy headers
    +#   LIBDIR         directory where to copy libraries
    +#   MANDIR         directory where to copy manual pages
    +#   PRINTMAN       command for printing manual pages
    +#   PRINTC         command for printing C files
    +#   CC             ANSI C or C++ compiler
    +#   CC_OPTS1       options used to compile .c files
    +#   CC_OPTS2       options used to link .o files
    +#   CC_OPTS3       options to build shared libraries
    +#
    +#   LIBQHULL_OBJS  .o files for linking
    +#   LIBQHULL_HDRS  .h files for printing
    +#   CFILES         .c files for printing
    +#   DOCFILES       documentation files
    +#   FILES          miscellaneous files for printing
    +#   TFILES         .txt versions of html files
    +#   FILES          all other files
    +#   LIBQHULL_OBJS  specifies the object files of libqhullstatic_r.a
    +#
    +# Results
    +#   rbox           Generates points sets for qhull, qconvex, etc.
    +#   qhull          Computes convex hulls and related structures
    +#   qconvex, qdelaunay, qhalf, qvoronoi
    +#                  Specializations of qhull for each geometric structure
    +#   libqhullstatic_r.a Static library for reentrant qhull
    +#   testqset_r     Standalone test of reentrant qset_r.c with mem_r.c
    +#   user_eg        An example of using qhull (reentrant)
    +#   user_eg2       An example of using qhull (reentrant)
    +#
    +# Make targets
    +#   make           Build results using gcc or another compiler
    +#   make clean     Remove object files
    +#   make cleanall  Remove generated files
    +#   make doc       Print documentation
    +#   make help
    +#   make install   Copy qhull, rbox, qhull.1, rbox.1 to BINDIR, MANDIR
    +#   make new       Rebuild qhull and rbox from source
    +#   make printall  Print all files
    +#   make qtest     Quick test of qset, rbox, and qhull
    +#   make test      Quck test of qhull, qconvex, etc.
    +#
    +# Do not replace tabs with spaces.  Needed for build rules
    +# Unix line endings (\n)
    +# $Id: //main/2015/qhull/src/libqhull_r/Makefile#6 $
    +
    +DESTDIR = /usr/local
    +BINDIR	= $(DESTDIR)/bin
    +INCDIR	= $(DESTDIR)/include
    +LIBDIR	= $(DESTDIR)/lib
    +DOCDIR	= $(DESTDIR)/share/doc/qhull
    +MANDIR	= $(DESTDIR)/share/man/man1
    +
    +# if you do not have enscript, try a2ps or just use lpr.  The files are text.
    +PRINTMAN = enscript -2rl
    +PRINTC = enscript -2r
    +# PRINTMAN = lpr
    +# PRINTC = lpr
    +
    +#for Gnu's gcc compiler, -O3 for optimization, -g for debugging, -pg for profiling
    +# -fpic  needed for gcc x86_64-linux-gnu.  Not needed for mingw
    +CC        = gcc
    +CC_OPTS1  = -O3 -ansi -I../../src -fpic $(CC_WARNINGS)
    +
    +# for Sun's cc compiler, -fast or O2 for optimization, -g for debugging, -Xc for ANSI
    +#CC       = cc
    +#CC_OPTS1 = -Xc -v -fast -I../../src 
    +
    +# for Silicon Graphics cc compiler, -O2 for optimization, -g for debugging
    +#CC       = cc
    +#CC_OPTS1 = -ansi -O2 -I../../src 
    +
    +# for Next cc compiler with fat executable
    +#CC       = cc
    +#CC_OPTS1 = -ansi -O2 -I../../src -arch m68k -arch i386 -arch hppa
    +
    +# For loader, ld, 
    +CC_OPTS2 = $(CC_OPTS1)
    +
    +# Default targets for make
    +
    +all: qhull_links qhull_all qtest
    +
    +help:
    +	head -n 50 Makefile
    +
    +clean:
    +	rm -f *.o 
    +	# Delete linked files from other directories [qhull_links]
    +	rm -f qconvex_r.c unix_r.c qdelaun_r.c qhalf_r.c qvoronoi_r.c rbox_r.c
    +	rm -f user_eg_r.c user_eg2_r.c testqset_r.c
    +	
    +cleanall: clean
    +	rm -f qconvex qdelaunay qhalf qvoronoi qhull *.exe
    +	rm -f core user_eg_r user_eg2_r testqset_r libqhullstatic_r.a
    +
    +doc: 
    +	$(PRINTMAN) $(TXTFILES) $(DOCFILES)
    +
    +install:
    +	mkdir -p $(BINDIR)
    +	mkdir -p $(DOCDIR)
    +	mkdir -p $(INCDIR)/libqhull
    +	mkdir -p $(MANDIR)
    +	cp -p qconvex qdelaunay qhalf qhull qvoronoi rbox $(BINDIR)
    +	cp -p libqhullstatic_r.a $(LIBDIR)
    +	cp -p ../../html/qhull.man $(MANDIR)/qhull.1
    +	cp -p ../../html/rbox.man $(MANDIR)/rbox.1
    +	cp -p ../../html/* $(DOCDIR)
    +	cp *.h $(INCDIR)/libqhull_r
    +
    +new:	cleanall all
    +
    +printall: doc printh printc printf
    +
    +printh:
    +	$(PRINTC) $(LIBQHULL_HDRS)
    +
    +printc:
    +	$(PRINTC) $(CFILES)
    +
    +# LIBQHULL_OBJS_1 ordered by frequency of execution with small files at end.  Better locality.
    +# Same definitions as ../../Makefile
    +
    +LIBQHULLS_OBJS_1= global_r.o stat_r.o geom2_r.o poly2_r.o merge_r.o \
    +        libqhull_r.o geom_r.o poly_r.o qset_r.o mem_r.o random_r.o 
    +
    +LIBQHULLS_OBJS_2= $(LIBQHULLS_OBJS_1) usermem_r.o userprintf_r.o io_r.o user_r.o
    +
    +LIBQHULLS_OBJS= $(LIBQHULLS_OBJS_2)  rboxlib_r.o userprintf_rbox_r.o
    +
    +LIBQHULL_HDRS= user_r.h libqhull_r.h qhull_ra.h geom_r.h \
    +        io_r.h mem_r.h merge_r.h poly_r.h random_r.h \
    +        qset_r.h stat_r.h
    +
    +# CFILES ordered alphabetically after libqhull.c 
    +CFILES= ../qhull/unix_r.c libqhull_r.c geom_r.c geom2_r.c global_r.c io_r.c \
    +	mem_r.c merge_r.c poly_r.c poly2_r.c random_r.c rboxlib_r.c \
    +	qset_r.c stat_r.c user_r.c usermem_r.c userprintf_r.c \
    +	../qconvex/qconvex.c ../qdelaunay/qdelaun.c ../qhalf/qhalf.c ../qvoronoi/qvoronoi.c
    +
    +TXTFILES= ../../Announce.txt ../../REGISTER.txt ../../COPYING.txt ../../README.txt ../Changes.txt
    +DOCFILES= ../../html/rbox.txt ../../html/qhull.txt
    +
    +.c.o:
    +	$(CC) -c $(CC_OPTS1) -o $@ $<
    +
    +# Work around problems with ../ in Red Hat Linux
    +qhull_links:
    +	# On MINSYS, 'ln -s' may create a copy instead of a symbolic link
    +	[ -f qconvex_r.c ]  || ln -s ../qconvex/qconvex_r.c
    +	[ -f qdelaun_r.c ]  || ln -s ../qdelaunay/qdelaun_r.c
    +	[ -f qhalf_r.c ]    || ln -s ../qhalf/qhalf_r.c
    +	[ -f qvoronoi_r.c ] || ln -s ../qvoronoi/qvoronoi_r.c
    +	[ -f rbox_r.c ]     || ln -s ../rbox/rbox_r.c
    +	[ -f testqset_r.c ] || ln -s ../testqset_r/testqset_r.c
    +	[ -f unix_r.c ]     || ln -s ../qhull/unix_r.c
    +	[ -f user_eg_r.c ]  || ln -s ../user_eg/user_eg_r.c
    +	[ -f user_eg2_r.c ] || ln -s ../user_eg2/user_eg2_r.c
    +
    +# compile qhull without using bin/libqhullstatic_r.a
    +qhull_all: qconvex_r.o qdelaun_r.o qhalf_r.o qvoronoi_r.o unix_r.o user_eg_r.o user_eg2_r.o rbox_r.o testqset_r.o $(LIBQHULLS_OBJS)
    +	$(CC) -o qconvex $(CC_OPTS2) -lm $(LIBQHULLS_OBJS_2) qconvex_r.o
    +	$(CC) -o qdelaunay $(CC_OPTS2) -lm $(LIBQHULLS_OBJS_2) qdelaun_r.o
    +	$(CC) -o qhalf $(CC_OPTS2) -lm $(LIBQHULLS_OBJS_2) qhalf_r.o
    +	$(CC) -o qvoronoi $(CC_OPTS2) -lm $(LIBQHULLS_OBJS_2) qvoronoi_r.o
    +	$(CC) -o qhull $(CC_OPTS2) -lm $(LIBQHULLS_OBJS_2) unix_r.o 
    +	$(CC) -o rbox $(CC_OPTS2) -lm $(LIBQHULLS_OBJS) rbox_r.o
    +	$(CC) -o user_eg $(CC_OPTS2) -lm $(LIBQHULLS_OBJS_2) user_eg_r.o 
    +	$(CC) -o user_eg2 $(CC_OPTS2) -lm $(LIBQHULLS_OBJS_1) user_eg2_r.o  usermem_r.o userprintf_r.o io_r.o
    +	$(CC) -o testqset_r $(CC_OPTS2) -lm mem_r.o qset_r.o usermem_r.o testqset_r.o
    +	-ar -rs libqhullstatic_r.a $(LIBQHULLS_OBJS)
    +	#libqhullstatic_r.a is not needed for qhull
    +	#If 'ar -rs' fails try using 'ar -s' with 'ranlib'
    +	#ranlib libqhullstatic_r.a
    +
    +qtest:
    +	@echo ============================================
    +	@echo == make qtest ==============================
    +	@echo ============================================
    +	@echo -n "== "
    +	@date
    +	@echo
    +	@echo Testing qset.c and mem.c with testqset
    +	./testqset_r 10000
    +	@echo Run the qhull smoketest
    +	./rbox D4 | ./qhull
    +	@echo ============================================
    +	@echo == To smoketest qhull programs
    +	@echo '==     make test'
    +	@echo ============================================
    +	@echo
    +	@echo ============================================
    +	@echo == For all make targets
    +	@echo '==     make help'
    +	@echo ============================================
    +	@echo
    +
    +test: qtest
    +	@echo ==============================
    +	@echo ========= qconvex ============
    +	@echo ==============================
    +	-./rbox 10 | ./qconvex Tv 
    +	@echo
    +	@echo ==============================
    +	@echo ========= qdelaunay ==========
    +	@echo ==============================
    +	-./rbox 10 | ./qdelaunay Tv 
    +	@echo
    +	@echo ==============================
    +	@echo ========= qhalf ==============
    +	@echo ==============================
    +	-./rbox 10 | ./qconvex FQ FV n Tv | ./qhalf Tv
    +	@echo
    +	@echo ==============================
    +	@echo ========= qvoronoi ===========
    +	@echo ==============================
    +	-./rbox 10 | ./qvoronoi Tv
    +	@echo
    +	@echo ==============================
    +	@echo ========= user_eg ============
    +	@echo == w/o shared library ========
    +	@echo ==============================
    +	-./user_eg
    +	@echo
    +	@echo ==============================
    +	@echo ========= user_eg2 ===========
    +	@echo ==============================
    +	-./user_eg2
    +	@echo
    +
    +# end of Makefile
    diff --git a/xs/src/qhull/src/libqhull_r/geom2_r.c b/xs/src/qhull/src/libqhull_r/geom2_r.c
    new file mode 100644
    index 000000000..48addba1c
    --- /dev/null
    +++ b/xs/src/qhull/src/libqhull_r/geom2_r.c
    @@ -0,0 +1,2096 @@
    +/*
      ---------------------------------
    +
    +
    +   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 ============*/
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +
    +/*---------------------------------
    +
    +  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= 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 */
    +
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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:", ¢er, 1, dim);
    +      if (qh->IStracing >= 5) {
    +        qh_printpoints(qh, qh->ferr, "points", simplex);
    +        FOREACHpoint_(simplex)
    +          qh_fprintf(qh, qh->ferr, 8034, "p%d dist %.2g, ", qh_pointid(qh, point),
    +                   qh_pointdist(point, center, dim));
    +        qh_fprintf(qh, qh->ferr, 8035, "\n");
    +      }
    +    }
    +#endif
    +  }
    +  if (simplex != points)
    +    qh_settempfree(qh, &simplex);
    +  return center;
    +} /* voronoi_center */
    +
    diff --git a/xs/src/qhull/src/libqhull_r/geom_r.c b/xs/src/qhull/src/libqhull_r/geom_r.c
    new file mode 100644
    index 000000000..8104813ca
    --- /dev/null
    +++ b/xs/src/qhull/src/libqhull_r/geom_r.c
    @@ -0,0 +1,1234 @@
    +/*
      ---------------------------------
    +
    +   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"
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 [?] ============ */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +
    +/*---------------------------------
    +
    +  qh_sethyperplane_gauss(qh, dim, rows, point0, toporient, normal, offset, nearzero )
    +    given(dim-1) X dim array of rows[i]= V_{i+1} - V_0 (point0)
    +    set normalized hyperplane equation from oriented simplex
    +
    +  returns:
    +    normal (normalized)
    +    offset (places point0 on the hyperplane)
    +
    +  notes:
    +    if nearzero
    +      orientation may be incorrect because of incorrect sign flips in gausselim
    +    solves [V_n-V_0,...,V_1-V_0, 0 .. 0 1] * N == [0 .. 0 1]
    +        or [V_n-V_0,...,V_1-V_0, 0 .. 0 1] * N == [0]
    +    i.e., N is normal to the hyperplane, and the unnormalized
    +        distance to [0 .. 1] is either 1 or   0
    +
    +  design:
    +    perform gaussian elimination
    +    flip sign for negative values
    +    perform back substitution
    +    normalize result
    +    compute offset
    +*/
    +void qh_sethyperplane_gauss(qhT *qh, int dim, coordT **rows, pointT *point0,
    +                boolT toporient, coordT *normal, coordT *offset, boolT *nearzero) {
    +  coordT *pointcoord, *normalcoef;
    +  int k;
    +  boolT sign= toporient, nearzero2= False;
    +
    +  qh_gausselim(qh, rows, dim-1, dim, &sign, nearzero);
    +  for (k=dim-1; k--; ) {
    +    if ((rows[k])[k] < 0)
    +      sign ^= 1;
    +  }
    +  if (*nearzero) {
    +    zzinc_(Znearlysingular);
    +    trace0((qh, qh->ferr, 4, "qh_sethyperplane_gauss: nearly singular or axis parallel hyperplane during p%d.\n", qh->furthest_id));
    +    qh_backnormal(qh, rows, dim-1, dim, sign, normal, &nearzero2);
    +  }else {
    +    qh_backnormal(qh, rows, dim-1, dim, sign, normal, &nearzero2);
    +    if (nearzero2) {
    +      zzinc_(Znearlysingular);
    +      trace0((qh, qh->ferr, 5, "qh_sethyperplane_gauss: singular or axis parallel hyperplane at normalization during p%d.\n", qh->furthest_id));
    +    }
    +  }
    +  if (nearzero2)
    +    *nearzero= True;
    +  qh_normalize2(qh, normal, dim, True, NULL, NULL);
    +  pointcoord= point0;
    +  normalcoef= normal;
    +  *offset= -(*pointcoord++ * *normalcoef++);
    +  for (k=dim-1; k--; )
    +    *offset -= *pointcoord++ * *normalcoef++;
    +} /* sethyperplane_gauss */
    +
    +
    +
    diff --git a/xs/src/qhull/src/libqhull_r/geom_r.h b/xs/src/qhull/src/libqhull_r/geom_r.h
    new file mode 100644
    index 000000000..d73e95345
    --- /dev/null
    +++ b/xs/src/qhull/src/libqhull_r/geom_r.h
    @@ -0,0 +1,184 @@
    +/*
      ---------------------------------
    +
    +  geom_r.h
    +    header file for geometric routines
    +
    +   see qh-geom_r.htm and geom_r.c
    +
    +   Copyright (c) 1993-2015 The Geometry Center.
    +   $Id: //main/2015/qhull/src/libqhull_r/geom_r.h#3 $$Change: 2079 $
    +   $DateTime: 2016/02/07 17:43:34 $$Author: bbarber $
    +*/
    +
    +#ifndef qhDEFgeom
    +#define qhDEFgeom 1
    +
    +#include "libqhull_r.h"
    +
    +/* ============ -macros- ======================== */
    +
    +/*----------------------------------
    +
    +  fabs_(a)
    +    returns the absolute value of a
    +*/
    +#define fabs_( a ) ((( a ) < 0 ) ? -( a ):( a ))
    +
    +/*----------------------------------
    +
    +  fmax_(a,b)
    +    returns the maximum value of a and b
    +*/
    +#define fmax_( a,b )  ( ( a ) < ( b ) ? ( b ) : ( a ) )
    +
    +/*----------------------------------
    +
    +  fmin_(a,b)
    +    returns the minimum value of a and b
    +*/
    +#define fmin_( a,b )  ( ( a ) > ( b ) ? ( b ) : ( a ) )
    +
    +/*----------------------------------
    +
    +  maximize_(maxval, val)
    +    set maxval to val if val is greater than maxval
    +*/
    +#define maximize_( maxval, val ) { if (( maxval ) < ( val )) ( maxval )= ( val ); }
    +
    +/*----------------------------------
    +
    +  minimize_(minval, val)
    +    set minval to val if val is less than minval
    +*/
    +#define minimize_( minval, val ) { if (( minval ) > ( val )) ( minval )= ( val ); }
    +
    +/*----------------------------------
    +
    +  det2_(a1, a2,
    +        b1, b2)
    +
    +    compute a 2-d determinate
    +*/
    +#define det2_( a1,a2,b1,b2 ) (( a1 )*( b2 ) - ( a2 )*( b1 ))
    +
    +/*----------------------------------
    +
    +  det3_(a1, a2, a3,
    +       b1, b2, b3,
    +       c1, c2, c3)
    +
    +    compute a 3-d determinate
    +*/
    +#define det3_( a1,a2,a3,b1,b2,b3,c1,c2,c3 ) ( ( a1 )*det2_( b2,b3,c2,c3 ) \
    +                - ( b1 )*det2_( a2,a3,c2,c3 ) + ( c1 )*det2_( a2,a3,b2,b3 ) )
    +
    +/*----------------------------------
    +
    +  dX( p1, p2 )
    +  dY( p1, p2 )
    +  dZ( p1, p2 )
    +
    +    given two indices into rows[],
    +
    +    compute the difference between X, Y, or Z coordinates
    +*/
    +#define dX( p1,p2 )  ( *( rows[p1] ) - *( rows[p2] ))
    +#define dY( p1,p2 )  ( *( rows[p1]+1 ) - *( rows[p2]+1 ))
    +#define dZ( p1,p2 )  ( *( rows[p1]+2 ) - *( rows[p2]+2 ))
    +#define dW( p1,p2 )  ( *( rows[p1]+3 ) - *( rows[p2]+3 ))
    +
    +/*============= prototypes in alphabetical order, infrequent at end ======= */
    +
    +#ifdef __cplusplus
    +extern "C" {
    +#endif
    +
    +void    qh_backnormal(qhT *qh, realT **rows, int numrow, int numcol, boolT sign, coordT *normal, boolT *nearzero);
    +void    qh_distplane(qhT *qh, pointT *point, facetT *facet, realT *dist);
    +facetT *qh_findbest(qhT *qh, pointT *point, facetT *startfacet,
    +                     boolT bestoutside, boolT isnewfacets, boolT noupper,
    +                     realT *dist, boolT *isoutside, int *numpart);
    +facetT *qh_findbesthorizon(qhT *qh, boolT ischeckmax, pointT *point,
    +                     facetT *startfacet, boolT noupper, realT *bestdist, int *numpart);
    +facetT *qh_findbestnew(qhT *qh, pointT *point, facetT *startfacet, realT *dist,
    +                     boolT bestoutside, boolT *isoutside, int *numpart);
    +void    qh_gausselim(qhT *qh, realT **rows, int numrow, int numcol, boolT *sign, boolT *nearzero);
    +realT   qh_getangle(qhT *qh, pointT *vect1, pointT *vect2);
    +pointT *qh_getcenter(qhT *qh, setT *vertices);
    +pointT *qh_getcentrum(qhT *qh, facetT *facet);
    +realT   qh_getdistance(qhT *qh, facetT *facet, facetT *neighbor, realT *mindist, realT *maxdist);
    +void    qh_normalize(qhT *qh, coordT *normal, int dim, boolT toporient);
    +void    qh_normalize2(qhT *qh, coordT *normal, int dim, boolT toporient,
    +            realT *minnorm, boolT *ismin);
    +pointT *qh_projectpoint(qhT *qh, pointT *point, facetT *facet, realT dist);
    +
    +void    qh_setfacetplane(qhT *qh, facetT *newfacets);
    +void    qh_sethyperplane_det(qhT *qh, int dim, coordT **rows, coordT *point0,
    +              boolT toporient, coordT *normal, realT *offset, boolT *nearzero);
    +void    qh_sethyperplane_gauss(qhT *qh, int dim, coordT **rows, pointT *point0,
    +             boolT toporient, coordT *normal, coordT *offset, boolT *nearzero);
    +boolT   qh_sharpnewfacets(qhT *qh);
    +
    +/*========= infrequently used code in geom2_r.c =============*/
    +
    +coordT *qh_copypoints(qhT *qh, coordT *points, int numpoints, int dimension);
    +void    qh_crossproduct(int dim, realT vecA[3], realT vecB[3], realT vecC[3]);
    +realT   qh_determinant(qhT *qh, realT **rows, int dim, boolT *nearzero);
    +realT   qh_detjoggle(qhT *qh, pointT *points, int numpoints, int dimension);
    +void    qh_detroundoff(qhT *qh);
    +realT   qh_detsimplex(qhT *qh, pointT *apex, setT *points, int dim, boolT *nearzero);
    +realT   qh_distnorm(int dim, pointT *point, pointT *normal, realT *offsetp);
    +realT   qh_distround(qhT *qh, int dimension, realT maxabs, realT maxsumabs);
    +realT   qh_divzero(realT numer, realT denom, realT mindenom1, boolT *zerodiv);
    +realT   qh_facetarea(qhT *qh, facetT *facet);
    +realT   qh_facetarea_simplex(qhT *qh, int dim, coordT *apex, setT *vertices,
    +          vertexT *notvertex,  boolT toporient, coordT *normal, realT *offset);
    +pointT *qh_facetcenter(qhT *qh, setT *vertices);
    +facetT *qh_findgooddist(qhT *qh, pointT *point, facetT *facetA, realT *distp, facetT **facetlist);
    +void    qh_getarea(qhT *qh, facetT *facetlist);
    +boolT   qh_gram_schmidt(qhT *qh, int dim, realT **rows);
    +boolT   qh_inthresholds(qhT *qh, coordT *normal, realT *angle);
    +void    qh_joggleinput(qhT *qh);
    +realT  *qh_maxabsval(realT *normal, int dim);
    +setT   *qh_maxmin(qhT *qh, pointT *points, int numpoints, int dimension);
    +realT   qh_maxouter(qhT *qh);
    +void    qh_maxsimplex(qhT *qh, int dim, setT *maxpoints, pointT *points, int numpoints, setT **simplex);
    +realT   qh_minabsval(realT *normal, int dim);
    +int     qh_mindiff(realT *vecA, realT *vecB, int dim);
    +boolT   qh_orientoutside(qhT *qh, facetT *facet);
    +void    qh_outerinner(qhT *qh, facetT *facet, realT *outerplane, realT *innerplane);
    +coordT  qh_pointdist(pointT *point1, pointT *point2, int dim);
    +void    qh_printmatrix(qhT *qh, FILE *fp, const char *string, realT **rows, int numrow, int numcol);
    +void    qh_printpoints(qhT *qh, FILE *fp, const char *string, setT *points);
    +void    qh_projectinput(qhT *qh);
    +void    qh_projectpoints(qhT *qh, signed char *project, int n, realT *points,
    +             int numpoints, int dim, realT *newpoints, int newdim);
    +void    qh_rotateinput(qhT *qh, realT **rows);
    +void    qh_rotatepoints(qhT *qh, realT *points, int numpoints, int dim, realT **rows);
    +void    qh_scaleinput(qhT *qh);
    +void    qh_scalelast(qhT *qh, coordT *points, int numpoints, int dim, coordT low,
    +                   coordT high, coordT newhigh);
    +void    qh_scalepoints(qhT *qh, pointT *points, int numpoints, int dim,
    +                realT *newlows, realT *newhighs);
    +boolT   qh_sethalfspace(qhT *qh, int dim, coordT *coords, coordT **nextp,
    +              coordT *normal, coordT *offset, coordT *feasible);
    +coordT *qh_sethalfspace_all(qhT *qh, int dim, int count, coordT *halfspaces, pointT *feasible);
    +pointT *qh_voronoi_center(qhT *qh, int dim, setT *points);
    +
    +#ifdef __cplusplus
    +} /* extern "C"*/
    +#endif
    +
    +#endif /* qhDEFgeom */
    +
    +
    +
    diff --git a/xs/src/qhull/src/libqhull_r/global_r.c b/xs/src/qhull/src/libqhull_r/global_r.c
    new file mode 100644
    index 000000000..eef465ca1
    --- /dev/null
    +++ b/xs/src/qhull/src/libqhull_r/global_r.c
    @@ -0,0 +1,2100 @@
    +
    +/*
      ---------------------------------
    +
    +   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 =======================*/
    +
    +/*----------------------------------
    +
    +  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)";
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  qh_zero( qh, errfile )
    +    Initialize and zero Qhull's memory for qh_new_qhull()
    +
    +  notes:
    +    Not needed in global.c because static variables are initialized to zero
    +*/
    +void qh_zero(qhT *qh, FILE *errfile) {
    +    memset((char *)qh, 0, sizeof(qhT));   /* every field is 0, FALSE, NULL */
    +    qh->NOerrexit= True;
    +    qh_meminit(qh, errfile);
    +} /* zero */
    +
    diff --git a/xs/src/qhull/src/libqhull_r/index.htm b/xs/src/qhull/src/libqhull_r/index.htm
    new file mode 100644
    index 000000000..c62030e06
    --- /dev/null
    +++ b/xs/src/qhull/src/libqhull_r/index.htm
    @@ -0,0 +1,266 @@
    +
    +
    +
    +
    +Reentrant Qhull functions, macros, and data structures
    +
    +
    +
    +
    +

    Up: Home page for Qhull
    +Up: Qhull manual: Table of Contents
    +Up: Programs +• Options +• Output +• Formats +• Geomview +• Print +• Qhull +• Precision +• Trace +• Functions
    +Up: Qhull code
    +To: Qhull files
    +To: GeomGlobal +• IoMem +• MergePoly +• QhullSet +• StatUser + +


    + + +

    Reentrant Qhull functions, macros, and data structures

    +
    +

    The following sections provide an overview and index to +reentrant Qhull's functions, macros, and data structures. +Each section starts with an introduction. +See also Calling +Qhull from C programs and Calling Qhull from C++ programs.

    + +

    Qhull uses the following conventions:

    +
    + +
      +
    • in code, global variables start with "qh " +
    • in documentation, global variables start with 'qh.' +
    • constants start with an upper case word +
    • important globals include an '_' +
    • functions, macros, and constants start with "qh_"
    • +
    • data types end in "T"
    • +
    • macros with arguments end in "_"
    • +
    • iterators are macros that use local variables
    • +
    • iterators for sets start with "FOREACH"
    • +
    • iterators for lists start with "FORALL"
    • +
    • qhull options are in single quotes (e.g., 'Pdn')
    • +
    • lists are sorted alphabetically
    • +
    • preprocessor directives on left margin for older compilers
    • +
    +
    +

    +Reentrant Qhull is nearly the same as non-reentrant Qhull. In reentrant +Qhull, the qhT data structure is the first parameter to most functions. Qhull accesses +this data structure with 'qh->...'. +In non-reentrant Qhull, the global data structure is either a struct (qh_QHpointer==0) +or a pointer (qh_QHpointer==1). The non-reentrant code looks different because this data +structure is accessed via the 'qh' macro. This macro expands to 'qh_qh.' or 'qh_qh->' (resp.). +

    +When reading code with an editor, a search for +'"function' +will locate the header of qh_function. A search for '* function' +will locate the tail of qh_function. + +

    A useful starting point is libqhull_r.h. It defines most +of Qhull data structures and top-level functions. Search for 'PFn' to +determine the corresponding constant in Qhull. Search for 'Fp' to +determine the corresponding qh_PRINT... constant. +Search io_r.c to learn how the print function is implemented.

    + +

    If your web browser is configured for .c and .h files, the function, macro, and data type links +go to the corresponding source location. To configure your web browser for .c and .h files. +

      +
    • In the Download Preferences or Options panel, add file extensions 'c' and 'h' to mime type 'text/html'. +
    • Opera 12.10 +
        +
      1. In Tools > Preferences > Advanced > Downloads +
      2. Uncheck 'Hide file types opened with Opera' +
      3. Quick find 'html' +
      4. Select 'text/html' > Edit +
      5. Add File extensions 'c,h,' +
      6. Click 'OK' +
      +
    • Internet Explorer -- Mime types are not available from 'Internet Options'. Is there a registry key for these settings? +
    • Firefox -- Mime types are not available from 'Preferences'. Is there an add-on to change the file extensions for a mime type? +
    • Chrome -- Can Chrome be configured? +
    + +

    +Please report documentation and link errors +to qhull-bug@qhull.org. +

    + +

    Copyright © 1997-2015 C.B. Barber

    + +
    + +

    »Qhull files

    +
    + +

    This sections lists the .c and .h files for Qhull. Please +refer to these files for detailed information.

    +
    + +
    +
    Makefile, CMakeLists.txt
    +
    Makefile is preconfigured for gcc. CMakeLists.txt supports multiple +platforms with CMake. +Qhull includes project files for Visual Studio and Qt. +
    + +
     
    +
    libqhull_r.h
    +
    Include file for the Qhull library (libqhull.so, qhull.dll, libqhullstatic.a). +Data structures are documented under Poly. +Global variables are documented under Global. +Other data structures and variables are documented under +Qhull or Geom.
    + +
     
    +
    Geom, +geom_r.h, +geom_r.c, +geom2_r.c, +random_r.c, +random_r.h
    +
    Geometric routines. These routines implement mathematical +functions such as Gaussian elimination and geometric +routines needed for Qhull. Frequently used routines are +in geom_r.c while infrequent ones are in geom2_r.c. +
    + +
     
    +
    Global, +global_r.c, +libqhull_r.h
    +
    Global routines. Qhull uses a global data structure, qh, +to store globally defined constants, lists, sets, and +variables. +global_r.c initializes and frees these +structures.
    + +
     
    +
    Io, io_r.h, +io_r.c
    +
    Input and output routines. Qhull provides a wide range of +input and output options.
    + +
     
    +
    Mem, +mem_r.h, +mem_r.c
    +
    Memory routines. Qhull provides memory allocation and +deallocation. It uses quick-fit allocation.
    + +
     
    +
    Merge, +merge_r.h, +merge_r.c
    +
    Merge routines. Qhull handles precision problems by +merged facets or joggled input. These routines merge simplicial facets, +merge non-simplicial facets, merge cycles of facets, and +rename redundant vertices.
    + +
     
    +
    Poly, +poly_r.h, +poly_r.c, +poly2_r.c, +libqhull_r.h
    +
    Polyhedral routines. Qhull produces a polyhedron as a +list of facets with vertices, neighbors, ridges, and +geometric information. libqhull_r.h defines the main +data structures. Frequently used routines are in poly_r.c +while infrequent ones are in poly2_r.c.
    + +
     
    +
    Qhull, +libqhull_r.c, +libqhull_r.h, +qhull_ra.h, +unix_r.c , +qconvex_r.c , +qdelaun_r.c , +qhalf_r.c , +qvoronoi_r.c
    +
    Top-level routines. The Quickhull algorithm is +implemented by libqhull_r.c. qhull_ra.h +includes all header files.
    + +
     
    +
    Set, +qset_r.h, +qset_r.c
    +
    Set routines. Qhull implements its data structures as +sets. A set is an array of pointers that is expanded as +needed. This is a separate package that may be used in +other applications.
    + +
     
    +
    Stat, +stat_r.h, +stat_r.c
    +
    Statistical routines. Qhull maintains statistics about +its implementation.
    + +
     
    +
    User, +user_r.h, +user_r.c, +user_eg_r.c, +user_eg2_r.c, +user_eg3_r.cpp, +
    +
    User-defined routines. Qhull allows the user to configure +the code with defined constants and specialized routines. +
    +
    +
    + +
    +

    +
    +

    Up: +Home page for +Qhull
    +Up: Qhull manual: Table of Contents
    +Up: Programs +• Options +• Output +• Formats +• Geomview +• Print +• Qhull +• Precision +• Trace +• Functions
    +Up: Qhull code: Table of Contents
    +To: Qhull files
    +To: Geom • +GlobalIo +• MemMerge +• PolyQhull +• SetStat +• User
    + +

    +
    +

    The +Geometry Center Home Page

    +

    Comments to: qhull@qhull.org +
    +Created: May 2, 1997 --- Last modified: see top

    + + diff --git a/xs/src/qhull/src/libqhull_r/io_r.c b/xs/src/qhull/src/libqhull_r/io_r.c new file mode 100644 index 000000000..9721a000d --- /dev/null +++ b/xs/src/qhull/src/libqhull_r/io_r.c @@ -0,0 +1,4062 @@ +/*
      ---------------------------------
    +
    +   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)  =====*/
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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));
    +}*/
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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, ¢ers, neighbor);
    +      }else if (firstinf) {
    +        firstinf= False;
    +        qh_setappend(qh, ¢ers, neighbor);
    +      }
    +    }
    +  }
    +  qsort(SETaddr_(centers, facetT), (size_t)qh_setsize(qh, centers),
    +             sizeof(facetT *), qh_compare_facetvisit);
    +  qh_settempfree(qh, &tricenters);
    +  return centers;
    +} /* detvridge */
    +
    +/*---------------------------------
    +
    +  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, ¢ers, facet);
    +      }else if (firstinf) {
    +        firstinf= False;
    +        qh_setappend(qh, ¢ers, 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 */
    +
    +/*---------------------------------
    +
    +  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, ¢ers);
    +            }
    +          }
    +        }
    +      }
    +    }
    +  }
    +  FOREACHneighbor_(atvertex)
    +    neighbor->seen= False;
    +  qh_settempfree(qh, &tricenters);
    +  return totridges;
    +} /* eachvoronoi */
    +
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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);
    +}
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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]);
    +}
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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
    +*/
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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
    +*/
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  qh_skipfilename(qh, string )
    +    returns pointer to character after filename
    +
    +  notes:
    +    skips leading spaces
    +    ends with spacing or eol
    +    if starts with ' or " ends with the same, skipping \' or \"
    +    For qhull, qh_argv_to_command() only uses double quotes
    +*/
    +char *qh_skipfilename(qhT *qh, char *filename) {
    +  char *s= filename;  /* non-const due to return */
    +  char c;
    +
    +  while (*s && isspace(*s))
    +    s++;
    +  c= *s++;
    +  if (c == '\0') {
    +    qh_fprintf(qh, qh->ferr, 6204, "qhull input error: filename expected, none found.\n");
    +    qh_errexit(qh, qh_ERRinput, NULL, NULL);
    +  }
    +  if (c == '\'' || c == '"') {
    +    while (*s !=c || s[-1] == '\\') {
    +      if (!*s) {
    +        qh_fprintf(qh, qh->ferr, 6203, "qhull input error: missing quote after filename -- %s\n", filename);
    +        qh_errexit(qh, qh_ERRinput, NULL, NULL);
    +      }
    +      s++;
    +    }
    +    s++;
    +  }
    +  else while (*s && !isspace(*s))
    +      s++;
    +  return s;
    +} /* skipfilename */
    +
    diff --git a/xs/src/qhull/src/libqhull_r/io_r.h b/xs/src/qhull/src/libqhull_r/io_r.h
    new file mode 100644
    index 000000000..12e05ae7a
    --- /dev/null
    +++ b/xs/src/qhull/src/libqhull_r/io_r.h
    @@ -0,0 +1,167 @@
    +/*
      ---------------------------------
    +
    +   io_r.h
    +   declarations of Input/Output functions
    +
    +   see README, libqhull_r.h and io_r.c
    +
    +   Copyright (c) 1993-2015 The Geometry Center.
    +   $Id: //main/2015/qhull/src/libqhull_r/io_r.h#3 $$Change: 2079 $
    +   $DateTime: 2016/02/07 17:43:34 $$Author: bbarber $
    +*/
    +
    +#ifndef qhDEFio
    +#define qhDEFio 1
    +
    +#include "libqhull_r.h"
    +
    +/*============ constants and flags ==================*/
    +
    +/*----------------------------------
    +
    +  qh_MAXfirst
    +    maximum length of first two lines of stdin
    +*/
    +#define qh_MAXfirst  200
    +
    +/*----------------------------------
    +
    +  qh_MINradius
    +    min radius for Gp and Gv, fraction of maxcoord
    +*/
    +#define qh_MINradius 0.02
    +
    +/*----------------------------------
    +
    +  qh_GEOMepsilon
    +    adjust outer planes for 'lines closer' and geomview roundoff.
    +    This prevents bleed through.
    +*/
    +#define qh_GEOMepsilon 2e-3
    +
    +/*----------------------------------
    +
    +  qh_WHITESPACE
    +    possible values of white space
    +*/
    +#define qh_WHITESPACE " \n\t\v\r\f"
    +
    +
    +/*----------------------------------
    +
    +  qh_RIDGE
    +    to select which ridges to print in qh_eachvoronoi
    +*/
    +typedef enum
    +{
    +    qh_RIDGEall = 0, qh_RIDGEinner, qh_RIDGEouter
    +}
    +qh_RIDGE;
    +
    +/*----------------------------------
    +
    +  printvridgeT
    +    prints results of qh_printvdiagram
    +
    +  see:
    +    qh_printvridge for an example
    +*/
    +typedef void (*printvridgeT)(qhT *qh, FILE *fp, vertexT *vertex, vertexT *vertexA, setT *centers, boolT unbounded);
    +
    +/*============== -prototypes in alphabetical order =========*/
    +
    +#ifdef __cplusplus
    +extern "C" {
    +#endif
    +
    +void    qh_dfacet(qhT *qh, unsigned id);
    +void    qh_dvertex(qhT *qh, unsigned id);
    +int     qh_compare_facetarea(const void *p1, const void *p2);
    +int     qh_compare_facetmerge(const void *p1, const void *p2);
    +int     qh_compare_facetvisit(const void *p1, const void *p2);
    +/* int  qh_compare_vertexpoint(const void *p1, const void *p2); Not useable since it depends on qh */
    +void    qh_copyfilename(qhT *qh, char *filename, int size, const char* source, int length);
    +void    qh_countfacets(qhT *qh, facetT *facetlist, setT *facets, boolT printall,
    +              int *numfacetsp, int *numsimplicialp, int *totneighborsp,
    +              int *numridgesp, int *numcoplanarsp, int *numnumtricoplanarsp);
    +pointT *qh_detvnorm(qhT *qh, vertexT *vertex, vertexT *vertexA, setT *centers, realT *offsetp);
    +setT   *qh_detvridge(qhT *qh, vertexT *vertex);
    +setT   *qh_detvridge3(qhT *qh, vertexT *atvertex, vertexT *vertex);
    +int     qh_eachvoronoi(qhT *qh, FILE *fp, printvridgeT printvridge, vertexT *atvertex, boolT visitall, qh_RIDGE innerouter, boolT inorder);
    +int     qh_eachvoronoi_all(qhT *qh, FILE *fp, printvridgeT printvridge, boolT isUpper, qh_RIDGE innerouter, boolT inorder);
    +void    qh_facet2point(qhT *qh, facetT *facet, pointT **point0, pointT **point1, realT *mindist);
    +setT   *qh_facetvertices(qhT *qh, facetT *facetlist, setT *facets, boolT allfacets);
    +void    qh_geomplanes(qhT *qh, facetT *facet, realT *outerplane, realT *innerplane);
    +void    qh_markkeep(qhT *qh, facetT *facetlist);
    +setT   *qh_markvoronoi(qhT *qh, facetT *facetlist, setT *facets, boolT printall, boolT *isLowerp, int *numcentersp);
    +void    qh_order_vertexneighbors(qhT *qh, vertexT *vertex);
    +void    qh_prepare_output(qhT *qh);
    +void    qh_printafacet(qhT *qh, FILE *fp, qh_PRINT format, facetT *facet, boolT printall);
    +void    qh_printbegin(qhT *qh, FILE *fp, qh_PRINT format, facetT *facetlist, setT *facets, boolT printall);
    +void    qh_printcenter(qhT *qh, FILE *fp, qh_PRINT format, const char *string, facetT *facet);
    +void    qh_printcentrum(qhT *qh, FILE *fp, facetT *facet, realT radius);
    +void    qh_printend(qhT *qh, FILE *fp, qh_PRINT format, facetT *facetlist, setT *facets, boolT printall);
    +void    qh_printend4geom(qhT *qh, FILE *fp, facetT *facet, int *num, boolT printall);
    +void    qh_printextremes(qhT *qh, FILE *fp, facetT *facetlist, setT *facets, boolT printall);
    +void    qh_printextremes_2d(qhT *qh, FILE *fp, facetT *facetlist, setT *facets, boolT printall);
    +void    qh_printextremes_d(qhT *qh, FILE *fp, facetT *facetlist, setT *facets, boolT printall);
    +void    qh_printfacet(qhT *qh, FILE *fp, facetT *facet);
    +void    qh_printfacet2math(qhT *qh, FILE *fp, facetT *facet, qh_PRINT format, int notfirst);
    +void    qh_printfacet2geom(qhT *qh, FILE *fp, facetT *facet, realT color[3]);
    +void    qh_printfacet2geom_points(qhT *qh, FILE *fp, pointT *point1, pointT *point2,
    +                               facetT *facet, realT offset, realT color[3]);
    +void    qh_printfacet3math(qhT *qh, FILE *fp, facetT *facet, qh_PRINT format, int notfirst);
    +void    qh_printfacet3geom_nonsimplicial(qhT *qh, FILE *fp, facetT *facet, realT color[3]);
    +void    qh_printfacet3geom_points(qhT *qh, FILE *fp, setT *points, facetT *facet, realT offset, realT color[3]);
    +void    qh_printfacet3geom_simplicial(qhT *qh, FILE *fp, facetT *facet, realT color[3]);
    +void    qh_printfacet3vertex(qhT *qh, FILE *fp, facetT *facet, qh_PRINT format);
    +void    qh_printfacet4geom_nonsimplicial(qhT *qh, FILE *fp, facetT *facet, realT color[3]);
    +void    qh_printfacet4geom_simplicial(qhT *qh, FILE *fp, facetT *facet, realT color[3]);
    +void    qh_printfacetNvertex_nonsimplicial(qhT *qh, FILE *fp, facetT *facet, int id, qh_PRINT format);
    +void    qh_printfacetNvertex_simplicial(qhT *qh, FILE *fp, facetT *facet, qh_PRINT format);
    +void    qh_printfacetheader(qhT *qh, FILE *fp, facetT *facet);
    +void    qh_printfacetridges(qhT *qh, FILE *fp, facetT *facet);
    +void    qh_printfacets(qhT *qh, FILE *fp, qh_PRINT format, facetT *facetlist, setT *facets, boolT printall);
    +void    qh_printhyperplaneintersection(qhT *qh, FILE *fp, facetT *facet1, facetT *facet2,
    +                   setT *vertices, realT color[3]);
    +void    qh_printneighborhood(qhT *qh, FILE *fp, qh_PRINT format, facetT *facetA, facetT *facetB, boolT printall);
    +void    qh_printline3geom(qhT *qh, FILE *fp, pointT *pointA, pointT *pointB, realT color[3]);
    +void    qh_printpoint(qhT *qh, FILE *fp, const char *string, pointT *point);
    +void    qh_printpointid(qhT *qh, FILE *fp, const char *string, int dim, pointT *point, int id);
    +void    qh_printpoint3(qhT *qh, FILE *fp, pointT *point);
    +void    qh_printpoints_out(qhT *qh, FILE *fp, facetT *facetlist, setT *facets, boolT printall);
    +void    qh_printpointvect(qhT *qh, FILE *fp, pointT *point, coordT *normal, pointT *center, realT radius, realT color[3]);
    +void    qh_printpointvect2(qhT *qh, FILE *fp, pointT *point, coordT *normal, pointT *center, realT radius);
    +void    qh_printridge(qhT *qh, FILE *fp, ridgeT *ridge);
    +void    qh_printspheres(qhT *qh, FILE *fp, setT *vertices, realT radius);
    +void    qh_printvdiagram(qhT *qh, FILE *fp, qh_PRINT format, facetT *facetlist, setT *facets, boolT printall);
    +int     qh_printvdiagram2(qhT *qh, FILE *fp, printvridgeT printvridge, setT *vertices, qh_RIDGE innerouter, boolT inorder);
    +void    qh_printvertex(qhT *qh, FILE *fp, vertexT *vertex);
    +void    qh_printvertexlist(qhT *qh, FILE *fp, const char* string, facetT *facetlist,
    +                         setT *facets, boolT printall);
    +void    qh_printvertices(qhT *qh, FILE *fp, const char* string, setT *vertices);
    +void    qh_printvneighbors(qhT *qh, FILE *fp, facetT* facetlist, setT *facets, boolT printall);
    +void    qh_printvoronoi(qhT *qh, FILE *fp, qh_PRINT format, facetT *facetlist, setT *facets, boolT printall);
    +void    qh_printvnorm(qhT *qh, FILE *fp, vertexT *vertex, vertexT *vertexA, setT *centers, boolT unbounded);
    +void    qh_printvridge(qhT *qh, FILE *fp, vertexT *vertex, vertexT *vertexA, setT *centers, boolT unbounded);
    +void    qh_produce_output(qhT *qh);
    +void    qh_produce_output2(qhT *qh);
    +void    qh_projectdim3(qhT *qh, pointT *source, pointT *destination);
    +int     qh_readfeasible(qhT *qh, int dim, const char *curline);
    +coordT *qh_readpoints(qhT *qh, int *numpoints, int *dimension, boolT *ismalloc);
    +void    qh_setfeasible(qhT *qh, int dim);
    +boolT   qh_skipfacet(qhT *qh, facetT *facet);
    +char   *qh_skipfilename(qhT *qh, char *filename);
    +
    +#ifdef __cplusplus
    +} /* extern "C" */
    +#endif
    +
    +#endif /* qhDEFio */
    diff --git a/xs/src/qhull/src/libqhull_r/libqhull_r.c b/xs/src/qhull/src/libqhull_r/libqhull_r.c
    new file mode 100644
    index 000000000..0fe0c980d
    --- /dev/null
    +++ b/xs/src/qhull/src/libqhull_r/libqhull_r.c
    @@ -0,0 +1,1403 @@
    +/*
      ---------------------------------
    +
    +   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() =======*/
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  qh_printsummary(qh, fp )
    +    prints summary to fp
    +
    +  notes:
    +    not in io_r.c so that user_eg.c can prevent io_r.c from loading
    +    qh_printsummary and qh_countfacets must match counts
    +
    +  design:
    +    determine number of points, vertices, and coplanar points
    +    print summary
    +*/
    +void qh_printsummary(qhT *qh, FILE *fp) {
    +  realT ratio, outerplane, innerplane;
    +  float cpu;
    +  int size, id, nummerged, numvertices, numcoplanars= 0, nonsimplicial=0;
    +  int goodused;
    +  facetT *facet;
    +  const char *s;
    +  int numdel= zzval_(Zdelvertextot);
    +  int numtricoplanars= 0;
    +
    +  size= qh->num_points + qh_setsize(qh, qh->other_points);
    +  numvertices= qh->num_vertices - qh_setsize(qh, qh->del_vertices);
    +  id= qh_pointid(qh, qh->GOODpointp);
    +  FORALLfacets {
    +    if (facet->coplanarset)
    +      numcoplanars += qh_setsize(qh, facet->coplanarset);
    +    if (facet->good) {
    +      if (facet->simplicial) {
    +        if (facet->keepcentrum && facet->tricoplanar)
    +          numtricoplanars++;
    +      }else if (qh_setsize(qh, facet->vertices) != qh->hull_dim)
    +        nonsimplicial++;
    +    }
    +  }
    +  if (id >=0 && qh->STOPcone-1 != id && -qh->STOPpoint-1 != id)
    +    size--;
    +  if (qh->STOPcone || qh->STOPpoint)
    +      qh_fprintf(qh, fp, 9288, "\nAt a premature exit due to 'TVn', 'TCn', 'TRn', or precision error with 'QJn'.");
    +  if (qh->UPPERdelaunay)
    +    goodused= qh->GOODvertex + qh->GOODpoint + qh->SPLITthresholds;
    +  else if (qh->DELAUNAY)
    +    goodused= qh->GOODvertex + qh->GOODpoint + qh->GOODthreshold;
    +  else
    +    goodused= qh->num_good;
    +  nummerged= zzval_(Ztotmerge) - zzval_(Zcyclehorizon) + zzval_(Zcyclefacettot);
    +  if (qh->VORONOI) {
    +    if (qh->UPPERdelaunay)
    +      qh_fprintf(qh, fp, 9289, "\n\
    +Furthest-site Voronoi vertices by the convex hull of %d points in %d-d:\n\n", size, qh->hull_dim);
    +    else
    +      qh_fprintf(qh, fp, 9290, "\n\
    +Voronoi diagram by the convex hull of %d points in %d-d:\n\n", size, qh->hull_dim);
    +    qh_fprintf(qh, fp, 9291, "  Number of Voronoi regions%s: %d\n",
    +              qh->ATinfinity ? " and at-infinity" : "", numvertices);
    +    if (numdel)
    +      qh_fprintf(qh, fp, 9292, "  Total number of deleted points due to merging: %d\n", numdel);
    +    if (numcoplanars - numdel > 0)
    +      qh_fprintf(qh, fp, 9293, "  Number of nearly incident points: %d\n", numcoplanars - numdel);
    +    else if (size - numvertices - numdel > 0)
    +      qh_fprintf(qh, fp, 9294, "  Total number of nearly incident points: %d\n", size - numvertices - numdel);
    +    qh_fprintf(qh, fp, 9295, "  Number of%s Voronoi vertices: %d\n",
    +              goodused ? " 'good'" : "", qh->num_good);
    +    if (nonsimplicial)
    +      qh_fprintf(qh, fp, 9296, "  Number of%s non-simplicial Voronoi vertices: %d\n",
    +              goodused ? " 'good'" : "", nonsimplicial);
    +  }else if (qh->DELAUNAY) {
    +    if (qh->UPPERdelaunay)
    +      qh_fprintf(qh, fp, 9297, "\n\
    +Furthest-site Delaunay triangulation by the convex hull of %d points in %d-d:\n\n", size, qh->hull_dim);
    +    else
    +      qh_fprintf(qh, fp, 9298, "\n\
    +Delaunay triangulation by the convex hull of %d points in %d-d:\n\n", size, qh->hull_dim);
    +    qh_fprintf(qh, fp, 9299, "  Number of input sites%s: %d\n",
    +              qh->ATinfinity ? " and at-infinity" : "", numvertices);
    +    if (numdel)
    +      qh_fprintf(qh, fp, 9300, "  Total number of deleted points due to merging: %d\n", numdel);
    +    if (numcoplanars - numdel > 0)
    +      qh_fprintf(qh, fp, 9301, "  Number of nearly incident points: %d\n", numcoplanars - numdel);
    +    else if (size - numvertices - numdel > 0)
    +      qh_fprintf(qh, fp, 9302, "  Total number of nearly incident points: %d\n", size - numvertices - numdel);
    +    qh_fprintf(qh, fp, 9303, "  Number of%s Delaunay regions: %d\n",
    +              goodused ? " 'good'" : "", qh->num_good);
    +    if (nonsimplicial)
    +      qh_fprintf(qh, fp, 9304, "  Number of%s non-simplicial Delaunay regions: %d\n",
    +              goodused ? " 'good'" : "", nonsimplicial);
    +  }else if (qh->HALFspace) {
    +    qh_fprintf(qh, fp, 9305, "\n\
    +Halfspace intersection by the convex hull of %d points in %d-d:\n\n", size, qh->hull_dim);
    +    qh_fprintf(qh, fp, 9306, "  Number of halfspaces: %d\n", size);
    +    qh_fprintf(qh, fp, 9307, "  Number of non-redundant halfspaces: %d\n", numvertices);
    +    if (numcoplanars) {
    +      if (qh->KEEPinside && qh->KEEPcoplanar)
    +        s= "similar and redundant";
    +      else if (qh->KEEPinside)
    +        s= "redundant";
    +      else
    +        s= "similar";
    +      qh_fprintf(qh, fp, 9308, "  Number of %s halfspaces: %d\n", s, numcoplanars);
    +    }
    +    qh_fprintf(qh, fp, 9309, "  Number of intersection points: %d\n", qh->num_facets - qh->num_visible);
    +    if (goodused)
    +      qh_fprintf(qh, fp, 9310, "  Number of 'good' intersection points: %d\n", qh->num_good);
    +    if (nonsimplicial)
    +      qh_fprintf(qh, fp, 9311, "  Number of%s non-simplicial intersection points: %d\n",
    +              goodused ? " 'good'" : "", nonsimplicial);
    +  }else {
    +    qh_fprintf(qh, fp, 9312, "\n\
    +Convex hull of %d points in %d-d:\n\n", size, qh->hull_dim);
    +    qh_fprintf(qh, fp, 9313, "  Number of vertices: %d\n", numvertices);
    +    if (numcoplanars) {
    +      if (qh->KEEPinside && qh->KEEPcoplanar)
    +        s= "coplanar and interior";
    +      else if (qh->KEEPinside)
    +        s= "interior";
    +      else
    +        s= "coplanar";
    +      qh_fprintf(qh, fp, 9314, "  Number of %s points: %d\n", s, numcoplanars);
    +    }
    +    qh_fprintf(qh, fp, 9315, "  Number of facets: %d\n", qh->num_facets - qh->num_visible);
    +    if (goodused)
    +      qh_fprintf(qh, fp, 9316, "  Number of 'good' facets: %d\n", qh->num_good);
    +    if (nonsimplicial)
    +      qh_fprintf(qh, fp, 9317, "  Number of%s non-simplicial facets: %d\n",
    +              goodused ? " 'good'" : "", nonsimplicial);
    +  }
    +  if (numtricoplanars)
    +      qh_fprintf(qh, fp, 9318, "  Number of triangulated facets: %d\n", numtricoplanars);
    +  qh_fprintf(qh, fp, 9319, "\nStatistics for: %s | %s",
    +                      qh->rbox_command, qh->qhull_command);
    +  if (qh->ROTATErandom != INT_MIN)
    +    qh_fprintf(qh, fp, 9320, " QR%d\n\n", qh->ROTATErandom);
    +  else
    +    qh_fprintf(qh, fp, 9321, "\n\n");
    +  qh_fprintf(qh, fp, 9322, "  Number of points processed: %d\n", zzval_(Zprocessed));
    +  qh_fprintf(qh, fp, 9323, "  Number of hyperplanes created: %d\n", zzval_(Zsetplane));
    +  if (qh->DELAUNAY)
    +    qh_fprintf(qh, fp, 9324, "  Number of facets in hull: %d\n", qh->num_facets - qh->num_visible);
    +  qh_fprintf(qh, fp, 9325, "  Number of distance tests for qhull: %d\n", zzval_(Zpartition)+
    +      zzval_(Zpartitionall)+zzval_(Znumvisibility)+zzval_(Zpartcoplanar));
    +#if 0  /* NOTE: must print before printstatistics() */
    +  {realT stddev, ave;
    +  qh_fprintf(qh, fp, 9326, "  average new facet balance: %2.2g\n",
    +          wval_(Wnewbalance)/zval_(Zprocessed));
    +  stddev= qh_stddev(zval_(Zprocessed), wval_(Wnewbalance),
    +                                 wval_(Wnewbalance2), &ave);
    +  qh_fprintf(qh, fp, 9327, "  new facet standard deviation: %2.2g\n", stddev);
    +  qh_fprintf(qh, fp, 9328, "  average partition balance: %2.2g\n",
    +          wval_(Wpbalance)/zval_(Zpbalance));
    +  stddev= qh_stddev(zval_(Zpbalance), wval_(Wpbalance),
    +                                 wval_(Wpbalance2), &ave);
    +  qh_fprintf(qh, fp, 9329, "  partition standard deviation: %2.2g\n", stddev);
    +  }
    +#endif
    +  if (nummerged) {
    +    qh_fprintf(qh, fp, 9330,"  Number of distance tests for merging: %d\n",zzval_(Zbestdist)+
    +          zzval_(Zcentrumtests)+zzval_(Zdistconvex)+zzval_(Zdistcheck)+
    +          zzval_(Zdistzero));
    +    qh_fprintf(qh, fp, 9331,"  Number of distance tests for checking: %d\n",zzval_(Zcheckpart));
    +    qh_fprintf(qh, fp, 9332,"  Number of merged facets: %d\n", nummerged);
    +  }
    +  if (!qh->RANDOMoutside && qh->QHULLfinished) {
    +    cpu= (float)qh->hulltime;
    +    cpu /= (float)qh_SECticks;
    +    wval_(Wcpu)= cpu;
    +    qh_fprintf(qh, fp, 9333, "  CPU seconds to compute hull (after input): %2.4g\n", cpu);
    +  }
    +  if (qh->RERUN) {
    +    if (!qh->PREmerge && !qh->MERGEexact)
    +      qh_fprintf(qh, fp, 9334, "  Percentage of runs with precision errors: %4.1f\n",
    +           zzval_(Zretry)*100.0/qh->build_cnt);  /* careful of order */
    +  }else if (qh->JOGGLEmax < REALmax/2) {
    +    if (zzval_(Zretry))
    +      qh_fprintf(qh, fp, 9335, "  After %d retries, input joggled by: %2.2g\n",
    +         zzval_(Zretry), qh->JOGGLEmax);
    +    else
    +      qh_fprintf(qh, fp, 9336, "  Input joggled by: %2.2g\n", qh->JOGGLEmax);
    +  }
    +  if (qh->totarea != 0.0)
    +    qh_fprintf(qh, fp, 9337, "  %s facet area:   %2.8g\n",
    +            zzval_(Ztotmerge) ? "Approximate" : "Total", qh->totarea);
    +  if (qh->totvol != 0.0)
    +    qh_fprintf(qh, fp, 9338, "  %s volume:       %2.8g\n",
    +            zzval_(Ztotmerge) ? "Approximate" : "Total", qh->totvol);
    +  if (qh->MERGING) {
    +    qh_outerinner(qh, NULL, &outerplane, &innerplane);
    +    if (outerplane > 2 * qh->DISTround) {
    +      qh_fprintf(qh, fp, 9339, "  Maximum distance of %spoint above facet: %2.2g",
    +            (qh->QHULLfinished ? "" : "merged "), outerplane);
    +      ratio= outerplane/(qh->ONEmerge + qh->DISTround);
    +      /* don't report ratio if MINoutside is large */
    +      if (ratio > 0.05 && 2* qh->ONEmerge > qh->MINoutside && qh->JOGGLEmax > REALmax/2)
    +        qh_fprintf(qh, fp, 9340, " (%.1fx)\n", ratio);
    +      else
    +        qh_fprintf(qh, fp, 9341, "\n");
    +    }
    +    if (innerplane < -2 * qh->DISTround) {
    +      qh_fprintf(qh, fp, 9342, "  Maximum distance of %svertex below facet: %2.2g",
    +            (qh->QHULLfinished ? "" : "merged "), innerplane);
    +      ratio= -innerplane/(qh->ONEmerge+qh->DISTround);
    +      if (ratio > 0.05 && qh->JOGGLEmax > REALmax/2)
    +        qh_fprintf(qh, fp, 9343, " (%.1fx)\n", ratio);
    +      else
    +        qh_fprintf(qh, fp, 9344, "\n");
    +    }
    +  }
    +  qh_fprintf(qh, fp, 9345, "\n");
    +} /* printsummary */
    +
    +
    diff --git a/xs/src/qhull/src/libqhull_r/libqhull_r.h b/xs/src/qhull/src/libqhull_r/libqhull_r.h
    new file mode 100644
    index 000000000..363e6da6a
    --- /dev/null
    +++ b/xs/src/qhull/src/libqhull_r/libqhull_r.h
    @@ -0,0 +1,1134 @@
    +/*
      ---------------------------------
    +
    +   libqhull_r.h
    +   user-level header file for using qhull.a library
    +
    +   see qh-qhull_r.htm, qhull_ra.h
    +
    +   Copyright (c) 1993-2015 The Geometry Center.
    +   $Id: //main/2015/qhull/src/libqhull_r/libqhull_r.h#8 $$Change: 2079 $
    +   $DateTime: 2016/02/07 17:43:34 $$Author: bbarber $
    +
    +   includes function prototypes for libqhull_r.c, geom_r.c, global_r.c, io_r.c, user.c
    +
    +   use mem_r.h for mem_r.c
    +   use qset_r.h for qset_r.c
    +
    +   see unix_r.c for an example of using libqhull_r.h
    +
    +   recompile qhull if you change this file
    +*/
    +
    +#ifndef qhDEFlibqhull
    +#define qhDEFlibqhull 1
    +
    +/*=========================== -included files ==============*/
    +
    +/* user_r.h first for QHULL_CRTDBG */
    +#include "user_r.h"      /* user definable constants (e.g., realT). */
    +
    +#include "mem_r.h"   /* Needed for qhT in libqhull_r.h */
    +#include "qset_r.h"   /* Needed for QHULL_LIB_CHECK */
    +/* include stat_r.h after defining boolT.  statT needed for qhT in libqhull_r.h */
    +
    +#include 
    +#include 
    +#include 
    +#include 
    +
    +#ifndef __STDC__
    +#ifndef __cplusplus
    +#if     !_MSC_VER
    +#error  Neither __STDC__ nor __cplusplus is defined.  Please use strict ANSI C or C++ to compile
    +#error  Qhull.  You may need to turn off compiler extensions in your project configuration.  If
    +#error  your compiler is a standard C compiler, you can delete this warning from libqhull_r.h
    +#endif
    +#endif
    +#endif
    +
    +/*============ constants and basic types ====================*/
    +
    +extern const char qh_version[]; /* defined in global_r.c */
    +extern const char qh_version2[]; /* defined in global_r.c */
    +
    +/*----------------------------------
    +
    +  coordT
    +    coordinates and coefficients are stored as realT (i.e., double)
    +
    +  notes:
    +    Qhull works well if realT is 'float'.  If so joggle (QJ) is not effective.
    +
    +    Could use 'float' for data and 'double' for calculations (realT vs. coordT)
    +      This requires many type casts, and adjusted error bounds.
    +      Also C compilers may do expressions in double anyway.
    +*/
    +#define coordT realT
    +
    +/*----------------------------------
    +
    +  pointT
    +    a point is an array of coordinates, usually qh.hull_dim
    +    qh_pointid returns
    +      qh_IDnone if point==0 or qh is undefined
    +      qh_IDinterior for qh.interior_point
    +      qh_IDunknown if point is neither in qh.first_point... nor qh.other_points
    +
    +  notes:
    +    qh.STOPcone and qh.STOPpoint assume that qh_IDunknown==-1 (other negative numbers indicate points)
    +    qh_IDunknown is also returned by getid_() for unknown facet, ridge, or vertex
    +*/
    +#define pointT coordT
    +typedef enum
    +{
    +    qh_IDnone = -3, qh_IDinterior = -2, qh_IDunknown = -1
    +}
    +qh_pointT;
    +
    +/*----------------------------------
    +
    +  flagT
    +    Boolean flag as a bit
    +*/
    +#define flagT unsigned int
    +
    +/*----------------------------------
    +
    +  boolT
    +    boolean value, either True or False
    +
    +  notes:
    +    needed for portability
    +    Use qh_False/qh_True as synonyms
    +*/
    +#define boolT unsigned int
    +#ifdef False
    +#undef False
    +#endif
    +#ifdef True
    +#undef True
    +#endif
    +#define False 0
    +#define True 1
    +#define qh_False 0
    +#define qh_True 1
    +
    +#include "stat_r.h"  /* needs boolT */
    +
    +/*----------------------------------
    +
    +  qh_CENTER
    +    to distinguish facet->center
    +*/
    +typedef enum
    +{
    +    qh_ASnone = 0,   /* If not MERGING and not VORONOI */
    +    qh_ASvoronoi,    /* Set by qh_clearcenters on qh_prepare_output, or if not MERGING and VORONOI */
    +    qh_AScentrum     /* If MERGING (assumed during merging) */
    +}
    +qh_CENTER;
    +
    +/*----------------------------------
    +
    +  qh_PRINT
    +    output formats for printing (qh.PRINTout).
    +    'Fa' 'FV' 'Fc' 'FC'
    +
    +
    +   notes:
    +   some of these names are similar to qhT names.  The similar names are only
    +   used in switch statements in qh_printbegin() etc.
    +*/
    +typedef enum {qh_PRINTnone= 0,
    +  qh_PRINTarea, qh_PRINTaverage,           /* 'Fa' 'FV' 'Fc' 'FC' */
    +  qh_PRINTcoplanars, qh_PRINTcentrums,
    +  qh_PRINTfacets, qh_PRINTfacets_xridge,   /* 'f' 'FF' 'G' 'FI' 'Fi' 'Fn' */
    +  qh_PRINTgeom, qh_PRINTids, qh_PRINTinner, qh_PRINTneighbors,
    +  qh_PRINTnormals, qh_PRINTouter, qh_PRINTmaple, /* 'n' 'Fo' 'i' 'm' 'Fm' 'FM', 'o' */
    +  qh_PRINTincidences, qh_PRINTmathematica, qh_PRINTmerges, qh_PRINToff,
    +  qh_PRINToptions, qh_PRINTpointintersect, /* 'FO' 'Fp' 'FP' 'p' 'FQ' 'FS' */
    +  qh_PRINTpointnearest, qh_PRINTpoints, qh_PRINTqhull, qh_PRINTsize,
    +  qh_PRINTsummary, qh_PRINTtriangles,      /* 'Fs' 'Ft' 'Fv' 'FN' 'Fx' */
    +  qh_PRINTvertices, qh_PRINTvneighbors, qh_PRINTextremes,
    +  qh_PRINTEND} qh_PRINT;
    +
    +/*----------------------------------
    +
    +  qh_ALL
    +    argument flag for selecting everything
    +*/
    +#define qh_ALL      True
    +#define qh_NOupper  True     /* argument for qh_findbest */
    +#define qh_IScheckmax  True     /* argument for qh_findbesthorizon */
    +#define qh_ISnewfacets  True     /* argument for qh_findbest */
    +#define qh_RESETvisible  True     /* argument for qh_resetlists */
    +
    +/*----------------------------------
    +
    +  qh_ERR
    +    Qhull exit codes, for indicating errors
    +    See: MSG_ERROR and MSG_WARNING [user.h]
    +*/
    +#define qh_ERRnone  0    /* no error occurred during qhull */
    +#define qh_ERRinput 1    /* input inconsistency */
    +#define qh_ERRsingular 2 /* singular input data */
    +#define qh_ERRprec  3    /* precision error */
    +#define qh_ERRmem   4    /* insufficient memory, matches mem_r.h */
    +#define qh_ERRqhull 5    /* internal error detected, matches mem_r.h */
    +
    +/*----------------------------------
    +
    +qh_FILEstderr
    +Fake stderr to distinguish error output from normal output
    +For C++ interface.  Must redefine qh_fprintf_qhull
    +*/
    +#define qh_FILEstderr ((FILE*)1)
    +
    +/* ============ -structures- ====================
    +   each of the following structures is defined by a typedef
    +   all realT and coordT fields occur at the beginning of a structure
    +        (otherwise space may be wasted due to alignment)
    +   define all flags together and pack into 32-bit number
    +
    +   DEFqhT and DEFsetT are likewise defined in
    +   mem_r.h, qset_r.h, and stat_r.h.
    +
    +*/
    +
    +typedef struct vertexT vertexT;
    +typedef struct ridgeT ridgeT;
    +typedef struct facetT facetT;
    +
    +#ifndef DEFqhT
    +#define DEFqhT 1
    +typedef struct qhT qhT;          /* defined below */
    +#endif
    +
    +#ifndef DEFsetT
    +#define DEFsetT 1
    +typedef struct setT setT;          /* defined in qset_r.h */
    +#endif
    +
    +/*----------------------------------
    +
    +  facetT
    +    defines a facet
    +
    +  notes:
    +   qhull() generates the hull as a list of facets.
    +
    +  topological information:
    +    f.previous,next     doubly-linked list of facets
    +    f.vertices          set of vertices
    +    f.ridges            set of ridges
    +    f.neighbors         set of neighbors
    +    f.toporient         True if facet has top-orientation (else bottom)
    +
    +  geometric information:
    +    f.offset,normal     hyperplane equation
    +    f.maxoutside        offset to outer plane -- all points inside
    +    f.center            centrum for testing convexity or Voronoi center for output
    +    f.simplicial        True if facet is simplicial
    +    f.flipped           True if facet does not include qh.interior_point
    +
    +  for constructing hull:
    +    f.visible           True if facet on list of visible facets (will be deleted)
    +    f.newfacet          True if facet on list of newly created facets
    +    f.coplanarset       set of points coplanar with this facet
    +                        (includes near-inside points for later testing)
    +    f.outsideset        set of points outside of this facet
    +    f.furthestdist      distance to furthest point of outside set
    +    f.visitid           marks visited facets during a loop
    +    f.replace           replacement facet for to-be-deleted, visible facets
    +    f.samecycle,newcycle cycle of facets for merging into horizon facet
    +
    +  see below for other flags and fields
    +*/
    +struct facetT {
    +#if !qh_COMPUTEfurthest
    +  coordT   furthestdist;/* distance to furthest point of outsideset */
    +#endif
    +#if qh_MAXoutside
    +  coordT   maxoutside;  /* max computed distance of point to facet
    +                        Before QHULLfinished this is an approximation
    +                        since maxdist not always set for mergefacet
    +                        Actual outer plane is +DISTround and
    +                        computed outer plane is +2*DISTround */
    +#endif
    +  coordT   offset;      /* exact offset of hyperplane from origin */
    +  coordT  *normal;      /* normal of hyperplane, hull_dim coefficients */
    +                        /*   if ->tricoplanar, shared with a neighbor */
    +  union {               /* in order of testing */
    +   realT   area;        /* area of facet, only in io_r.c if  ->isarea */
    +   facetT *replace;     /*  replacement facet if ->visible and NEWfacets
    +                             is NULL only if qh_mergedegen_redundant or interior */
    +   facetT *samecycle;   /*  cycle of facets from the same visible/horizon intersection,
    +                             if ->newfacet */
    +   facetT *newcycle;    /*  in horizon facet, current samecycle of new facets */
    +   facetT *trivisible;  /* visible facet for ->tricoplanar facets during qh_triangulate() */
    +   facetT *triowner;    /* owner facet for ->tricoplanar, !isarea facets w/ ->keepcentrum */
    +  }f;
    +  coordT  *center;      /* set according to qh.CENTERtype */
    +                        /*   qh_ASnone:    no center (not MERGING) */
    +                        /*   qh_AScentrum: centrum for testing convexity (qh_getcentrum) */
    +                        /*                 assumed qh_AScentrum while merging */
    +                        /*   qh_ASvoronoi: Voronoi center (qh_facetcenter) */
    +                        /* after constructing the hull, it may be changed (qh_clearcenter) */
    +                        /* if tricoplanar and !keepcentrum, shared with a neighbor */
    +  facetT  *previous;    /* previous facet in the facet_list */
    +  facetT  *next;        /* next facet in the facet_list */
    +  setT    *vertices;    /* vertices for this facet, inverse sorted by ID
    +                           if simplicial, 1st vertex was apex/furthest */
    +  setT    *ridges;      /* explicit ridges for nonsimplicial facets.
    +                           for simplicial facets, neighbors define the ridges */
    +  setT    *neighbors;   /* neighbors of the facet.  If simplicial, the kth
    +                           neighbor is opposite the kth vertex, and the first
    +                           neighbor is the horizon facet for the first vertex*/
    +  setT    *outsideset;  /* set of points outside this facet
    +                           if non-empty, last point is furthest
    +                           if NARROWhull, includes coplanars for partitioning*/
    +  setT    *coplanarset; /* set of points coplanar with this facet
    +                           > qh.min_vertex and <= facet->max_outside
    +                           a point is assigned to the furthest facet
    +                           if non-empty, last point is furthest away */
    +  unsigned visitid;     /* visit_id, for visiting all neighbors,
    +                           all uses are independent */
    +  unsigned id;          /* unique identifier from qh.facet_id */
    +  unsigned nummerge:9;  /* number of merges */
    +#define qh_MAXnummerge 511 /*     2^9-1, 32 flags total, see "flags:" in io_r.c */
    +  flagT    tricoplanar:1; /* True if TRIangulate and simplicial and coplanar with a neighbor */
    +                          /*   all tricoplanars share the same apex */
    +                          /*   all tricoplanars share the same ->center, ->normal, ->offset, ->maxoutside */
    +                          /*     ->keepcentrum is true for the owner.  It has the ->coplanareset */
    +                          /*   if ->degenerate, does not span facet (one logical ridge) */
    +                          /*   during qh_triangulate, f.trivisible points to original facet */
    +  flagT    newfacet:1;  /* True if facet on qh.newfacet_list (new or merged) */
    +  flagT    visible:1;   /* True if visible facet (will be deleted) */
    +  flagT    toporient:1; /* True if created with top orientation
    +                           after merging, use ridge orientation */
    +  flagT    simplicial:1;/* True if simplicial facet, ->ridges may be implicit */
    +  flagT    seen:1;      /* used to perform operations only once, like visitid */
    +  flagT    seen2:1;     /* used to perform operations only once, like visitid */
    +  flagT    flipped:1;   /* True if facet is flipped */
    +  flagT    upperdelaunay:1; /* True if facet is upper envelope of Delaunay triangulation */
    +  flagT    notfurthest:1; /* True if last point of outsideset is not furthest*/
    +
    +/*-------- flags primarily for output ---------*/
    +  flagT    good:1;      /* True if a facet marked good for output */
    +  flagT    isarea:1;    /* True if facet->f.area is defined */
    +
    +/*-------- flags for merging ------------------*/
    +  flagT    dupridge:1;  /* True if duplicate ridge in facet */
    +  flagT    mergeridge:1; /* True if facet or neighbor contains a qh_MERGEridge
    +                            ->normal defined (also defined for mergeridge2) */
    +  flagT    mergeridge2:1; /* True if neighbor contains a qh_MERGEridge (qhT *qh, mark_dupridges */
    +  flagT    coplanar:1;  /* True if horizon facet is coplanar at last use */
    +  flagT     mergehorizon:1; /* True if will merge into horizon (->coplanar) */
    +  flagT     cycledone:1;/* True if mergecycle_all already done */
    +  flagT    tested:1;    /* True if facet convexity has been tested (false after merge */
    +  flagT    keepcentrum:1; /* True if keep old centrum after a merge, or marks owner for ->tricoplanar */
    +  flagT    newmerge:1;  /* True if facet is newly merged for reducevertices */
    +  flagT    degenerate:1; /* True if facet is degenerate (degen_mergeset or ->tricoplanar) */
    +  flagT    redundant:1;  /* True if facet is redundant (degen_mergeset) */
    +};
    +
    +
    +/*----------------------------------
    +
    +  ridgeT
    +    defines a ridge
    +
    +  notes:
    +  a ridge is hull_dim-1 simplex between two neighboring facets.  If the
    +  facets are non-simplicial, there may be more than one ridge between
    +  two facets.  E.G. a 4-d hypercube has two triangles between each pair
    +  of neighboring facets.
    +
    +  topological information:
    +    vertices            a set of vertices
    +    top,bottom          neighboring facets with orientation
    +
    +  geometric information:
    +    tested              True if ridge is clearly convex
    +    nonconvex           True if ridge is non-convex
    +*/
    +struct ridgeT {
    +  setT    *vertices;    /* vertices belonging to this ridge, inverse sorted by ID
    +                           NULL if a degen ridge (matchsame) */
    +  facetT  *top;         /* top facet this ridge is part of */
    +  facetT  *bottom;      /* bottom facet this ridge is part of */
    +  unsigned id;          /* unique identifier.  Same size as vertex_id and ridge_id */
    +  flagT    seen:1;      /* used to perform operations only once */
    +  flagT    tested:1;    /* True when ridge is tested for convexity */
    +  flagT    nonconvex:1; /* True if getmergeset detected a non-convex neighbor
    +                           only one ridge between neighbors may have nonconvex */
    +};
    +
    +/*----------------------------------
    +
    +  vertexT
    +     defines a vertex
    +
    +  topological information:
    +    next,previous       doubly-linked list of all vertices
    +    neighbors           set of adjacent facets (only if qh.VERTEXneighbors)
    +
    +  geometric information:
    +    point               array of DIM3 coordinates
    +*/
    +struct vertexT {
    +  vertexT *next;        /* next vertex in vertex_list */
    +  vertexT *previous;    /* previous vertex in vertex_list */
    +  pointT  *point;       /* hull_dim coordinates (coordT) */
    +  setT    *neighbors;   /* neighboring facets of vertex, qh_vertexneighbors()
    +                           inits in io_r.c or after first merge */
    +  unsigned id;          /* unique identifier.  Same size as qh.vertex_id and qh.ridge_id */
    +  unsigned visitid;    /* for use with qh.vertex_visit, size must match */
    +  flagT    seen:1;      /* used to perform operations only once */
    +  flagT    seen2:1;     /* another seen flag */
    +  flagT    delridge:1;  /* vertex was part of a deleted ridge */
    +  flagT    deleted:1;   /* true if vertex on qh.del_vertices */
    +  flagT    newlist:1;   /* true if vertex on qh.newvertex_list */
    +};
    +
    +/*======= -global variables -qh ============================*/
    +
    +/*----------------------------------
    +
    +  qhT
    +   All global variables for qhull are in qhT.  It includes qhmemT, qhstatT, and rbox globals
    +
    +   This version of Qhull is reentrant, but it is not thread-safe.
    +
    +   Do not run separate threads on the same instance of qhT.
    +
    +   QHULL_LIB_CHECK checks that a program and the corresponding
    +   qhull library were built with the same type of header files.
    +*/
    +
    +#define QHULL_NON_REENTRANT 0
    +#define QHULL_QH_POINTER 1
    +#define QHULL_REENTRANT 2
    +
    +#define QHULL_LIB_TYPE QHULL_REENTRANT
    +
    +#define QHULL_LIB_CHECK qh_lib_check(QHULL_LIB_TYPE, sizeof(qhT), sizeof(vertexT), sizeof(ridgeT), sizeof(facetT), sizeof(setT), sizeof(qhmemT));
    +#define QHULL_LIB_CHECK_RBOX qh_lib_check(QHULL_LIB_TYPE, sizeof(qhT), sizeof(vertexT), sizeof(ridgeT), sizeof(facetT), 0, 0);
    +
    +struct qhT {
    +
    +/*----------------------------------
    +
    +  qh constants
    +    configuration flags and constants for Qhull
    +
    +  notes:
    +    The user configures Qhull by defining flags.  They are
    +    copied into qh by qh_setflags().  qh-quick_r.htm#options defines the flags.
    +*/
    +  boolT ALLpoints;        /* true 'Qs' if search all points for initial simplex */
    +  boolT ANGLEmerge;       /* true 'Qa' if sort potential merges by angle */
    +  boolT APPROXhull;       /* true 'Wn' if MINoutside set */
    +  realT   MINoutside;     /*   'Wn' min. distance for an outside point */
    +  boolT ANNOTATEoutput;   /* true 'Ta' if annotate output with message codes */
    +  boolT ATinfinity;       /* true 'Qz' if point num_points-1 is "at-infinity"
    +                             for improving precision in Delaunay triangulations */
    +  boolT AVOIDold;         /* true 'Q4' if avoid old->new merges */
    +  boolT BESToutside;      /* true 'Qf' if partition points into best outsideset */
    +  boolT CDDinput;         /* true 'Pc' if input uses CDD format (1.0/offset first) */
    +  boolT CDDoutput;        /* true 'PC' if print normals in CDD format (offset first) */
    +  boolT CHECKfrequently;  /* true 'Tc' if checking frequently */
    +  realT premerge_cos;     /*   'A-n'   cos_max when pre merging */
    +  realT postmerge_cos;    /*   'An'    cos_max when post merging */
    +  boolT DELAUNAY;         /* true 'd' if computing DELAUNAY triangulation */
    +  boolT DOintersections;  /* true 'Gh' if print hyperplane intersections */
    +  int   DROPdim;          /* drops dim 'GDn' for 4-d -> 3-d output */
    +  boolT FORCEoutput;      /* true 'Po' if forcing output despite degeneracies */
    +  int   GOODpoint;        /* 1+n for 'QGn', good facet if visible/not(-) from point n*/
    +  pointT *GOODpointp;     /*   the actual point */
    +  boolT GOODthreshold;    /* true if qh.lower_threshold/upper_threshold defined
    +                             false if qh.SPLITthreshold */
    +  int   GOODvertex;       /* 1+n, good facet if vertex for point n */
    +  pointT *GOODvertexp;     /*   the actual point */
    +  boolT HALFspace;        /* true 'Hn,n,n' if halfspace intersection */
    +  boolT ISqhullQh;        /* Set by Qhull.cpp on initialization */
    +  int   IStracing;        /* trace execution, 0=none, 1=least, 4=most, -1=events */
    +  int   KEEParea;         /* 'PAn' number of largest facets to keep */
    +  boolT KEEPcoplanar;     /* true 'Qc' if keeping nearest facet for coplanar points */
    +  boolT KEEPinside;       /* true 'Qi' if keeping nearest facet for inside points
    +                              set automatically if 'd Qc' */
    +  int   KEEPmerge;        /* 'PMn' number of facets to keep with most merges */
    +  realT KEEPminArea;      /* 'PFn' minimum facet area to keep */
    +  realT MAXcoplanar;      /* 'Un' max distance below a facet to be coplanar*/
    +  boolT MERGEexact;       /* true 'Qx' if exact merges (coplanar, degen, dupridge, flipped) */
    +  boolT MERGEindependent; /* true 'Q2' if merging independent sets */
    +  boolT MERGING;          /* true if exact-, pre- or post-merging, with angle and centrum tests */
    +  realT   premerge_centrum;  /*   'C-n' centrum_radius when pre merging.  Default is round-off */
    +  realT   postmerge_centrum; /*   'Cn' centrum_radius when post merging.  Default is round-off */
    +  boolT MERGEvertices;    /* true 'Q3' if merging redundant vertices */
    +  realT MINvisible;       /* 'Vn' min. distance for a facet to be visible */
    +  boolT NOnarrow;         /* true 'Q10' if no special processing for narrow distributions */
    +  boolT NOnearinside;     /* true 'Q8' if ignore near-inside points when partitioning */
    +  boolT NOpremerge;       /* true 'Q0' if no defaults for C-0 or Qx */
    +  boolT NOwide;           /* true 'Q12' if no error on wide merge due to duplicate ridge */
    +  boolT ONLYgood;         /* true 'Qg' if process points with good visible or horizon facets */
    +  boolT ONLYmax;          /* true 'Qm' if only process points that increase max_outside */
    +  boolT PICKfurthest;     /* true 'Q9' if process furthest of furthest points*/
    +  boolT POSTmerge;        /* true if merging after buildhull (Cn or An) */
    +  boolT PREmerge;         /* true if merging during buildhull (C-n or A-n) */
    +                        /* NOTE: some of these names are similar to qh_PRINT names */
    +  boolT PRINTcentrums;    /* true 'Gc' if printing centrums */
    +  boolT PRINTcoplanar;    /* true 'Gp' if printing coplanar points */
    +  int   PRINTdim;         /* print dimension for Geomview output */
    +  boolT PRINTdots;        /* true 'Ga' if printing all points as dots */
    +  boolT PRINTgood;        /* true 'Pg' if printing good facets */
    +  boolT PRINTinner;       /* true 'Gi' if printing inner planes */
    +  boolT PRINTneighbors;   /* true 'PG' if printing neighbors of good facets */
    +  boolT PRINTnoplanes;    /* true 'Gn' if printing no planes */
    +  boolT PRINToptions1st;  /* true 'FO' if printing options to stderr */
    +  boolT PRINTouter;       /* true 'Go' if printing outer planes */
    +  boolT PRINTprecision;   /* false 'Pp' if not reporting precision problems */
    +  qh_PRINT PRINTout[qh_PRINTEND]; /* list of output formats to print */
    +  boolT PRINTridges;      /* true 'Gr' if print ridges */
    +  boolT PRINTspheres;     /* true 'Gv' if print vertices as spheres */
    +  boolT PRINTstatistics;  /* true 'Ts' if printing statistics to stderr */
    +  boolT PRINTsummary;     /* true 's' if printing summary to stderr */
    +  boolT PRINTtransparent; /* true 'Gt' if print transparent outer ridges */
    +  boolT PROJECTdelaunay;  /* true if DELAUNAY, no readpoints() and
    +                             need projectinput() for Delaunay in qh_init_B */
    +  int   PROJECTinput;     /* number of projected dimensions 'bn:0Bn:0' */
    +  boolT QUICKhelp;        /* true if quick help message for degen input */
    +  boolT RANDOMdist;       /* true if randomly change distplane and setfacetplane */
    +  realT RANDOMfactor;     /*    maximum random perturbation */
    +  realT RANDOMa;          /*    qh_randomfactor is randr * RANDOMa + RANDOMb */
    +  realT RANDOMb;
    +  boolT RANDOMoutside;    /* true if select a random outside point */
    +  int   REPORTfreq;       /* buildtracing reports every n facets */
    +  int   REPORTfreq2;      /* tracemerging reports every REPORTfreq/2 facets */
    +  int   RERUN;            /* 'TRn' rerun qhull n times (qh.build_cnt) */
    +  int   ROTATErandom;     /* 'QRn' seed, 0 time, >= rotate input */
    +  boolT SCALEinput;       /* true 'Qbk' if scaling input */
    +  boolT SCALElast;        /* true 'Qbb' if scale last coord to max prev coord */
    +  boolT SETroundoff;      /* true 'E' if qh.DISTround is predefined */
    +  boolT SKIPcheckmax;     /* true 'Q5' if skip qh_check_maxout */
    +  boolT SKIPconvex;       /* true 'Q6' if skip convexity testing during pre-merge */
    +  boolT SPLITthresholds;  /* true if upper_/lower_threshold defines a region
    +                               used only for printing (!for qh.ONLYgood) */
    +  int   STOPcone;         /* 'TCn' 1+n for stopping after cone for point n */
    +                          /*       also used by qh_build_withresart for err exit*/
    +  int   STOPpoint;        /* 'TVn' 'TV-n' 1+n for stopping after/before(-)
    +                                        adding point n */
    +  int   TESTpoints;       /* 'QTn' num of test points after qh.num_points.  Test points always coplanar. */
    +  boolT TESTvneighbors;   /*  true 'Qv' if test vertex neighbors at end */
    +  int   TRACElevel;       /* 'Tn' conditional IStracing level */
    +  int   TRACElastrun;     /*  qh.TRACElevel applies to last qh.RERUN */
    +  int   TRACEpoint;       /* 'TPn' start tracing when point n is a vertex */
    +  realT TRACEdist;        /* 'TWn' start tracing when merge distance too big */
    +  int   TRACEmerge;       /* 'TMn' start tracing before this merge */
    +  boolT TRIangulate;      /* true 'Qt' if triangulate non-simplicial facets */
    +  boolT TRInormals;       /* true 'Q11' if triangulate duplicates ->normal and ->center (sets Qt) */
    +  boolT UPPERdelaunay;    /* true 'Qu' if computing furthest-site Delaunay */
    +  boolT USEstdout;        /* true 'Tz' if using stdout instead of stderr */
    +  boolT VERIFYoutput;     /* true 'Tv' if verify output at end of qhull */
    +  boolT VIRTUALmemory;    /* true 'Q7' if depth-first processing in buildhull */
    +  boolT VORONOI;          /* true 'v' if computing Voronoi diagram */
    +
    +  /*--------input constants ---------*/
    +  realT AREAfactor;       /* 1/(hull_dim-1)! for converting det's to area */
    +  boolT DOcheckmax;       /* true if calling qh_check_maxout (qhT *qh, qh_initqhull_globals) */
    +  char  *feasible_string;  /* feasible point 'Hn,n,n' for halfspace intersection */
    +  coordT *feasible_point;  /*    as coordinates, both malloc'd */
    +  boolT GETarea;          /* true 'Fa', 'FA', 'FS', 'PAn', 'PFn' if compute facet area/Voronoi volume in io_r.c */
    +  boolT KEEPnearinside;   /* true if near-inside points in coplanarset */
    +  int   hull_dim;         /* dimension of hull, set by initbuffers */
    +  int   input_dim;        /* dimension of input, set by initbuffers */
    +  int   num_points;       /* number of input points */
    +  pointT *first_point;    /* array of input points, see POINTSmalloc */
    +  boolT POINTSmalloc;     /*   true if qh.first_point/num_points allocated */
    +  pointT *input_points;   /* copy of original qh.first_point for input points for qh_joggleinput */
    +  boolT input_malloc;     /* true if qh.input_points malloc'd */
    +  char  qhull_command[256];/* command line that invoked this program */
    +  int   qhull_commandsiz2; /*    size of qhull_command at qh_clear_outputflags */
    +  char  rbox_command[256]; /* command line that produced the input points */
    +  char  qhull_options[512];/* descriptive list of options */
    +  int   qhull_optionlen;  /*    length of last line */
    +  int   qhull_optionsiz;  /*    size of qhull_options at qh_build_withrestart */
    +  int   qhull_optionsiz2; /*    size of qhull_options at qh_clear_outputflags */
    +  int   run_id;           /* non-zero, random identifier for this instance of qhull */
    +  boolT VERTEXneighbors;  /* true if maintaining vertex neighbors */
    +  boolT ZEROcentrum;      /* true if 'C-0' or 'C-0 Qx'.  sets ZEROall_ok */
    +  realT *upper_threshold; /* don't print if facet->normal[k]>=upper_threshold[k]
    +                             must set either GOODthreshold or SPLITthreshold
    +                             if Delaunay, default is 0.0 for upper envelope */
    +  realT *lower_threshold; /* don't print if facet->normal[k] <=lower_threshold[k] */
    +  realT *upper_bound;     /* scale point[k] to new upper bound */
    +  realT *lower_bound;     /* scale point[k] to new lower bound
    +                             project if both upper_ and lower_bound == 0 */
    +
    +/*----------------------------------
    +
    +  qh precision constants
    +    precision constants for Qhull
    +
    +  notes:
    +    qh_detroundoff(qh) computes the maximum roundoff error for distance
    +    and other computations.  It also sets default values for the
    +    qh constants above.
    +*/
    +  realT ANGLEround;       /* max round off error for angles */
    +  realT centrum_radius;   /* max centrum radius for convexity (roundoff added) */
    +  realT cos_max;          /* max cosine for convexity (roundoff added) */
    +  realT DISTround;        /* max round off error for distances, 'E' overrides qh_distround() */
    +  realT MAXabs_coord;     /* max absolute coordinate */
    +  realT MAXlastcoord;     /* max last coordinate for qh_scalelast */
    +  realT MAXsumcoord;      /* max sum of coordinates */
    +  realT MAXwidth;         /* max rectilinear width of point coordinates */
    +  realT MINdenom_1;       /* min. abs. value for 1/x */
    +  realT MINdenom;         /*    use divzero if denominator < MINdenom */
    +  realT MINdenom_1_2;     /* min. abs. val for 1/x that allows normalization */
    +  realT MINdenom_2;       /*    use divzero if denominator < MINdenom_2 */
    +  realT MINlastcoord;     /* min. last coordinate for qh_scalelast */
    +  boolT NARROWhull;       /* set in qh_initialhull if angle < qh_MAXnarrow */
    +  realT *NEARzero;        /* hull_dim array for near zero in gausselim */
    +  realT NEARinside;       /* keep points for qh_check_maxout if close to facet */
    +  realT ONEmerge;         /* max distance for merging simplicial facets */
    +  realT outside_err;      /* application's epsilon for coplanar points
    +                             qh_check_bestdist() qh_check_points() reports error if point outside */
    +  realT WIDEfacet;        /* size of wide facet for skipping ridge in
    +                             area computation and locking centrum */
    +
    +/*----------------------------------
    +
    +  qh internal constants
    +    internal constants for Qhull
    +*/
    +  char qhull[sizeof("qhull")]; /* "qhull" for checking ownership while debugging */
    +  jmp_buf errexit;        /* exit label for qh_errexit, defined by setjmp() and NOerrexit */
    +  char jmpXtra[40];       /* extra bytes in case jmp_buf is defined wrong by compiler */
    +  jmp_buf restartexit;    /* restart label for qh_errexit, defined by setjmp() and ALLOWrestart */
    +  char jmpXtra2[40];      /* extra bytes in case jmp_buf is defined wrong by compiler*/
    +  FILE *fin;              /* pointer to input file, init by qh_initqhull_start */
    +  FILE *fout;             /* pointer to output file */
    +  FILE *ferr;             /* pointer to error file */
    +  pointT *interior_point; /* center point of the initial simplex*/
    +  int normal_size;     /* size in bytes for facet normals and point coords*/
    +  int center_size;     /* size in bytes for Voronoi centers */
    +  int   TEMPsize;         /* size for small, temporary sets (in quick mem) */
    +
    +/*----------------------------------
    +
    +  qh facet and vertex lists
    +    defines lists of facets, new facets, visible facets, vertices, and
    +    new vertices.  Includes counts, next ids, and trace ids.
    +  see:
    +    qh_resetlists()
    +*/
    +  facetT *facet_list;     /* first facet */
    +  facetT  *facet_tail;     /* end of facet_list (dummy facet) */
    +  facetT *facet_next;     /* next facet for buildhull()
    +                             previous facets do not have outside sets
    +                             NARROWhull: previous facets may have coplanar outside sets for qh_outcoplanar */
    +  facetT *newfacet_list;  /* list of new facets to end of facet_list */
    +  facetT *visible_list;   /* list of visible facets preceding newfacet_list,
    +                             facet->visible set */
    +  int       num_visible;  /* current number of visible facets */
    +  unsigned tracefacet_id;  /* set at init, then can print whenever */
    +  facetT *tracefacet;     /*   set in newfacet/mergefacet, undone in delfacet*/
    +  unsigned tracevertex_id;  /* set at buildtracing, can print whenever */
    +  vertexT *tracevertex;     /*   set in newvertex, undone in delvertex*/
    +  vertexT *vertex_list;     /* list of all vertices, to vertex_tail */
    +  vertexT  *vertex_tail;    /*      end of vertex_list (dummy vertex) */
    +  vertexT *newvertex_list; /* list of vertices in newfacet_list, to vertex_tail
    +                             all vertices have 'newlist' set */
    +  int   num_facets;       /* number of facets in facet_list
    +                             includes visible faces (num_visible) */
    +  int   num_vertices;     /* number of vertices in facet_list */
    +  int   num_outside;      /* number of points in outsidesets (for tracing and RANDOMoutside)
    +                               includes coplanar outsideset points for NARROWhull/qh_outcoplanar() */
    +  int   num_good;         /* number of good facets (after findgood_all) */
    +  unsigned facet_id;      /* ID of next, new facet from newfacet() */
    +  unsigned ridge_id;      /* ID of next, new ridge from newridge() */
    +  unsigned vertex_id;     /* ID of next, new vertex from newvertex() */
    +
    +/*----------------------------------
    +
    +  qh global variables
    +    defines minimum and maximum distances, next visit ids, several flags,
    +    and other global variables.
    +    initialize in qh_initbuild or qh_maxmin if used in qh_buildhull
    +*/
    +  unsigned long hulltime; /* ignore time to set up input and randomize */
    +                          /*   use unsigned to avoid wrap-around errors */
    +  boolT ALLOWrestart;     /* true if qh_precision can use qh.restartexit */
    +  int   build_cnt;        /* number of calls to qh_initbuild */
    +  qh_CENTER CENTERtype;   /* current type of facet->center, qh_CENTER */
    +  int   furthest_id;      /* pointid of furthest point, for tracing */
    +  facetT *GOODclosest;    /* closest facet to GOODthreshold in qh_findgood */
    +  boolT hasAreaVolume;    /* true if totarea, totvol was defined by qh_getarea */
    +  boolT hasTriangulation; /* true if triangulation created by qh_triangulate */
    +  realT JOGGLEmax;        /* set 'QJn' if randomly joggle input */
    +  boolT maxoutdone;       /* set qh_check_maxout(), cleared by qh_addpoint() */
    +  realT max_outside;      /* maximum distance from a point to a facet,
    +                               before roundoff, not simplicial vertices
    +                               actual outer plane is +DISTround and
    +                               computed outer plane is +2*DISTround */
    +  realT max_vertex;       /* maximum distance (>0) from vertex to a facet,
    +                               before roundoff, due to a merge */
    +  realT min_vertex;       /* minimum distance (<0) from vertex to a facet,
    +                               before roundoff, due to a merge
    +                               if qh.JOGGLEmax, qh_makenewplanes sets it
    +                               recomputed if qh.DOcheckmax, default -qh.DISTround */
    +  boolT NEWfacets;        /* true while visible facets invalid due to new or merge
    +                              from makecone/attachnewfacets to deletevisible */
    +  boolT findbestnew;      /* true if partitioning calls qh_findbestnew */
    +  boolT findbest_notsharp; /* true if new facets are at least 90 degrees */
    +  boolT NOerrexit;        /* true if qh.errexit is not available, cleared after setjmp */
    +  realT PRINTcradius;     /* radius for printing centrums */
    +  realT PRINTradius;      /* radius for printing vertex spheres and points */
    +  boolT POSTmerging;      /* true when post merging */
    +  int   printoutvar;      /* temporary variable for qh_printbegin, etc. */
    +  int   printoutnum;      /* number of facets printed */
    +  boolT QHULLfinished;    /* True after qhull() is finished */
    +  realT totarea;          /* 'FA': total facet area computed by qh_getarea, hasAreaVolume */
    +  realT totvol;           /* 'FA': total volume computed by qh_getarea, hasAreaVolume */
    +  unsigned int visit_id;  /* unique ID for searching neighborhoods, */
    +  unsigned int vertex_visit; /* unique ID for searching vertices, reset with qh_buildtracing */
    +  boolT ZEROall_ok;       /* True if qh_checkzero always succeeds */
    +  boolT WAScoplanar;      /* True if qh_partitioncoplanar (qhT *qh, qh_check_maxout) */
    +
    +/*----------------------------------
    +
    +  qh global sets
    +    defines sets for merging, initial simplex, hashing, extra input points,
    +    and deleted vertices
    +*/
    +  setT *facet_mergeset;   /* temporary set of merges to be done */
    +  setT *degen_mergeset;   /* temporary set of degenerate and redundant merges */
    +  setT *hash_table;       /* hash table for matching ridges in qh_matchfacets
    +                             size is setsize() */
    +  setT *other_points;     /* additional points */
    +  setT *del_vertices;     /* vertices to partition and delete with visible
    +                             facets.  Have deleted set for checkfacet */
    +
    +/*----------------------------------
    +
    +  qh global buffers
    +    defines buffers for maxtrix operations, input, and error messages
    +*/
    +  coordT *gm_matrix;      /* (dim+1)Xdim matrix for geom_r.c */
    +  coordT **gm_row;        /* array of gm_matrix rows */
    +  char* line;             /* malloc'd input line of maxline+1 chars */
    +  int maxline;
    +  coordT *half_space;     /* malloc'd input array for halfspace (qh.normal_size+coordT) */
    +  coordT *temp_malloc;    /* malloc'd input array for points */
    +
    +/*----------------------------------
    +
    +  qh static variables
    +    defines static variables for individual functions
    +
    +  notes:
    +    do not use 'static' within a function.  Multiple instances of qhull
    +    may exist.
    +
    +    do not assume zero initialization, 'QPn' may cause a restart
    +*/
    +  boolT ERREXITcalled;    /* true during qh_errexit (qhT *qh, prevents duplicate calls */
    +  boolT firstcentrum;     /* for qh_printcentrum */
    +  boolT old_randomdist;   /* save RANDOMdist flag during io, tracing, or statistics */
    +  setT *coplanarfacetset;  /* set of coplanar facets for searching qh_findbesthorizon() */
    +  realT last_low;         /* qh_scalelast parameters for qh_setdelaunay */
    +  realT last_high;
    +  realT last_newhigh;
    +  unsigned lastreport;    /* for qh_buildtracing */
    +  int mergereport;        /* for qh_tracemerging */
    +  setT *old_tempstack;    /* for saving qh->qhmem.tempstack in save_qhull */
    +  int   ridgeoutnum;      /* number of ridges for 4OFF output (qh_printbegin,etc) */
    +
    +/*----------------------------------
    +
    +  qh memory management, rbox globals, and statistics
    +
    +  Replaces global data structures defined for libqhull
    +*/
    +  int     last_random;    /* Last random number from qh_rand (random_r.c) */
    +  jmp_buf rbox_errexit;   /* errexit from rboxlib_r.c, defined by qh_rboxpoints() only */
    +  char    jmpXtra3[40];   /* extra bytes in case jmp_buf is defined wrong by compiler */
    +  int     rbox_isinteger;
    +  double  rbox_out_offset;
    +  void *  cpp_object;     /* C++ pointer.  Currently used by RboxPoints.qh_fprintf_rbox */
    +
    +  /* Last, otherwise zero'd by qh_initqhull_start2 (global_r.c */
    +  qhmemT  qhmem;          /* Qhull managed memory (mem_r.h) */
    +  /* After qhmem because its size depends on the number of statistics */
    +  qhstatT qhstat;         /* Qhull statistics (stat_r.h) */
    +};
    +
    +/*=========== -macros- =========================*/
    +
    +/*----------------------------------
    +
    +  otherfacet_(ridge, facet)
    +    return neighboring facet for a ridge in facet
    +*/
    +#define otherfacet_(ridge, facet) \
    +                        (((ridge)->top == (facet)) ? (ridge)->bottom : (ridge)->top)
    +
    +/*----------------------------------
    +
    +  getid_(p)
    +    return int ID for facet, ridge, or vertex
    +    return qh_IDunknown(-1) if NULL
    +*/
    +#define getid_(p)       ((p) ? (int)((p)->id) : qh_IDunknown)
    +
    +/*============== FORALL macros ===================*/
    +
    +/*----------------------------------
    +
    +  FORALLfacets { ... }
    +    assign 'facet' to each facet in qh.facet_list
    +
    +  notes:
    +    uses 'facetT *facet;'
    +    assumes last facet is a sentinel
    +    assumes qh defined
    +
    +  see:
    +    FORALLfacet_( facetlist )
    +*/
    +#define FORALLfacets for (facet=qh->facet_list;facet && facet->next;facet=facet->next)
    +
    +/*----------------------------------
    +
    +  FORALLpoints { ... }
    +    assign 'point' to each point in qh.first_point, qh.num_points
    +
    +  notes:
    +    assumes qh defined
    +
    +  declare:
    +    coordT *point, *pointtemp;
    +*/
    +#define FORALLpoints FORALLpoint_(qh, qh->first_point, qh->num_points)
    +
    +/*----------------------------------
    +
    +  FORALLpoint_( qh, points, num) { ... }
    +    assign 'point' to each point in points array of num points
    +
    +  declare:
    +    coordT *point, *pointtemp;
    +*/
    +#define FORALLpoint_(qh, points, num) for (point= (points), \
    +      pointtemp= (points)+qh->hull_dim*(num); point < pointtemp; point += qh->hull_dim)
    +
    +/*----------------------------------
    +
    +  FORALLvertices { ... }
    +    assign 'vertex' to each vertex in qh.vertex_list
    +
    +  declare:
    +    vertexT *vertex;
    +
    +  notes:
    +    assumes qh.vertex_list terminated with a sentinel
    +    assumes qh defined
    +*/
    +#define FORALLvertices for (vertex=qh->vertex_list;vertex && vertex->next;vertex= vertex->next)
    +
    +/*----------------------------------
    +
    +  FOREACHfacet_( facets ) { ... }
    +    assign 'facet' to each facet in facets
    +
    +  declare:
    +    facetT *facet, **facetp;
    +
    +  see:
    +    FOREACHsetelement_
    +*/
    +#define FOREACHfacet_(facets)    FOREACHsetelement_(facetT, facets, facet)
    +
    +/*----------------------------------
    +
    +  FOREACHneighbor_( facet ) { ... }
    +    assign 'neighbor' to each neighbor in facet->neighbors
    +
    +  FOREACHneighbor_( vertex ) { ... }
    +    assign 'neighbor' to each neighbor in vertex->neighbors
    +
    +  declare:
    +    facetT *neighbor, **neighborp;
    +
    +  see:
    +    FOREACHsetelement_
    +*/
    +#define FOREACHneighbor_(facet)  FOREACHsetelement_(facetT, facet->neighbors, neighbor)
    +
    +/*----------------------------------
    +
    +  FOREACHpoint_( points ) { ... }
    +    assign 'point' to each point in points set
    +
    +  declare:
    +    pointT *point, **pointp;
    +
    +  see:
    +    FOREACHsetelement_
    +*/
    +#define FOREACHpoint_(points)    FOREACHsetelement_(pointT, points, point)
    +
    +/*----------------------------------
    +
    +  FOREACHridge_( ridges ) { ... }
    +    assign 'ridge' to each ridge in ridges set
    +
    +  declare:
    +    ridgeT *ridge, **ridgep;
    +
    +  see:
    +    FOREACHsetelement_
    +*/
    +#define FOREACHridge_(ridges)    FOREACHsetelement_(ridgeT, ridges, ridge)
    +
    +/*----------------------------------
    +
    +  FOREACHvertex_( vertices ) { ... }
    +    assign 'vertex' to each vertex in vertices set
    +
    +  declare:
    +    vertexT *vertex, **vertexp;
    +
    +  see:
    +    FOREACHsetelement_
    +*/
    +#define FOREACHvertex_(vertices) FOREACHsetelement_(vertexT, vertices,vertex)
    +
    +/*----------------------------------
    +
    +  FOREACHfacet_i_( qh, facets ) { ... }
    +    assign 'facet' and 'facet_i' for each facet in facets set
    +
    +  declare:
    +    facetT *facet;
    +    int     facet_n, facet_i;
    +
    +  see:
    +    FOREACHsetelement_i_
    +*/
    +#define FOREACHfacet_i_(qh, facets)    FOREACHsetelement_i_(qh, facetT, facets, facet)
    +
    +/*----------------------------------
    +
    +  FOREACHneighbor_i_( qh, facet ) { ... }
    +    assign 'neighbor' and 'neighbor_i' for each neighbor in facet->neighbors
    +
    +  FOREACHneighbor_i_( qh, vertex ) { ... }
    +    assign 'neighbor' and 'neighbor_i' for each neighbor in vertex->neighbors
    +
    +  declare:
    +    facetT *neighbor;
    +    int     neighbor_n, neighbor_i;
    +
    +  see:
    +    FOREACHsetelement_i_
    +*/
    +#define FOREACHneighbor_i_(qh, facet)  FOREACHsetelement_i_(qh, facetT, facet->neighbors, neighbor)
    +
    +/*----------------------------------
    +
    +  FOREACHpoint_i_( qh, points ) { ... }
    +    assign 'point' and 'point_i' for each point in points set
    +
    +  declare:
    +    pointT *point;
    +    int     point_n, point_i;
    +
    +  see:
    +    FOREACHsetelement_i_
    +*/
    +#define FOREACHpoint_i_(qh, points)    FOREACHsetelement_i_(qh, pointT, points, point)
    +
    +/*----------------------------------
    +
    +  FOREACHridge_i_( qh, ridges ) { ... }
    +    assign 'ridge' and 'ridge_i' for each ridge in ridges set
    +
    +  declare:
    +    ridgeT *ridge;
    +    int     ridge_n, ridge_i;
    +
    +  see:
    +    FOREACHsetelement_i_
    +*/
    +#define FOREACHridge_i_(qh, ridges)    FOREACHsetelement_i_(qh, ridgeT, ridges, ridge)
    +
    +/*----------------------------------
    +
    +  FOREACHvertex_i_( qh, vertices ) { ... }
    +    assign 'vertex' and 'vertex_i' for each vertex in vertices set
    +
    +  declare:
    +    vertexT *vertex;
    +    int     vertex_n, vertex_i;
    +
    +  see:
    +    FOREACHsetelement_i_
    +*/
    +#define FOREACHvertex_i_(qh, vertices) FOREACHsetelement_i_(qh, vertexT, vertices,vertex)
    +
    +#ifdef __cplusplus
    +extern "C" {
    +#endif
    +
    +/********* -libqhull_r.c prototypes (duplicated from qhull_ra.h) **********************/
    +
    +void    qh_qhull(qhT *qh);
    +boolT   qh_addpoint(qhT *qh, pointT *furthest, facetT *facet, boolT checkdist);
    +void    qh_printsummary(qhT *qh, FILE *fp);
    +
    +/********* -user.c prototypes (alphabetical) **********************/
    +
    +void    qh_errexit(qhT *qh, int exitcode, facetT *facet, ridgeT *ridge);
    +void    qh_errprint(qhT *qh, const char* string, facetT *atfacet, facetT *otherfacet, ridgeT *atridge, vertexT *atvertex);
    +int     qh_new_qhull(qhT *qh, int dim, int numpoints, coordT *points, boolT ismalloc,
    +                char *qhull_cmd, FILE *outfile, FILE *errfile);
    +void    qh_printfacetlist(qhT *qh, facetT *facetlist, setT *facets, boolT printall);
    +void    qh_printhelp_degenerate(qhT *qh, FILE *fp);
    +void    qh_printhelp_narrowhull(qhT *qh, FILE *fp, realT minangle);
    +void    qh_printhelp_singular(qhT *qh, FILE *fp);
    +void    qh_user_memsizes(qhT *qh);
    +
    +/********* -usermem_r.c prototypes (alphabetical) **********************/
    +void    qh_exit(int exitcode);
    +void    qh_fprintf_stderr(int msgcode, const char *fmt, ... );
    +void    qh_free(void *mem);
    +void   *qh_malloc(size_t size);
    +
    +/********* -userprintf_r.c and userprintf_rbox_r.c prototypes **********************/
    +void    qh_fprintf(qhT *qh, FILE *fp, int msgcode, const char *fmt, ... );
    +void    qh_fprintf_rbox(qhT *qh, FILE *fp, int msgcode, const char *fmt, ... );
    +
    +/***** -geom_r.c/geom2_r.c/random_r.c prototypes (duplicated from geom_r.h, random_r.h) ****************/
    +
    +facetT *qh_findbest(qhT *qh, pointT *point, facetT *startfacet,
    +                     boolT bestoutside, boolT newfacets, boolT noupper,
    +                     realT *dist, boolT *isoutside, int *numpart);
    +facetT *qh_findbestnew(qhT *qh, pointT *point, facetT *startfacet,
    +                     realT *dist, boolT bestoutside, boolT *isoutside, int *numpart);
    +boolT   qh_gram_schmidt(qhT *qh, int dim, realT **rows);
    +void    qh_outerinner(qhT *qh, facetT *facet, realT *outerplane, realT *innerplane);
    +void    qh_printsummary(qhT *qh, FILE *fp);
    +void    qh_projectinput(qhT *qh);
    +void    qh_randommatrix(qhT *qh, realT *buffer, int dim, realT **row);
    +void    qh_rotateinput(qhT *qh, realT **rows);
    +void    qh_scaleinput(qhT *qh);
    +void    qh_setdelaunay(qhT *qh, int dim, int count, pointT *points);
    +coordT  *qh_sethalfspace_all(qhT *qh, int dim, int count, coordT *halfspaces, pointT *feasible);
    +
    +/***** -global_r.c prototypes (alphabetical) ***********************/
    +
    +unsigned long qh_clock(qhT *qh);
    +void    qh_checkflags(qhT *qh, char *command, char *hiddenflags);
    +void    qh_clear_outputflags(qhT *qh);
    +void    qh_freebuffers(qhT *qh);
    +void    qh_freeqhull(qhT *qh, boolT allmem);
    +void    qh_init_A(qhT *qh, FILE *infile, FILE *outfile, FILE *errfile, int argc, char *argv[]);
    +void    qh_init_B(qhT *qh, coordT *points, int numpoints, int dim, boolT ismalloc);
    +void    qh_init_qhull_command(qhT *qh, int argc, char *argv[]);
    +void    qh_initbuffers(qhT *qh, coordT *points, int numpoints, int dim, boolT ismalloc);
    +void    qh_initflags(qhT *qh, char *command);
    +void    qh_initqhull_buffers(qhT *qh);
    +void    qh_initqhull_globals(qhT *qh, coordT *points, int numpoints, int dim, boolT ismalloc);
    +void    qh_initqhull_mem(qhT *qh);
    +void    qh_initqhull_outputflags(qhT *qh);
    +void    qh_initqhull_start(qhT *qh, FILE *infile, FILE *outfile, FILE *errfile);
    +void    qh_initqhull_start2(qhT *qh, FILE *infile, FILE *outfile, FILE *errfile);
    +void    qh_initthresholds(qhT *qh, char *command);
    +void    qh_lib_check(int qhullLibraryType, int qhTsize, int vertexTsize, int ridgeTsize, int facetTsize, int setTsize, int qhmemTsize);
    +void    qh_option(qhT *qh, const char *option, int *i, realT *r);
    +void    qh_zero(qhT *qh, FILE *errfile);
    +
    +/***** -io_r.c prototypes (duplicated from io_r.h) ***********************/
    +
    +void    qh_dfacet(qhT *qh, unsigned id);
    +void    qh_dvertex(qhT *qh, unsigned id);
    +void    qh_printneighborhood(qhT *qh, FILE *fp, qh_PRINT format, facetT *facetA, facetT *facetB, boolT printall);
    +void    qh_produce_output(qhT *qh);
    +coordT *qh_readpoints(qhT *qh, int *numpoints, int *dimension, boolT *ismalloc);
    +
    +
    +/********* -mem_r.c prototypes (duplicated from mem_r.h) **********************/
    +
    +void qh_meminit(qhT *qh, FILE *ferr);
    +void qh_memfreeshort(qhT *qh, int *curlong, int *totlong);
    +
    +/********* -poly_r.c/poly2_r.c prototypes (duplicated from poly_r.h) **********************/
    +
    +void    qh_check_output(qhT *qh);
    +void    qh_check_points(qhT *qh);
    +setT   *qh_facetvertices(qhT *qh, facetT *facetlist, setT *facets, boolT allfacets);
    +facetT *qh_findbestfacet(qhT *qh, pointT *point, boolT bestoutside,
    +           realT *bestdist, boolT *isoutside);
    +vertexT *qh_nearvertex(qhT *qh, facetT *facet, pointT *point, realT *bestdistp);
    +pointT *qh_point(qhT *qh, int id);
    +setT   *qh_pointfacet(qhT *qh /*qh.facet_list*/);
    +int     qh_pointid(qhT *qh, pointT *point);
    +setT   *qh_pointvertex(qhT *qh /*qh.facet_list*/);
    +void    qh_setvoronoi_all(qhT *qh);
    +void    qh_triangulate(qhT *qh /*qh.facet_list*/);
    +
    +/********* -rboxpoints_r.c prototypes **********************/
    +int     qh_rboxpoints(qhT *qh, char* rbox_command);
    +void    qh_errexit_rbox(qhT *qh, int exitcode);
    +
    +/********* -stat_r.c prototypes (duplicated from stat_r.h) **********************/
    +
    +void    qh_collectstatistics(qhT *qh);
    +void    qh_printallstatistics(qhT *qh, FILE *fp, const char *string);
    +
    +#ifdef __cplusplus
    +} /* extern "C" */
    +#endif
    +
    +#endif /* qhDEFlibqhull */
    diff --git a/xs/src/qhull/src/libqhull_r/libqhull_r.pro b/xs/src/qhull/src/libqhull_r/libqhull_r.pro
    new file mode 100644
    index 000000000..6b8db44b7
    --- /dev/null
    +++ b/xs/src/qhull/src/libqhull_r/libqhull_r.pro
    @@ -0,0 +1,67 @@
    +# -------------------------------------------------
    +# libqhull_r.pro -- Qt project for Qhull shared library
    +#
    +# It uses reentrant Qhull
    +# -------------------------------------------------
    +
    +include(../qhull-warn.pri)
    +
    +DESTDIR = ../../lib
    +DLLDESTDIR = ../../bin
    +TEMPLATE = lib
    +CONFIG += shared warn_on
    +CONFIG -= qt
    +
    +build_pass:CONFIG(debug, debug|release):{
    +    TARGET = qhull_rd
    +    OBJECTS_DIR = Debug
    +}else:build_pass:CONFIG(release, debug|release):{
    +    TARGET = qhull_r
    +    OBJECTS_DIR = Release
    +}
    +win32-msvc* : QMAKE_LFLAGS += /INCREMENTAL:NO
    +
    +win32-msvc* : DEF_FILE += ../../src/libqhull_r/qhull_r-exports.def
    +
    +# libqhull_r/libqhull_r.pro and ../qhull-libqhull-src_r.pri have the same SOURCES and HEADERS
    +
    +SOURCES += ../libqhull_r/global_r.c
    +SOURCES += ../libqhull_r/stat_r.c
    +SOURCES += ../libqhull_r/geom2_r.c
    +SOURCES += ../libqhull_r/poly2_r.c
    +SOURCES += ../libqhull_r/merge_r.c
    +SOURCES += ../libqhull_r/libqhull_r.c
    +SOURCES += ../libqhull_r/geom_r.c
    +SOURCES += ../libqhull_r/poly_r.c
    +SOURCES += ../libqhull_r/qset_r.c
    +SOURCES += ../libqhull_r/mem_r.c
    +SOURCES += ../libqhull_r/random_r.c
    +SOURCES += ../libqhull_r/usermem_r.c
    +SOURCES += ../libqhull_r/userprintf_r.c
    +SOURCES += ../libqhull_r/io_r.c
    +SOURCES += ../libqhull_r/user_r.c
    +SOURCES += ../libqhull_r/rboxlib_r.c
    +SOURCES += ../libqhull_r/userprintf_rbox_r.c
    +
    +HEADERS += ../libqhull_r/geom_r.h
    +HEADERS += ../libqhull_r/io_r.h
    +HEADERS += ../libqhull_r/libqhull_r.h
    +HEADERS += ../libqhull_r/mem_r.h
    +HEADERS += ../libqhull_r/merge_r.h
    +HEADERS += ../libqhull_r/poly_r.h
    +HEADERS += ../libqhull_r/random_r.h
    +HEADERS += ../libqhull_r/qhull_ra.h
    +HEADERS += ../libqhull_r/qset_r.h
    +HEADERS += ../libqhull_r/stat_r.h
    +HEADERS += ../libqhull_r/user_r.h
    +
    +OTHER_FILES += qh-geom_r.htm
    +OTHER_FILES += qh-globa_r.htm
    +OTHER_FILES += qh-io_r.htm
    +OTHER_FILES += qh-mem_r.htm
    +OTHER_FILES += qh-merge_r.htm
    +OTHER_FILES += qh-poly_r.htm
    +OTHER_FILES += qh-qhull_r.htm
    +OTHER_FILES += qh-set_r.htm
    +OTHER_FILES += qh-stat_r.htm
    +OTHER_FILES += qh-user_r.htm
    diff --git a/xs/src/qhull/src/libqhull_r/mem_r.c b/xs/src/qhull/src/libqhull_r/mem_r.c
    new file mode 100644
    index 000000000..801a8c76a
    --- /dev/null
    +++ b/xs/src/qhull/src/libqhull_r/mem_r.c
    @@ -0,0 +1,562 @@
    +/*
      ---------------------------------
    +
    +  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 
    +#include 
    +#include 
    +
    +#ifndef qh_NOmem
    +
    +/*============= internal functions ==============*/
    +
    +static int qh_intcompare(const void *i, const void *j);
    +
    +/*========== functions in alphabetical order ======== */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +
    +/*----------------------------------
    +
    +  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 */
    +
    +
    +/*----------------------------------
    +
    +  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() */
    +  }
    +  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 */
    +
    +/*----------------------------------
    +
    +  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 */
    +
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +
    +/*----------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  qh_memtotal(qh, totlong, curlong, totshort, curshort, maxlong, totbuffer )
    +    Return the total, allocated long and short memory
    +
    +  returns:
    +    Returns the total current bytes of long and short allocations
    +    Returns the current count of long and short allocations
    +    Returns the maximum long memory and total short buffer (minus one link per buffer)
    +    Does not error (for deprecated UsingLibQhull.cpp (libqhullpcpp))
    +*/
    +void qh_memtotal(qhT *qh, int *totlong, int *curlong, int *totshort, int *curshort, int *maxlong, int *totbuffer) {
    +    *totlong= qh->qhmem.totlong;
    +    *curlong= qh->qhmem.cntlong - qh->qhmem.freelong;
    +    *totshort= qh->qhmem.totshort;
    +    *curshort= qh->qhmem.cntshort + qh->qhmem.cntquick - qh->qhmem.freeshort;
    +    *maxlong= qh->qhmem.maxlong;
    +    *totbuffer= qh->qhmem.totbuffer;
    +} /* memtotlong */
    +
    diff --git a/xs/src/qhull/src/libqhull_r/mem_r.h b/xs/src/qhull/src/libqhull_r/mem_r.h
    new file mode 100644
    index 000000000..25b551333
    --- /dev/null
    +++ b/xs/src/qhull/src/libqhull_r/mem_r.h
    @@ -0,0 +1,234 @@
    +/*
      ---------------------------------
    +
    +   mem_r.h
    +     prototypes for memory management functions
    +
    +   see qh-mem_r.htm, mem_r.c and qset_r.h
    +
    +   for error handling, writes message and calls
    +     qh_errexit(qhT *qh, qhmem_ERRmem, NULL, NULL) if insufficient memory
    +       and
    +     qh_errexit(qhT *qh, qhmem_ERRqhull, NULL, NULL) otherwise
    +
    +   Copyright (c) 1993-2015 The Geometry Center.
    +   $Id: //main/2015/qhull/src/libqhull_r/mem_r.h#4 $$Change: 2079 $
    +   $DateTime: 2016/02/07 17:43:34 $$Author: bbarber $
    +*/
    +
    +#ifndef qhDEFmem
    +#define qhDEFmem 1
    +
    +#include 
    +
    +#ifndef DEFsetT
    +#define DEFsetT 1
    +typedef struct setT setT;          /* defined in qset_r.h */
    +#endif
    +
    +#ifndef DEFqhT
    +#define DEFqhT 1
    +typedef struct qhT qhT;          /* defined in libqhull_r.h */
    +#endif
    +
    +/*---------------------------------
    +
    +  qh_NOmem
    +    turn off quick-fit memory allocation
    +
    +  notes:
    +    mem_r.c implements Quickfit memory allocation for about 20% time
    +    savings.  If it fails on your machine, try to locate the
    +    problem, and send the answer to qhull@qhull.org.  If this can
    +    not be done, define qh_NOmem to use malloc/free instead.
    +
    +   #define qh_NOmem
    +*/
    +
    +/*---------------------------------
    +
    +qh_TRACEshort
    +Trace short and quick memory allocations at T5
    +
    +*/
    +#define qh_TRACEshort
    +
    +/*-------------------------------------------
    +    to avoid bus errors, memory allocation must consider alignment requirements.
    +    malloc() automatically takes care of alignment.   Since mem_r.c manages
    +    its own memory, we need to explicitly specify alignment in
    +    qh_meminitbuffers().
    +
    +    A safe choice is sizeof(double).  sizeof(float) may be used if doubles
    +    do not occur in data structures and pointers are the same size.  Be careful
    +    of machines (e.g., DEC Alpha) with large pointers.  If gcc is available,
    +    use __alignof__(double) or fmax_(__alignof__(float), __alignof__(void *)).
    +
    +   see qh_MEMalign in user.h for qhull's alignment
    +*/
    +
    +#define qhmem_ERRmem 4    /* matches qh_ERRmem in libqhull_r.h */
    +#define qhmem_ERRqhull 5  /* matches qh_ERRqhull in libqhull_r.h */
    +
    +/*----------------------------------
    +
    +  ptr_intT
    +    for casting a void * to an integer-type that holds a pointer
    +    Used for integer expressions (e.g., computing qh_gethash() in poly_r.c)
    +
    +  notes:
    +    WARN64 -- these notes indicate 64-bit issues
    +    On 64-bit machines, a pointer may be larger than an 'int'.
    +    qh_meminit()/mem_r.c checks that 'ptr_intT' holds a 'void*'
    +    ptr_intT is typically a signed value, but not necessarily so
    +    size_t is typically unsigned, but should match the parameter type
    +    Qhull uses int instead of size_t except for system calls such as malloc, qsort, qh_malloc, etc.
    +    This matches Qt convention and is easier to work with.
    +*/
    +#if (defined(__MINGW64__)) && defined(_WIN64)
    +typedef long long ptr_intT;
    +#elif (_MSC_VER) && defined(_WIN64)
    +typedef long long ptr_intT;
    +#else
    +typedef long ptr_intT;
    +#endif
    +
    +/*----------------------------------
    +
    +  qhmemT
    +    global memory structure for mem_r.c
    +
    + notes:
    +   users should ignore qhmem except for writing extensions
    +   qhmem is allocated in mem_r.c
    +
    +   qhmem could be swapable like qh and qhstat, but then
    +   multiple qh's and qhmem's would need to keep in synch.
    +   A swapable qhmem would also waste memory buffers.  As long
    +   as memory operations are atomic, there is no problem with
    +   multiple qh structures being active at the same time.
    +   If you need separate address spaces, you can swap the
    +   contents of qh->qhmem.
    +*/
    +typedef struct qhmemT qhmemT;
    +
    +/* Update qhmem in mem_r.c if add or remove fields */
    +struct qhmemT {               /* global memory management variables */
    +  int      BUFsize;           /* size of memory allocation buffer */
    +  int      BUFinit;           /* initial size of memory allocation buffer */
    +  int      TABLEsize;         /* actual number of sizes in free list table */
    +  int      NUMsizes;          /* maximum number of sizes in free list table */
    +  int      LASTsize;          /* last size in free list table */
    +  int      ALIGNmask;         /* worst-case alignment, must be 2^n-1 */
    +  void   **freelists;          /* free list table, linked by offset 0 */
    +  int     *sizetable;         /* size of each freelist */
    +  int     *indextable;        /* size->index table */
    +  void    *curbuffer;         /* current buffer, linked by offset 0 */
    +  void    *freemem;           /*   free memory in curbuffer */
    +  int      freesize;          /*   size of freemem in bytes */
    +  setT    *tempstack;         /* stack of temporary memory, managed by users */
    +  FILE    *ferr;              /* file for reporting errors when 'qh' may be undefined */
    +  int      IStracing;         /* =5 if tracing memory allocations */
    +  int      cntquick;          /* count of quick allocations */
    +                              /* Note: removing statistics doesn't effect speed */
    +  int      cntshort;          /* count of short allocations */
    +  int      cntlong;           /* count of long allocations */
    +  int      freeshort;         /* count of short memfrees */
    +  int      freelong;          /* count of long memfrees */
    +  int      totbuffer;         /* total short memory buffers minus buffer links */
    +  int      totdropped;        /* total dropped memory at end of short memory buffers (e.g., freesize) */
    +  int      totfree;           /* total size of free, short memory on freelists */
    +  int      totlong;           /* total size of long memory in use */
    +  int      maxlong;           /*   maximum totlong */
    +  int      totshort;          /* total size of short memory in use */
    +  int      totunused;         /* total unused short memory (estimated, short size - request size of first allocations) */
    +  int      cntlarger;         /* count of setlarger's */
    +  int      totlarger;         /* total copied by setlarger */
    +};
    +
    +
    +/*==================== -macros ====================*/
    +
    +/*----------------------------------
    +
    +  qh_memalloc_(qh, insize, freelistp, object, type)
    +    returns object of size bytes
    +        assumes size<=qh->qhmem.LASTsize and void **freelistp is a temp
    +*/
    +
    +#if defined qh_NOmem
    +#define qh_memalloc_(qh, insize, freelistp, object, type) {\
    +  object= (type*)qh_memalloc(qh, insize); }
    +#elif defined qh_TRACEshort
    +#define qh_memalloc_(qh, insize, freelistp, object, type) {\
    +    freelistp= NULL; /* Avoid warnings */ \
    +    object= (type*)qh_memalloc(qh, insize); }
    +#else /* !qh_NOmem */
    +
    +#define qh_memalloc_(qh, insize, freelistp, object, type) {\
    +  freelistp= qh->qhmem.freelists + qh->qhmem.indextable[insize];\
    +  if ((object= (type*)*freelistp)) {\
    +    qh->qhmem.totshort += qh->qhmem.sizetable[qh->qhmem.indextable[insize]]; \
    +    qh->qhmem.totfree -= qh->qhmem.sizetable[qh->qhmem.indextable[insize]]; \
    +    qh->qhmem.cntquick++;  \
    +    *freelistp= *((void **)*freelistp);\
    +  }else object= (type*)qh_memalloc(qh, insize);}
    +#endif
    +
    +/*----------------------------------
    +
    +  qh_memfree_(qh, object, insize, freelistp)
    +    free up an object
    +
    +  notes:
    +    object may be NULL
    +    assumes size<=qh->qhmem.LASTsize and void **freelistp is a temp
    +*/
    +#if defined qh_NOmem
    +#define qh_memfree_(qh, object, insize, freelistp) {\
    +  qh_memfree(qh, object, insize); }
    +#elif defined qh_TRACEshort
    +#define qh_memfree_(qh, object, insize, freelistp) {\
    +    freelistp= NULL; /* Avoid warnings */ \
    +    qh_memfree(qh, object, insize); }
    +#else /* !qh_NOmem */
    +
    +#define qh_memfree_(qh, object, insize, freelistp) {\
    +  if (object) { \
    +    qh->qhmem.freeshort++;\
    +    freelistp= qh->qhmem.freelists + qh->qhmem.indextable[insize];\
    +    qh->qhmem.totshort -= qh->qhmem.sizetable[qh->qhmem.indextable[insize]]; \
    +    qh->qhmem.totfree += qh->qhmem.sizetable[qh->qhmem.indextable[insize]]; \
    +    *((void **)object)= *freelistp;\
    +    *freelistp= object;}}
    +#endif
    +
    +/*=============== prototypes in alphabetical order ============*/
    +
    +#ifdef __cplusplus
    +extern "C" {
    +#endif
    +
    +void *qh_memalloc(qhT *qh, int insize);
    +void qh_memcheck(qhT *qh);
    +void qh_memfree(qhT *qh, void *object, int insize);
    +void qh_memfreeshort(qhT *qh, int *curlong, int *totlong);
    +void qh_meminit(qhT *qh, FILE *ferr);
    +void qh_meminitbuffers(qhT *qh, int tracelevel, int alignment, int numsizes,
    +                        int bufsize, int bufinit);
    +void qh_memsetup(qhT *qh);
    +void qh_memsize(qhT *qh, int size);
    +void qh_memstatistics(qhT *qh, FILE *fp);
    +void qh_memtotal(qhT *qh, int *totlong, int *curlong, int *totshort, int *curshort, int *maxlong, int *totbuffer);
    +
    +#ifdef __cplusplus
    +} /* extern "C" */
    +#endif
    +
    +#endif /* qhDEFmem */
    diff --git a/xs/src/qhull/src/libqhull_r/merge_r.c b/xs/src/qhull/src/libqhull_r/merge_r.c
    new file mode 100644
    index 000000000..e5823de8d
    --- /dev/null
    +++ b/xs/src/qhull/src/libqhull_r/merge_r.c
    @@ -0,0 +1,3627 @@
    +/*
      ---------------------------------
    +
    +   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) ======*/
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  qh_willdelete(qh, facet, replace )
    +    moves facet to visible list
    +    sets facet->f.replace to replace (may be NULL)
    +
    +  returns:
    +    bumps qh.num_visible
    +*/
    +void qh_willdelete(qhT *qh, facetT *facet, facetT *replace) {
    +
    +  qh_removefacet(qh, facet);
    +  qh_prependfacet(qh, facet, &qh->visible_list);
    +  qh->num_visible++;
    +  facet->visible= True;
    +  facet->f.replace= replace;
    +} /* willdelete */
    +
    +#else /* qh_NOmerge */
    +void qh_premerge(qhT *qh, vertexT *apex, realT maxcentrum, realT maxangle) {
    +}
    +void qh_postmerge(qhT *qh, const char *reason, realT maxcentrum, realT maxangle,
    +                      boolT vneighbors) {
    +}
    +boolT qh_checkzero(qhT *qh, boolT testall) {
    +   }
    +#endif /* qh_NOmerge */
    +
    diff --git a/xs/src/qhull/src/libqhull_r/merge_r.h b/xs/src/qhull/src/libqhull_r/merge_r.h
    new file mode 100644
    index 000000000..30a51815d
    --- /dev/null
    +++ b/xs/src/qhull/src/libqhull_r/merge_r.h
    @@ -0,0 +1,186 @@
    +/*
      ---------------------------------
    +
    +   merge_r.h
    +   header file for merge_r.c
    +
    +   see qh-merge_r.htm and merge_r.c
    +
    +   Copyright (c) 1993-2015 C.B. Barber.
    +   $Id: //main/2015/qhull/src/libqhull_r/merge_r.h#3 $$Change: 2079 $
    +   $DateTime: 2016/02/07 17:43:34 $$Author: bbarber $
    +*/
    +
    +#ifndef qhDEFmerge
    +#define qhDEFmerge 1
    +
    +#include "libqhull_r.h"
    +
    +
    +/*============ -constants- ==============*/
    +
    +/*----------------------------------
    +
    +  qh_ANGLEredundant
    +    indicates redundant merge in mergeT->angle
    +*/
    +#define qh_ANGLEredundant 6.0
    +
    +/*----------------------------------
    +
    +  qh_ANGLEdegen
    +    indicates degenerate facet in mergeT->angle
    +*/
    +#define qh_ANGLEdegen     5.0
    +
    +/*----------------------------------
    +
    +  qh_ANGLEconcave
    +    offset to indicate concave facets in mergeT->angle
    +
    +  notes:
    +    concave facets are assigned the range of [2,4] in mergeT->angle
    +    roundoff error may make the angle less than 2
    +*/
    +#define qh_ANGLEconcave  1.5
    +
    +/*----------------------------------
    +
    +  MRG... (mergeType)
    +    indicates the type of a merge (mergeT->type)
    +*/
    +typedef enum {  /* in sort order for facet_mergeset */
    +  MRGnone= 0,
    +  MRGcoplanar,          /* centrum coplanar */
    +  MRGanglecoplanar,     /* angle coplanar */
    +                        /* could detect half concave ridges */
    +  MRGconcave,           /* concave ridge */
    +  MRGflip,              /* flipped facet. facet1 == facet2 */
    +  MRGridge,             /* duplicate ridge (qh_MERGEridge) */
    +                        /* degen and redundant go onto degen_mergeset */
    +  MRGdegen,             /* degenerate facet (!enough neighbors) facet1 == facet2 */
    +  MRGredundant,         /* redundant facet (vertex subset) */
    +                        /* merge_degenredundant assumes degen < redundant */
    +  MRGmirror,            /* mirror facet from qh_triangulate */
    +  ENDmrg
    +} mergeType;
    +
    +/*----------------------------------
    +
    +  qh_MERGEapex
    +    flag for qh_mergefacet() to indicate an apex merge
    +*/
    +#define qh_MERGEapex     True
    +
    +/*============ -structures- ====================*/
    +
    +/*----------------------------------
    +
    +  mergeT
    +    structure used to merge facets
    +*/
    +
    +typedef struct mergeT mergeT;
    +struct mergeT {         /* initialize in qh_appendmergeset */
    +  realT   angle;        /* angle between normals of facet1 and facet2 */
    +  facetT *facet1;       /* will merge facet1 into facet2 */
    +  facetT *facet2;
    +  mergeType type;
    +};
    +
    +
    +/*=========== -macros- =========================*/
    +
    +/*----------------------------------
    +
    +  FOREACHmerge_( merges ) {...}
    +    assign 'merge' to each merge in merges
    +
    +  notes:
    +    uses 'mergeT *merge, **mergep;'
    +    if qh_mergefacet(),
    +      restart since qh.facet_mergeset may change
    +    see FOREACHsetelement_
    +*/
    +#define FOREACHmerge_( merges ) FOREACHsetelement_(mergeT, merges, merge)
    +
    +/*============ prototypes in alphabetical order after pre/postmerge =======*/
    +
    +#ifdef __cplusplus
    +extern "C" {
    +#endif
    +
    +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);
    +void    qh_all_merges(qhT *qh, boolT othermerge, boolT vneighbors);
    +void    qh_appendmergeset(qhT *qh, facetT *facet, facetT *neighbor, mergeType mergetype, realT *angle);
    +setT   *qh_basevertices(qhT *qh, facetT *samecycle);
    +void    qh_checkconnect(qhT *qh /* qh.new_facets */);
    +boolT   qh_checkzero(qhT *qh, boolT testall);
    +int     qh_compareangle(const void *p1, const void *p2);
    +int     qh_comparemerge(const void *p1, const void *p2);
    +int     qh_comparevisit(const void *p1, const void *p2);
    +void    qh_copynonconvex(qhT *qh, ridgeT *atridge);
    +void    qh_degen_redundant_facet(qhT *qh, facetT *facet);
    +void    qh_degen_redundant_neighbors(qhT *qh, facetT *facet, facetT *delfacet);
    +vertexT *qh_find_newvertex(qhT *qh, vertexT *oldvertex, setT *vertices, setT *ridges);
    +void    qh_findbest_test(qhT *qh, boolT testcentrum, facetT *facet, facetT *neighbor,
    +           facetT **bestfacet, realT *distp, realT *mindistp, realT *maxdistp);
    +facetT *qh_findbestneighbor(qhT *qh, facetT *facet, realT *distp, realT *mindistp, realT *maxdistp);
    +void    qh_flippedmerges(qhT *qh, facetT *facetlist, boolT *wasmerge);
    +void    qh_forcedmerges(qhT *qh, boolT *wasmerge);
    +void    qh_getmergeset(qhT *qh, facetT *facetlist);
    +void    qh_getmergeset_initial(qhT *qh, facetT *facetlist);
    +void    qh_hashridge(qhT *qh, setT *hashtable, int hashsize, ridgeT *ridge, vertexT *oldvertex);
    +ridgeT *qh_hashridge_find(qhT *qh, setT *hashtable, int hashsize, ridgeT *ridge,
    +              vertexT *vertex, vertexT *oldvertex, int *hashslot);
    +void    qh_makeridges(qhT *qh, facetT *facet);
    +void    qh_mark_dupridges(qhT *qh, facetT *facetlist);
    +void    qh_maydropneighbor(qhT *qh, facetT *facet);
    +int     qh_merge_degenredundant(qhT *qh);
    +void    qh_merge_nonconvex(qhT *qh, facetT *facet1, facetT *facet2, mergeType mergetype);
    +void    qh_mergecycle(qhT *qh, facetT *samecycle, facetT *newfacet);
    +void    qh_mergecycle_all(qhT *qh, facetT *facetlist, boolT *wasmerge);
    +void    qh_mergecycle_facets(qhT *qh, facetT *samecycle, facetT *newfacet);
    +void    qh_mergecycle_neighbors(qhT *qh, facetT *samecycle, facetT *newfacet);
    +void    qh_mergecycle_ridges(qhT *qh, facetT *samecycle, facetT *newfacet);
    +void    qh_mergecycle_vneighbors(qhT *qh, facetT *samecycle, facetT *newfacet);
    +void    qh_mergefacet(qhT *qh, facetT *facet1, facetT *facet2, realT *mindist, realT *maxdist, boolT mergeapex);
    +void    qh_mergefacet2d(qhT *qh, facetT *facet1, facetT *facet2);
    +void    qh_mergeneighbors(qhT *qh, facetT *facet1, facetT *facet2);
    +void    qh_mergeridges(qhT *qh, facetT *facet1, facetT *facet2);
    +void    qh_mergesimplex(qhT *qh, facetT *facet1, facetT *facet2, boolT mergeapex);
    +void    qh_mergevertex_del(qhT *qh, vertexT *vertex, facetT *facet1, facetT *facet2);
    +void    qh_mergevertex_neighbors(qhT *qh, facetT *facet1, facetT *facet2);
    +void    qh_mergevertices(qhT *qh, setT *vertices1, setT **vertices);
    +setT   *qh_neighbor_intersections(qhT *qh, vertexT *vertex);
    +void    qh_newvertices(qhT *qh, setT *vertices);
    +boolT   qh_reducevertices(qhT *qh);
    +vertexT *qh_redundant_vertex(qhT *qh, vertexT *vertex);
    +boolT   qh_remove_extravertices(qhT *qh, facetT *facet);
    +vertexT *qh_rename_sharedvertex(qhT *qh, vertexT *vertex, facetT *facet);
    +void    qh_renameridgevertex(qhT *qh, ridgeT *ridge, vertexT *oldvertex, vertexT *newvertex);
    +void    qh_renamevertex(qhT *qh, vertexT *oldvertex, vertexT *newvertex, setT *ridges,
    +                        facetT *oldfacet, facetT *neighborA);
    +boolT   qh_test_appendmerge(qhT *qh, facetT *facet, facetT *neighbor);
    +boolT   qh_test_vneighbors(qhT *qh /* qh.newfacet_list */);
    +void    qh_tracemerge(qhT *qh, facetT *facet1, facetT *facet2);
    +void    qh_tracemerging(qhT *qh);
    +void    qh_updatetested(qhT *qh, facetT *facet1, facetT *facet2);
    +setT   *qh_vertexridges(qhT *qh, vertexT *vertex);
    +void    qh_vertexridges_facet(qhT *qh, vertexT *vertex, facetT *facet, setT **ridges);
    +void    qh_willdelete(qhT *qh, facetT *facet, facetT *replace);
    +
    +#ifdef __cplusplus
    +} /* extern "C" */
    +#endif
    +
    +#endif /* qhDEFmerge */
    diff --git a/xs/src/qhull/src/libqhull_r/poly2_r.c b/xs/src/qhull/src/libqhull_r/poly2_r.c
    new file mode 100644
    index 000000000..b8ae9af9f
    --- /dev/null
    +++ b/xs/src/qhull/src/libqhull_r/poly2_r.c
    @@ -0,0 +1,3222 @@
    +/*
      ---------------------------------
    +
    +   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 ==========*/
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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
    +
    +/*---------------------------------
    +
    +  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
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 locate a facet with qh_findbestfacet()
    +
    +    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:
    +    qh_findbest
    +*/
    +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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  qh_nearcoplanar()
    +    for all facets, remove near-inside points from facet->coplanarset
    +    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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +   /*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  qh_vertexsubset( vertexsetA, vertexsetB )
    +    returns True if vertexsetA is a subset of vertexsetB
    +    assumes vertexsets are sorted
    +
    +  note:
    +    empty set is a subset of any other set
    +*/
    +boolT qh_vertexsubset(setT *vertexsetA, setT *vertexsetB) {
    +  vertexT **vertexA= (vertexT **) SETaddr_(vertexsetA, vertexT);
    +  vertexT **vertexB= (vertexT **) SETaddr_(vertexsetB, vertexT);
    +
    +  while (True) {
    +    if (!*vertexA)
    +      return True;
    +    if (!*vertexB)
    +      return False;
    +    if ((*vertexA)->id > (*vertexB)->id)
    +      return False;
    +    if (*vertexA  == *vertexB)
    +      vertexA++;
    +    vertexB++;
    +  }
    +  return False; /* avoid warnings */
    +} /* vertexsubset */
    diff --git a/xs/src/qhull/src/libqhull_r/poly_r.c b/xs/src/qhull/src/libqhull_r/poly_r.c
    new file mode 100644
    index 000000000..e5b479743
    --- /dev/null
    +++ b/xs/src/qhull/src/libqhull_r/poly_r.c
    @@ -0,0 +1,1205 @@
    +/*
      ---------------------------------
    +
    +   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 ==========*/
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +
    +/*---------------------------------
    +
    +  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; newskiphull_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 */
    +
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +
    +/*---------------------------------
    +
    +  qh_updatevertices()
    +    update vertex neighbors and delete interior vertices
    +
    +  returns:
    +    if qh.VERTEXneighbors, updates neighbors for each vertex
    +      if qh.newvertex_list,
    +         removes visible neighbors  from vertex neighbors
    +      if qh.newfacet_list
    +         adds new facets to vertex neighbors
    +    if qh.visible_list
    +       interior vertices added to qh.del_vertices for later partitioning
    +
    +  design:
    +    if qh.VERTEXneighbors
    +      deletes references to visible facets from vertex neighbors
    +      appends new facets to the neighbor list for each vertex
    +      checks all vertices of visible facets
    +        removes visible facets from neighbor lists
    +        marks unused vertices for deletion
    +*/
    +void qh_updatevertices(qhT *qh /*qh.newvertex_list, newfacet_list, visible_list*/) {
    +  facetT *newfacet= NULL, *neighbor, **neighborp, *visible;
    +  vertexT *vertex, **vertexp;
    +
    +  trace3((qh, qh->ferr, 3013, "qh_updatevertices: delete interior vertices and update vertex->neighbors\n"));
    +  if (qh->VERTEXneighbors) {
    +    FORALLvertex_(qh->newvertex_list) {
    +      FOREACHneighbor_(vertex) {
    +        if (neighbor->visible)
    +          SETref_(neighbor)= NULL;
    +      }
    +      qh_setcompact(qh, vertex->neighbors);
    +    }
    +    FORALLnew_facets {
    +      FOREACHvertex_(newfacet->vertices)
    +        qh_setappend(qh, &vertex->neighbors, newfacet);
    +    }
    +    FORALLvisible_facets {
    +      FOREACHvertex_(visible->vertices) {
    +        if (!vertex->newlist && !vertex->deleted) {
    +          FOREACHneighbor_(vertex) { /* this can happen under merging */
    +            if (!neighbor->visible)
    +              break;
    +          }
    +          if (neighbor)
    +            qh_setdel(vertex->neighbors, visible);
    +          else {
    +            vertex->deleted= True;
    +            qh_setappend(qh, &qh->del_vertices, vertex);
    +            trace2((qh, qh->ferr, 2041, "qh_updatevertices: delete vertex p%d(v%d) in f%d\n",
    +                  qh_pointid(qh, vertex->point), vertex->id, visible->id));
    +          }
    +        }
    +      }
    +    }
    +  }else {  /* !VERTEXneighbors */
    +    FORALLvisible_facets {
    +      FOREACHvertex_(visible->vertices) {
    +        if (!vertex->newlist && !vertex->deleted) {
    +          vertex->deleted= True;
    +          qh_setappend(qh, &qh->del_vertices, vertex);
    +          trace2((qh, qh->ferr, 2042, "qh_updatevertices: delete vertex p%d(v%d) in f%d\n",
    +                  qh_pointid(qh, vertex->point), vertex->id, visible->id));
    +        }
    +      }
    +    }
    +  }
    +} /* updatevertices */
    +
    +
    +
    diff --git a/xs/src/qhull/src/libqhull_r/poly_r.h b/xs/src/qhull/src/libqhull_r/poly_r.h
    new file mode 100644
    index 000000000..c71511bd6
    --- /dev/null
    +++ b/xs/src/qhull/src/libqhull_r/poly_r.h
    @@ -0,0 +1,303 @@
    +/*
      ---------------------------------
    +
    +   poly_r.h
    +   header file for poly_r.c and poly2_r.c
    +
    +   see qh-poly_r.htm, libqhull_r.h and poly_r.c
    +
    +   Copyright (c) 1993-2015 The Geometry Center.
    +   $Id: //main/2015/qhull/src/libqhull_r/poly_r.h#5 $$Change: 2079 $
    +   $DateTime: 2016/02/07 17:43:34 $$Author: bbarber $
    +*/
    +
    +#ifndef qhDEFpoly
    +#define qhDEFpoly 1
    +
    +#include "libqhull_r.h"
    +
    +/*===============   constants ========================== */
    +
    +/*----------------------------------
    +
    +  ALGORITHMfault
    +    use as argument to checkconvex() to report errors during buildhull
    +*/
    +#define qh_ALGORITHMfault 0
    +
    +/*----------------------------------
    +
    +  DATAfault
    +    use as argument to checkconvex() to report errors during initialhull
    +*/
    +#define qh_DATAfault 1
    +
    +/*----------------------------------
    +
    +  DUPLICATEridge
    +    special value for facet->neighbor to indicate a duplicate ridge
    +
    +  notes:
    +    set by matchneighbor, used by matchmatch and mark_dupridge
    +*/
    +#define qh_DUPLICATEridge (facetT *)1L
    +
    +/*----------------------------------
    +
    +  MERGEridge       flag in facet
    +    special value for facet->neighbor to indicate a merged ridge
    +
    +  notes:
    +    set by matchneighbor, used by matchmatch and mark_dupridge
    +*/
    +#define qh_MERGEridge (facetT *)2L
    +
    +
    +/*============ -structures- ====================*/
    +
    +/*=========== -macros- =========================*/
    +
    +/*----------------------------------
    +
    +  FORALLfacet_( facetlist ) { ... }
    +    assign 'facet' to each facet in facetlist
    +
    +  notes:
    +    uses 'facetT *facet;'
    +    assumes last facet is a sentinel
    +
    +  see:
    +    FORALLfacets
    +*/
    +#define FORALLfacet_( facetlist ) if (facetlist ) for ( facet=( facetlist ); facet && facet->next; facet= facet->next )
    +
    +/*----------------------------------
    +
    +  FORALLnew_facets { ... }
    +    assign 'newfacet' to each facet in qh.newfacet_list
    +
    +  notes:
    +    uses 'facetT *newfacet;'
    +    at exit, newfacet==NULL
    +*/
    +#define FORALLnew_facets for ( newfacet=qh->newfacet_list;newfacet && newfacet->next;newfacet=newfacet->next )
    +
    +/*----------------------------------
    +
    +  FORALLvertex_( vertexlist ) { ... }
    +    assign 'vertex' to each vertex in vertexlist
    +
    +  notes:
    +    uses 'vertexT *vertex;'
    +    at exit, vertex==NULL
    +*/
    +#define FORALLvertex_( vertexlist ) for (vertex=( vertexlist );vertex && vertex->next;vertex= vertex->next )
    +
    +/*----------------------------------
    +
    +  FORALLvisible_facets { ... }
    +    assign 'visible' to each visible facet in qh.visible_list
    +
    +  notes:
    +    uses 'vacetT *visible;'
    +    at exit, visible==NULL
    +*/
    +#define FORALLvisible_facets for (visible=qh->visible_list; visible && visible->visible; visible= visible->next)
    +
    +/*----------------------------------
    +
    +  FORALLsame_( newfacet ) { ... }
    +    assign 'same' to each facet in newfacet->f.samecycle
    +
    +  notes:
    +    uses 'facetT *same;'
    +    stops when it returns to newfacet
    +*/
    +#define FORALLsame_(newfacet) for (same= newfacet->f.samecycle; same != newfacet; same= same->f.samecycle)
    +
    +/*----------------------------------
    +
    +  FORALLsame_cycle_( newfacet ) { ... }
    +    assign 'same' to each facet in newfacet->f.samecycle
    +
    +  notes:
    +    uses 'facetT *same;'
    +    at exit, same == NULL
    +*/
    +#define FORALLsame_cycle_(newfacet) \
    +     for (same= newfacet->f.samecycle; \
    +         same; same= (same == newfacet ?  NULL : same->f.samecycle))
    +
    +/*----------------------------------
    +
    +  FOREACHneighborA_( facet ) { ... }
    +    assign 'neighborA' to each neighbor in facet->neighbors
    +
    +  FOREACHneighborA_( vertex ) { ... }
    +    assign 'neighborA' to each neighbor in vertex->neighbors
    +
    +  declare:
    +    facetT *neighborA, **neighborAp;
    +
    +  see:
    +    FOREACHsetelement_
    +*/
    +#define FOREACHneighborA_(facet)  FOREACHsetelement_(facetT, facet->neighbors, neighborA)
    +
    +/*----------------------------------
    +
    +  FOREACHvisible_( facets ) { ... }
    +    assign 'visible' to each facet in facets
    +
    +  notes:
    +    uses 'facetT *facet, *facetp;'
    +    see FOREACHsetelement_
    +*/
    +#define FOREACHvisible_(facets) FOREACHsetelement_(facetT, facets, visible)
    +
    +/*----------------------------------
    +
    +  FOREACHnewfacet_( facets ) { ... }
    +    assign 'newfacet' to each facet in facets
    +
    +  notes:
    +    uses 'facetT *newfacet, *newfacetp;'
    +    see FOREACHsetelement_
    +*/
    +#define FOREACHnewfacet_(facets) FOREACHsetelement_(facetT, facets, newfacet)
    +
    +/*----------------------------------
    +
    +  FOREACHvertexA_( vertices ) { ... }
    +    assign 'vertexA' to each vertex in vertices
    +
    +  notes:
    +    uses 'vertexT *vertexA, *vertexAp;'
    +    see FOREACHsetelement_
    +*/
    +#define FOREACHvertexA_(vertices) FOREACHsetelement_(vertexT, vertices, vertexA)
    +
    +/*----------------------------------
    +
    +  FOREACHvertexreverse12_( vertices ) { ... }
    +    assign 'vertex' to each vertex in vertices
    +    reverse order of first two vertices
    +
    +  notes:
    +    uses 'vertexT *vertex, *vertexp;'
    +    see FOREACHsetelement_
    +*/
    +#define FOREACHvertexreverse12_(vertices) FOREACHsetelementreverse12_(vertexT, vertices, vertex)
    +
    +
    +/*=============== prototypes poly_r.c in alphabetical order ================*/
    +
    +#ifdef __cplusplus
    +extern "C" {
    +#endif
    +
    +void    qh_appendfacet(qhT *qh, facetT *facet);
    +void    qh_appendvertex(qhT *qh, vertexT *vertex);
    +void    qh_attachnewfacets(qhT *qh /* qh.visible_list, qh.newfacet_list */);
    +boolT   qh_checkflipped(qhT *qh, facetT *facet, realT *dist, boolT allerror);
    +void    qh_delfacet(qhT *qh, facetT *facet);
    +void    qh_deletevisible(qhT *qh /* qh.visible_list, qh.horizon_list */);
    +setT   *qh_facetintersect(qhT *qh, facetT *facetA, facetT *facetB, int *skipAp,int *skipBp, int extra);
    +int     qh_gethash(qhT *qh, int hashsize, setT *set, int size, int firstindex, void *skipelem);
    +facetT *qh_makenewfacet(qhT *qh, setT *vertices, boolT toporient, facetT *facet);
    +void    qh_makenewplanes(qhT *qh /* qh.newfacet_list */);
    +facetT *qh_makenew_nonsimplicial(qhT *qh, facetT *visible, vertexT *apex, int *numnew);
    +facetT *qh_makenew_simplicial(qhT *qh, facetT *visible, vertexT *apex, int *numnew);
    +void    qh_matchneighbor(qhT *qh, facetT *newfacet, int newskip, int hashsize,
    +                          int *hashcount);
    +void    qh_matchnewfacets(qhT *qh);
    +boolT   qh_matchvertices(qhT *qh, int firstindex, setT *verticesA, int skipA,
    +                          setT *verticesB, int *skipB, boolT *same);
    +facetT *qh_newfacet(qhT *qh);
    +ridgeT *qh_newridge(qhT *qh);
    +int     qh_pointid(qhT *qh, pointT *point);
    +void    qh_removefacet(qhT *qh, facetT *facet);
    +void    qh_removevertex(qhT *qh, vertexT *vertex);
    +void    qh_updatevertices(qhT *qh);
    +
    +
    +/*========== -prototypes poly2_r.c in alphabetical order ===========*/
    +
    +void    qh_addhash(void *newelem, setT *hashtable, int hashsize, int hash);
    +void    qh_check_bestdist(qhT *qh);
    +void    qh_check_dupridge(qhT *qh, facetT *facet1, realT dist1, facetT *facet2, realT dist2);
    +void    qh_check_maxout(qhT *qh);
    +void    qh_check_output(qhT *qh);
    +void    qh_check_point(qhT *qh, pointT *point, facetT *facet, realT *maxoutside, realT *maxdist, facetT **errfacet1, facetT **errfacet2);
    +void    qh_check_points(qhT *qh);
    +void    qh_checkconvex(qhT *qh, facetT *facetlist, int fault);
    +void    qh_checkfacet(qhT *qh, facetT *facet, boolT newmerge, boolT *waserrorp);
    +void    qh_checkflipped_all(qhT *qh, facetT *facetlist);
    +void    qh_checkpolygon(qhT *qh, facetT *facetlist);
    +void    qh_checkvertex(qhT *qh, vertexT *vertex);
    +void    qh_clearcenters(qhT *qh, qh_CENTER type);
    +void    qh_createsimplex(qhT *qh, setT *vertices);
    +void    qh_delridge(qhT *qh, ridgeT *ridge);
    +void    qh_delvertex(qhT *qh, vertexT *vertex);
    +setT   *qh_facet3vertex(qhT *qh, facetT *facet);
    +facetT *qh_findbestfacet(qhT *qh, pointT *point, boolT bestoutside,
    +           realT *bestdist, boolT *isoutside);
    +facetT *qh_findbestlower(qhT *qh, facetT *upperfacet, pointT *point, realT *bestdistp, int *numpart);
    +facetT *qh_findfacet_all(qhT *qh, pointT *point, realT *bestdist, boolT *isoutside,
    +                          int *numpart);
    +int     qh_findgood(qhT *qh, facetT *facetlist, int goodhorizon);
    +void    qh_findgood_all(qhT *qh, facetT *facetlist);
    +void    qh_furthestnext(qhT *qh /* qh.facet_list */);
    +void    qh_furthestout(qhT *qh, facetT *facet);
    +void    qh_infiniteloop(qhT *qh, facetT *facet);
    +void    qh_initbuild(qhT *qh);
    +void    qh_initialhull(qhT *qh, setT *vertices);
    +setT   *qh_initialvertices(qhT *qh, int dim, setT *maxpoints, pointT *points, int numpoints);
    +vertexT *qh_isvertex(pointT *point, setT *vertices);
    +vertexT *qh_makenewfacets(qhT *qh, pointT *point /*horizon_list, visible_list*/);
    +void    qh_matchduplicates(qhT *qh, facetT *atfacet, int atskip, int hashsize, int *hashcount);
    +void    qh_nearcoplanar(qhT *qh /* qh.facet_list */);
    +vertexT *qh_nearvertex(qhT *qh, facetT *facet, pointT *point, realT *bestdistp);
    +int     qh_newhashtable(qhT *qh, int newsize);
    +vertexT *qh_newvertex(qhT *qh, pointT *point);
    +ridgeT *qh_nextridge3d(ridgeT *atridge, facetT *facet, vertexT **vertexp);
    +void    qh_outcoplanar(qhT *qh /* qh.facet_list */);
    +pointT *qh_point(qhT *qh, int id);
    +void    qh_point_add(qhT *qh, setT *set, pointT *point, void *elem);
    +setT   *qh_pointfacet(qhT *qh /*qh.facet_list*/);
    +setT   *qh_pointvertex(qhT *qh /*qh.facet_list*/);
    +void    qh_prependfacet(qhT *qh, facetT *facet, facetT **facetlist);
    +void    qh_printhashtable(qhT *qh, FILE *fp);
    +void    qh_printlists(qhT *qh);
    +void    qh_resetlists(qhT *qh, boolT stats, boolT resetVisible /*qh.newvertex_list qh.newfacet_list qh.visible_list*/);
    +void    qh_setvoronoi_all(qhT *qh);
    +void    qh_triangulate(qhT *qh /*qh.facet_list*/);
    +void    qh_triangulate_facet(qhT *qh, facetT *facetA, vertexT **first_vertex);
    +void    qh_triangulate_link(qhT *qh, facetT *oldfacetA, facetT *facetA, facetT *oldfacetB, facetT *facetB);
    +void    qh_triangulate_mirror(qhT *qh, facetT *facetA, facetT *facetB);
    +void    qh_triangulate_null(qhT *qh, facetT *facetA);
    +void    qh_vertexintersect(qhT *qh, setT **vertexsetA,setT *vertexsetB);
    +setT   *qh_vertexintersect_new(qhT *qh, setT *vertexsetA,setT *vertexsetB);
    +void    qh_vertexneighbors(qhT *qh /*qh.facet_list*/);
    +boolT   qh_vertexsubset(setT *vertexsetA, setT *vertexsetB);
    +
    +#ifdef __cplusplus
    +} /* extern "C" */
    +#endif
    +
    +#endif /* qhDEFpoly */
    diff --git a/xs/src/qhull/src/libqhull_r/qh-geom_r.htm b/xs/src/qhull/src/libqhull_r/qh-geom_r.htm
    new file mode 100644
    index 000000000..eeefc0c75
    --- /dev/null
    +++ b/xs/src/qhull/src/libqhull_r/qh-geom_r.htm
    @@ -0,0 +1,295 @@
    +
    +
    +
    +
    +geom_r.c, geom2_r.c -- geometric and floating point routines
    +
    +
    +
    +
    +

    Up: Home page for Qhull
    +Up: Qhull manual: Table of Contents
    +Up: Programs +• Options +• Output +• Formats +• Geomview +• Print +• Qhull +• Precision +• Trace +• Functions
    +Up: Qhull code: Table of Contents
    +To: Qhull functions, macros, and data structures
    +To: GeomGlobal +• IoMem +• MergePoly +• QhullSet +• StatUser +

    + +
    + + +

    geom_r.c, geom2_r.c, random_r.c -- geometric and floating point routines

    +
    +

    Geometrically, a vertex is a point with d coordinates +and a facet is a halfspace. A halfspace is defined by an +oriented hyperplane through the facet's vertices. A hyperplane +is defined by d normalized coefficients and an offset. A +point is above a facet if its distance to the facet is +positive.

    + +

    Qhull uses floating point coordinates for input points, +vertices, halfspace equations, centrums, and an interior point.

    + +

    Qhull may be configured for single precision or double +precision floating point arithmetic (see realT +).

    + +

    Each floating point operation may incur round-off error (see +Merge). The maximum error for distance +computations is determined at initialization. The roundoff error +in halfspace computation is accounted for by computing the +distance from vertices to the halfspace.

    +
    +

    Copyright © 1995-2015 C.B. Barber

    +
    +

    » Geom + Global • +IoMem • +MergePoly • +QhullSet • +StatUser

    + +

    Index to geom_r.c, +geom2_r.c, geom_r.h, +random_r.c, random_r.h +

    + + + +

    »geometric data types +and constants

    + +
      +
    • coordT coordinates and +coefficients are stored as realT
    • +
    • pointT a point is an array +of DIM3 coordinates
    • +
    + +

    »mathematical macros

    + +
      +
    • fabs_ returns the absolute +value of a
    • +
    • fmax_ returns the maximum +value of a and b
    • +
    • fmin_ returns the minimum +value of a and b
    • +
    • maximize_ maximize a value +
    • +
    • minimize_ minimize a value +
    • +
    • det2_ compute a 2-d +determinate
    • +
    • det3_ compute a 3-d +determinate
    • +
    • dX, dY, dZ compute the difference +between two coordinates
    • +
    + +

    »mathematical functions

    + + + +

    »computational geometry functions

    + + + +

    »point array functions

    + + +

    »geometric facet functions

    + + +

    »geometric roundoff functions

    +
      +
    • qh_detjoggle determine +default joggle for points and distance roundoff error
    • +
    • qh_detroundoff +determine maximum roundoff error and other precision constants
    • +
    • qh_distround compute +maximum roundoff error due to a distance computation to a +normalized hyperplane
    • +
    • qh_divzero divide by a +number that is nearly zero
    • +
    • qh_maxouter return maximum outer +plane
    • +
    • qh_outerinner return actual +outer and inner planes +
    + +

    +
    +

    Up: +Home page for +Qhull
    +Up: Qhull manual: Table of Contents
    +Up: Programs +• Options +• Output +• Formats +• Geomview +• Print +• Qhull +• Precision +• Trace +• Functions
    +Up: Qhull code: Table of Contents
    +To: Qhull functions, macros, and data structures
    +To: Geom • +GlobalIo +• MemMerge +• PolyQhull +• SetStat +• User
    + + +

    +
    +

    The +Geometry Center Home Page

    +

    Comments to: qhull@qhull.org +
    +Created: May 2, 1997 --- Last modified: see top

    + + diff --git a/xs/src/qhull/src/libqhull_r/qh-globa_r.htm b/xs/src/qhull/src/libqhull_r/qh-globa_r.htm new file mode 100644 index 000000000..45437a059 --- /dev/null +++ b/xs/src/qhull/src/libqhull_r/qh-globa_r.htm @@ -0,0 +1,163 @@ + + + + +global_r.c -- global variables and their functions + + + + +

    Up: Home page for Qhull
    +Up: Qhull manual: Table of Contents
    +Up: Programs +• Options +• Output +• Formats +• Geomview +• Print +• Qhull +• Precision +• Trace +• Functions
    +Up: Qhull code: Table of Contents
    +To: Qhull functions, macros, and data structures
    +To: GeomGlobal +• IoMem +• MergePoly +• QhullSet +• StatUser +

    + +
    + + +

    global_r.c -- global variables and their functions

    +
    +

    Qhull uses a data structure, qhT, to store +globally defined constants, lists, sets, and variables. It is passed as the +first argument to most functions. +

    +
    +

    Copyright © 1995-2015 C.B. Barber

    +
    +

    » Geom + Global • +IoMem • +MergePoly • +QhullSet • +StatUser

    + +

    Index to global_r.c and +libqhull_r.h

    + + + +

    »Qhull's global +variables

    + + + +

    »Global variable and +initialization routines

    + + + +

    +
    +

    Up: +Home page for +Qhull
    +Up: Qhull manual: Table of Contents
    +Up: Programs +• Options +• Output +• Formats +• Geomview +• Print +• Qhull +• Precision +• Trace +• Functions
    +Up: Qhull code: Table of Contents
    +To: Qhull functions, macros, and data structures
    +To: Geom • +GlobalIo +• MemMerge +• PolyQhull +• SetStat +• User
    +

    +
    +

    The +Geometry Center Home Page

    +

    Comments to: qhull@qhull.org +
    +Created: May 2, 1997 --- Last modified: see top

    + + diff --git a/xs/src/qhull/src/libqhull_r/qh-io_r.htm b/xs/src/qhull/src/libqhull_r/qh-io_r.htm new file mode 100644 index 000000000..8a8a96300 --- /dev/null +++ b/xs/src/qhull/src/libqhull_r/qh-io_r.htm @@ -0,0 +1,305 @@ + + + + +io_r.c -- input and output operations + + + + +

    Up: Home page for Qhull
    +Up: Qhull manual: Table of Contents
    +Up: Programs +• Options +• Output +• Formats +• Geomview +• Print +• Qhull +• Precision +• Trace +• Functions
    +Up: Qhull code: Table of Contents
    +To: Qhull functions, macros, and data structures
    +To: GeomGlobal +• IoMem +• MergePoly +• QhullSet +• StatUser +

    +
    + +

    io_r.c -- input and output operations

    +
    + +

    Qhull provides a wide range of input +and output options. To organize the code, most output formats use +the same driver:

    + +
    +    qh_printbegin( fp, format, facetlist, facets, printall );
    +
    +    FORALLfacet_( facetlist )
    +      qh_printafacet( fp, format, facet, printall );
    +
    +    FOREACHfacet_( facets )
    +      qh_printafacet( fp, format, facet, printall );
    +
    +    qh_printend( fp, format );
    +
    + +

    Note the 'printall' flag. It selects whether or not +qh_skipfacet() is tested.

    + +
    +

    Copyright © 1995-2015 C.B. Barber

    +
    +

    » Geom +GlobalIo • +MemMerge • +PolyQhull • +SetStat • +User

    + +

    Index to io_r.c and io_r.h

    + + + +

    »io_r.h constants and types

    + +
      +
    • qh_MAXfirst maximum length +of first two lines of stdin
    • +
    • qh_WHITESPACE possible +values of white space
    • +
    • printvridgeT function to +print results of qh_printvdiagram or qh_eachvoronoi
    • +
    + +

    »User level functions

    + + + +

    »Print functions for all +output formats

    + + + +

    »Text output functions

    + + +

    »Text utility functions

    + + +

    »Geomview output functions

    + +

    »Geomview utility functions

    + +

    +

    +
    +

    Up: +Home page for +Qhull
    +Up: Qhull manual: Table of Contents
    +Up: Programs +• Options +• Output +• Formats +• Geomview +• Print +• Qhull +• Precision +• Trace +• Functions
    +Up: Qhull code: Table of Contents
    +To: Qhull functions, macros, and data structures
    +To: Geom • +GlobalIo +• MemMerge +• PolyQhull +• SetStat +• User
    +

    +

    +
    +

    The +Geometry Center Home Page

    +

    Comments to: qhull@qhull.org +
    +Created: May 2, 1997 --- Last modified: see top

    + + diff --git a/xs/src/qhull/src/libqhull_r/qh-mem_r.htm b/xs/src/qhull/src/libqhull_r/qh-mem_r.htm new file mode 100644 index 000000000..db59119cb --- /dev/null +++ b/xs/src/qhull/src/libqhull_r/qh-mem_r.htm @@ -0,0 +1,145 @@ + + + + +mem_r.c -- memory operations + + + + +

    Up: Home page for Qhull
    +Up: Qhull manual: Table of Contents
    +Up: Programs +• Options +• Output +• Formats +• Geomview +• Print +• Qhull +• Precision +• Trace +• Functions
    +Up: Qhull code: Table of Contents
    +To: Qhull functions, macros, and data structures
    +To: GeomGlobal +• IoMem +• MergePoly +• QhullSet +• StatUser +

    +
    + +

    mem_r.c -- memory operations

    +
    +

    Qhull uses quick-fit memory allocation. It maintains a +set of free lists for a variety of small allocations. A +small request returns a block from the best fitting free +list. If the free list is empty, Qhull allocates a block +from a reserved buffer.

    +

    Use 'T5' to trace memory allocations.

    + +
    +

    Copyright © 1995-2015 C.B. Barber

    +
    +

    » Geom + Global • +IoMem +• MergePoly +• QhullSet +• StatUser +

    +

    Index to mem_r.c and +mem_r.h

    + +

    »mem_r.h data types and constants

    +
      +
    • ptr_intT for casting +a void* to an integer-type
    • +
    • qhmemT global memory +structure for mem_r.c
    • +
    • qh_NOmem disable memory allocation
    • +
    +

    »mem_r.h macros

    + +

    »User level +functions

    + + +

    »Initialization and +termination functions

    + + +

    +
    +

    Up: +Home page for +Qhull
    +Up: Qhull manual: Table of Contents
    +Up: Programs +• Options +• Output +• Formats +• Geomview +• Print +• Qhull +• Precision +• Trace +• Functions
    +Up: Qhull code: Table of Contents
    +To: Qhull functions, macros, and data structures
    +To: Geom • +GlobalIo +• MemMerge +• PolyQhull +• SetStat +• User
    +

    +

    +
    +

    The +Geometry Center Home Page

    +

    Comments to: qhull@qhull.org +
    +Created: May 2, 1997 --- Last modified: see top

    + + diff --git a/xs/src/qhull/src/libqhull_r/qh-merge_r.htm b/xs/src/qhull/src/libqhull_r/qh-merge_r.htm new file mode 100644 index 000000000..63e5135be --- /dev/null +++ b/xs/src/qhull/src/libqhull_r/qh-merge_r.htm @@ -0,0 +1,366 @@ + + + + +merge_r.c -- facet merge operations + + + + +

    Up: Home page for Qhull
    +Up: Qhull manual: Table of Contents
    +Up: Programs +• Options +• Output +• Formats +• Geomview +• Print +• Qhull +• Precision +• Trace +• Functions
    +Up: Qhull code: Table of Contents
    +To: Qhull functions, macros, and data structures
    +To: GeomGlobal +• IoMem +• MergePoly +• QhullSet +• StatUser +

    +
    + +

    merge_r.c -- facet merge operations

    +
    +

    Qhull handles precision problems by merged facets or joggled input. +Except for redundant vertices, it corrects a problem by +merging two facets. When done, all facets are clearly +convex. See Imprecision in Qhull +for further information.

    +

    Users may joggle the input ('QJn') +instead of merging facets.

    +

    Qhull detects and corrects the following problems:

    +
      +
    • More than two facets meeting at a ridge. When +Qhull creates facets, it creates an even number +of facets for each ridge. A convex hull always +has two facets for each ridge. More than two +facets may be created if non-adjacent facets +share a vertex. This is called a duplicate +ridge. In 2-d, a duplicate ridge would +create a loop of facets.
    • +
    +
      +
    • A facet contained in another facet. Facet +merging may leave all vertices of one facet as a +subset of the vertices of another facet. This is +called a redundant facet.
    • +
    +
      +
    • A facet with fewer than three neighbors. Facet +merging may leave a facet with one or two +neighbors. This is called a degenerate facet. +
    • +
    +
      +
    • A facet with flipped orientation. A +facet's hyperplane may define a halfspace that +does not include the interior point.This is +called a flipped facet.
    • +
    +
      +
    • A coplanar horizon facet. A +newly processed point may be coplanar with an +horizon facet. Qhull creates a new facet without +a hyperplane. It links new facets for the same +horizon facet together. This is called a samecycle. +The new facet or samecycle is merged into the +horizon facet.
    • +
    +
      +
    • Concave facets. A facet's centrum may be +above a neighboring facet. If so, the facets meet +at a concave angle.
    • +
    +
      +
    • Coplanar facets. A facet's centrum may be +coplanar with a neighboring facet (i.e., it is +neither clearly below nor clearly above the +facet's hyperplane). Qhull removes coplanar +facets in independent sets sorted by angle.
    • +
    +
      +
    • Redundant vertex. A vertex may have fewer +than three neighboring facets. If so, it is +redundant and may be renamed to an adjacent +vertex without changing the topological +structure.This is called a redundant vertex. +
    • +
    +
    +

    Copyright © 1995-2015 C.B. Barber

    +
    +

    » Geom + Global +• IoMem +• MergePoly +• QhullSet +• StatUser +

    +

    Index to merge_r.c and +merge_r.h

    + + +

    »merge_r.h data +types, macros, and global sets

    +
      +
    • mergeT structure to +identify a merge of two facets
    • +
    • FOREACHmerge_ +assign 'merge' to each merge in merges
    • +
    • qh global sets +qh.facet_mergeset contains non-convex merges +while qh.degen_mergeset contains degenerate and +redundant facets
    • +
    +

    »merge_r.h +constants

    + +

    »top-level merge +functions

    + + +

    »functions for +identifying merges

    + + +

    »functions for +determining the best merge

    + + +

    »functions for +merging facets

    + + +

    »functions for +merging a cycle of facets

    +

    If a point is coplanar with an horizon facet, the +corresponding new facets are linked together (a samecycle) +for merging.

    + +

    »functions +for renaming a vertex

    + + +

    »functions +for identifying vertices for renaming

    + + +

    »functions for check and +trace

    + + +

    +
    +

    Up: +Home page for +Qhull
    +Up: Qhull manual: Table of Contents
    +Up: Programs +• Options +• Output +• Formats +• Geomview +• Print +• Qhull +• Precision +• Trace +• Functions
    +Up: Qhull code: Table of Contents
    +To: Qhull functions, macros, and data structures
    +To: Geom • +GlobalIo +• MemMerge +• PolyQhull +• SetStat +• User
    +

    +

    +
    +

    The +Geometry Center Home Page

    +

    Comments to: qhull@qhull.org +
    +Created: May 2, 1997 --- Last modified: see top

    + + diff --git a/xs/src/qhull/src/libqhull_r/qh-poly_r.htm b/xs/src/qhull/src/libqhull_r/qh-poly_r.htm new file mode 100644 index 000000000..c5b6f2f83 --- /dev/null +++ b/xs/src/qhull/src/libqhull_r/qh-poly_r.htm @@ -0,0 +1,485 @@ + + + + +poly_r.c, poly2_r.c -- polyhedron operations + + + + +

    Up: Home page for Qhull
    +Up: Qhull manual: Table of Contents
    +Up: Programs +• Options +• Output +• Formats +• Geomview +• Print +• Qhull +• Precision +• Trace +• Functions
    +Up: Qhull code: Table of Contents
    +To: Qhull functions, macros, and data structures
    +To: GeomGlobal +• IoMem +• MergePoly +• QhullSet +• StatUser +

    +
    + +

    poly_r.c, poly2_r.c -- polyhedron operations

    +
    + +

    Qhull uses dimension-free terminology. Qhull builds a +polyhedron in dimension d. A polyhedron is a +simplicial complex of faces with geometric information for the +top and bottom-level faces. A (d-1)-face is a facet, +a (d-2)-face is a ridge, and a 0-face +is a vertex. For example in 3-d, a facet is a polygon +and a ridge is an edge. A facet is built from a ridge (the base) +and a vertex (the apex). See +Qhull's data structures.

    + +

    Qhull's primary data structure is a polyhedron. A +polyhedron is a list of facets. Each facet has a set of +neighboring facets and a set of vertices. Each facet has a +hyperplane. For example, a tetrahedron has four facets. +If its vertices are a, b, c, d, and its facets +are 1, 2, 3, 4, the tetrahedron is

    +
    +
      +
    • facet 1
        +
      • vertices: b c d
      • +
      • neighbors: 2 3 4
      • +
      +
    • +
    • facet 2
        +
      • vertices: a c d
      • +
      • neighbors: 1 3 4
      • +
      +
    • +
    • facet 3
        +
      • vertices: a b d
      • +
      • neighbors: 1 2 4
      • +
      +
    • +
    • facet 4
        +
      • vertices: a b c
      • +
      • neighbors: 1 2 3
      • +
      +
    • +
    +
    +

    A facet may be simplicial or non-simplicial. In 3-d, a +simplicial facet has three vertices and three +neighbors. A nonsimplicial facet has more than +three vertices and more than three neighbors. A +nonsimplicial facet has a set of ridges and a centrum.

    +

    +A simplicial facet has an orientation. An orientation +is either top or bottom. +The flag, facet->toporient, +defines the orientation of the facet's vertices. For example in 3-d, +'top' is left-handed orientation (i.e., the vertex order follows the direction +of the left-hand fingers when the thumb is pointing away from the center). +Except for axis-parallel facets in 5-d and higher, topological orientation +determines the geometric orientation of the facet's hyperplane. + +

    A nonsimplicial facet is due to merging two or more +facets. The facet's ridge set determine a simplicial +decomposition of the facet. Each ridge is a 1-face (i.e., +it has two vertices and two neighboring facets). The +orientation of a ridge is determined by the order of the +neighboring facets. The flag, facet->toporient,is +ignored.

    +

    A nonsimplicial facet has a centrum for testing +convexity. A centrum is a point on the facet's +hyperplane that is near the center of the facet. Except +for large facets, it is the arithmetic average of the +facet's vertices.

    +

    A nonsimplicial facet is an approximation that is +defined by offsets from the facet's hyperplane. When +Qhull finishes, the outer plane is above all +points while the inner plane is below the facet's +vertices. This guarantees that any exact convex hull +passes between the inner and outer planes. The outer +plane is defined by facet->maxoutside while +the inner plane is computed from the facet's vertices.

    + +

    Qhull 3.1 includes triangulation of non-simplicial facets +('Qt'). +These facets, +called tricoplanar, share the same normal. centrum, and Voronoi center. +One facet (keepcentrum) owns these data structures. +While tricoplanar facets are more accurate than the simplicial facets from +joggled input, they +may have zero area or flipped orientation. + +

    +

    Copyright © 1995-2015 C.B. Barber

    +
    +

    » Geom + Global +• IoMem +• MergePoly +• QhullSet +• StatUser +

    +

    Index to poly_r.c, +poly2_r.c, poly_r.h, +and libqhull_r.h

    + +

    »Data +types and global lists for polyhedrons

    + +

    »poly_r.h constants

    +
      +
    • ALGORITHMfault +flag to not report errors in qh_checkconvex()
    • +
    • DATAfault flag to +report errors in qh_checkconvex()
    • +
    • DUPLICATEridge +special value for facet->neighbor to indicate +a duplicate ridge
    • +
    • MERGEridge +special value for facet->neighbor to indicate +a merged ridge
    • +
    +

    »Global FORALL +macros

    + +

    »FORALL macros

    + +

    »FOREACH macros

    + +

    »Indexed +FOREACH macros

    +
      +
    • FOREACHfacet_i_ +assign 'facet' and 'facet_i' to each facet in +facet set
    • +
    • FOREACHneighbor_i_ +assign 'neighbor' and 'neighbor_i' to each facet +in facet->neighbors or vertex->neighbors
    • +
    • FOREACHpoint_i_ +assign 'point' and 'point_i' to each point in +points set
    • +
    • FOREACHridge_i_ +assign 'ridge' and 'ridge_i' to each ridge in +ridges set
    • +
    • FOREACHvertex_i_ +assign 'vertex' and 'vertex_i' to each vertex in +vertices set
    • +
    • FOREACHvertexreverse12_ +assign 'vertex' to each vertex in vertex set; +reverse the order of first two vertices
    • +
    +

    »Other macros for polyhedrons

    +
      +
    • getid_ return ID for +a facet, ridge, or vertex
    • +
    • otherfacet_ +return neighboring facet for a ridge in a facet
    • +
    +

    »Facetlist +functions

    + +

    »Facet +functions

    + +

    »Vertex, +ridge, and point functions

    + +

    »Hashtable functions

    + +

    »Allocation and +deallocation functions

    + +

    »Check +functions

    + + + +

    +
    +

    Up: +Home page for +Qhull
    +Up: Qhull manual: Table of Contents
    +Up: Programs +• Options +• Output +• Formats +• Geomview +• Print +• Qhull +• Precision +• Trace +• Functions
    +Up: Qhull code: Table of Contents
    +To: Qhull functions, macros, and data structures
    +To: Geom • +GlobalIo +• MemMerge +• PolyQhull +• SetStat +• User
    +

    +

    +
    +

    The +Geometry Center Home Page

    +

    Comments to: qhull@qhull.org +
    +Created: May 2, 1997 --- Last modified: see top

    + + diff --git a/xs/src/qhull/src/libqhull_r/qh-qhull_r.htm b/xs/src/qhull/src/libqhull_r/qh-qhull_r.htm new file mode 100644 index 000000000..25d5e4972 --- /dev/null +++ b/xs/src/qhull/src/libqhull_r/qh-qhull_r.htm @@ -0,0 +1,279 @@ + + + + +libqhull_r.c -- top-level functions and basic data types + + + + +

    Up: Home page for Qhull
    +Up: Qhull manual: Table of Contents
    +Up: Programs +• Options +• Output +• Formats +• Geomview +• Print +• Qhull +• Precision +• Trace +• Functions
    +Up: Qhull code: Table of Contents
    +To: Qhull functions, macros, and data structures
    +To: GeomGlobal +• IoMem +• MergePoly +• QhullSet +• StatUser +

    +
    + +

    libqhull_r.c -- top-level functions and basic data types

    +
    +

    Qhull implements the Quickhull algorithm for computing +the convex hull. The Quickhull algorithm combines two +well-known algorithms: the 2-d quickhull algorithm and +the n-d beneath-beyond algorithm. See +Description of Qhull.

    +

    This section provides an index to the top-level +functions and base data types. The top-level header file, libqhull_r.h, +contains prototypes for these functions.

    +
    +

    Copyright © 1995-2015 C.B. Barber

    +
    +

    » Geom + Global +• IoMem +• MergePoly +• QhullSet +• StatUser +

    +

    Index to libqhull_r.c, +libqhull_r.h, and +unix_r.c

    + + +

    »libqhull_r.h and unix_r.c +data types and constants

    +
      +
    • flagT Boolean flag as +a bit
    • +
    • boolT boolean value, +either True or False
    • +
    • CENTERtype to +distinguish facet->center
    • +
    • qh_PRINT output +formats for printing (qh.PRINTout)
    • +
    • qh_ALL argument flag +for selecting everything
    • +
    • qh_ERR Qhull exit +codes for indicating errors
    • +
    • qh_FILEstderr Fake stderr +to distinguish error output from normal output [C++ only]
    • +
    • qh_prompt version and long prompt for Qhull
    • +
    • qh_prompt2 synopsis for Qhull
    • +
    • qh_prompt3 concise prompt for Qhull
    • +
    • qh_version version stamp
    • +
    + +

    »libqhull_r.h other +macros

    +
      +
    • traceN print trace +message if qh.IStracing >= N.
    • +
    • QHULL_UNUSED declare an + unused variable to avoid warnings.
    • +
    + +

    »Quickhull +routines in call order

    + + +

    »Top-level routines for initializing and terminating Qhull (in other modules)

    + + +

    »Top-level routines for reading and modifying the input (in other modules)

    + + +

    »Top-level routines for calling Qhull (in other modules)

    + + +

    »Top-level routines for returning results (in other modules)

    + + +

    »Top-level routines for testing and debugging (in other modules)

    + + +

    +
    +

    Up: +Home page for +Qhull
    +Up: Qhull manual: Table of Contents
    +Up: Programs +• Options +• Output +• Formats +• Geomview +• Print +• Qhull +• Precision +• Trace +• Functions
    +Up: Qhull code: Table of Contents
    +To: Qhull functions, macros, and data structures
    +To: Geom • +GlobalIo +• MemMerge +• PolyQhull +• SetStat +• User
    +

    +

    +
    +

    The +Geometry Center Home Page

    +

    Comments to: qhull@qhull.org +
    +Created: May 2, 1997 --- Last modified: see top

    + + diff --git a/xs/src/qhull/src/libqhull_r/qh-set_r.htm b/xs/src/qhull/src/libqhull_r/qh-set_r.htm new file mode 100644 index 000000000..cf8ab63af --- /dev/null +++ b/xs/src/qhull/src/libqhull_r/qh-set_r.htm @@ -0,0 +1,308 @@ + + + + +qset_r.c -- set data type and operations + + + + +

    Up: Home page for Qhull
    +Up: Qhull manual: Table of Contents
    +Up: Programs +• Options +• Output +• Formats +• Geomview +• Print +• Qhull +• Precision +• Trace +• Functions
    +Up: Qhull code: Table of Contents
    +To: Qhull functions, macros, and data structures
    +To: GeomGlobal +• IoMem +• MergePoly +• QhullSet +• StatUser +

    +
    + +

    qset_r.c -- set data type and operations

    +
    +

    Qhull's data structures are constructed from sets. The +functions and macros in qset_r.c construct, iterate, and +modify these sets. They are the most frequently called +functions in Qhull. For this reason, efficiency is the +primary concern.

    +

    In Qhull, a set is represented by an unordered +array of pointers with a maximum size and a NULL +terminator (setT). +Most sets correspond to mathematical sets +(i.e., the pointers are unique). Some sets are sorted to +enforce uniqueness. Some sets are ordered. For example, +the order of vertices in a ridge determine the ridge's +orientation. If you reverse the order of adjacent +vertices, the orientation reverses. Some sets are not +mathematical sets. They may be indexed as an array and +they may include NULL pointers.

    +

    The most common operation on a set is to iterate its +members. This is done with a 'FOREACH...' macro. Each set +has a custom macro. For example, 'FOREACHvertex_' +iterates over a set of vertices. Each vertex is assigned +to the variable 'vertex' from the pointer 'vertexp'.

    +

    Most sets are constructed by appending elements to the +set. The last element of a set is either NULL or the +index of the terminating NULL for a partially full set. +If a set is full, appending an element copies the set to +a larger array.

    + +
    +

    Copyright © 1995-2015 C.B. Barber

    +
    +

    » Geom + Global • +IoMem • +MergePoly +• QhullSet +• StatUser +

    +

    Index to qset_r.c and +qset_r.h

    + +

    »Data types and +constants

    +
      +
    • SETelemsize size +of a set element in bytes
    • +
    • setT a set with a +maximum size and a current size
    • +
    • qh global sets +global sets for temporary sets, etc.
    • +
    +

    »FOREACH macros

    + +

    »Access and +size macros

    + +

    »Internal macros

    +
      +
    • SETsizeaddr_ +return pointer to end element of a set (indicates +current size)
    • +
    + +

    »address macros

    +
      +
    • SETaddr_ return +address of a set's elements
    • +
    • SETelemaddr_ +return address of the n'th element of a set
    • +
    • SETref_ l_r.h.s. for +modifying the current element in a FOREACH +iteration
    • +
    + +

    »Allocation and +deallocation functions

    + + +

    »Access and +predicate functions

    + + +

    »Add functions

    + + +

    »Check and print functions

    + + +

    »Copy, compact, and zero functions

    + + +

    »Delete functions

    + + +

    »Temporary set functions

    + + +

    +
    +

    Up: +Home page for +Qhull
    +Up: Qhull manual: Table of Contents
    +Up: Programs +• Options +• Output +• Formats +• Geomview +• Print +• Qhull +• Precision +• Trace +• Functions
    +Up: Qhull code: Table of Contents
    +To: Qhull functions, macros, and data structures
    +To: Geom • +GlobalIo +• MemMerge +• PolyQhull +• SetStat +• User
    +

    +

    +
    +

    The +Geometry Center Home Page

    +

    Comments to: qhull@qhull.org +
    +Created: May 2, 1997 --- Last modified: see top

    + + diff --git a/xs/src/qhull/src/libqhull_r/qh-stat_r.htm b/xs/src/qhull/src/libqhull_r/qh-stat_r.htm new file mode 100644 index 000000000..ea9d7fc56 --- /dev/null +++ b/xs/src/qhull/src/libqhull_r/qh-stat_r.htm @@ -0,0 +1,161 @@ + + + + +stat_r.c -- statistical operations + + + + +

    Up: Home page for Qhull
    +Up: Qhull manual: Table of Contents
    +Up: Programs +• Options +• Output +• Formats +• Geomview +• Print +• Qhull +• Precision +• Trace +• Functions
    +Up: Qhull code: Table of Contents
    +To: Qhull functions, macros, and data structures
    +To: GeomGlobal +• IoMem +• MergePoly +• QhullSet +• StatUser +

    +
    + +

    stat_r.c -- statistical operations

    +
    +

    Qhull records many statistics. These functions and +macros make it inexpensive to add a statistic. +

    As with Qhull's global variables, the statistics data structure is +accessed by a macro, 'qhstat'. If qh_QHpointer is defined, the macro +is 'qh_qhstat->', otherwise the macro is 'qh_qhstat.'. +Statistics +may be turned off in user_r.h. If so, all but the 'zz' +statistics are ignored.

    +
    +

    Copyright © 1995-2015 C.B. Barber

    +
    +

    » Geom + Global +• IoMem +• MergePoly +• QhullSet +• StatUser +

    +

    Index to stat_r.c and +stat_r.h

    + + +

    »stat_r.h types

    +
      +
    • intrealT union of +integer and real
    • +
    • qhstat global data +structure for statistics
    • +
    +

    »stat_r.h +constants

    +
      +
    • qh_KEEPstatistics 0 turns off most statistics
    • +
    • Z..., W... integer (Z) and real (W) statistics +
    • +
    • ZZstat Z.../W... statistics that +remain defined if qh_KEEPstatistics=0 +
    • +
    • ztype zdoc, zinc, etc. +for definining statistics
    • +
    +

    »stat_r.h macros

    +
      +
    • MAYdebugx called +frequently for error trapping
    • +
    • zadd_/wadd_ add value +to an integer or real statistic
    • +
    • zdef_ define a +statistic
    • +
    • zinc_ increment an +integer statistic
    • +
    • zmax_/wmax_ update +integer or real maximum statistic
    • +
    • zmin_/wmin_ update +integer or real minimum statistic
    • +
    • zval_/wval_ set or +return value of a statistic
    • +
    + +

    »stat_r.c +functions

    + + +

    +
    +

    Up: +Home page for +Qhull
    +Up: Qhull manual: Table of Contents
    +Up: Programs +• Options +• Output +• Formats +• Geomview +• Print +• Qhull +• Precision +• Trace +• Functions
    +Up: Qhull code: Table of Contents
    +To: Qhull functions, macros, and data structures
    +To: Geom • +GlobalIo +• MemMerge +• PolyQhull +• SetStat +• User
    +

    +

    +
    +

    The +Geometry Center Home Page

    +

    Comments to: qhull@qhull.org +
    +Created: May 2, 1997 --- Last modified: see top

    + + diff --git a/xs/src/qhull/src/libqhull_r/qh-user_r.htm b/xs/src/qhull/src/libqhull_r/qh-user_r.htm new file mode 100644 index 000000000..909fec656 --- /dev/null +++ b/xs/src/qhull/src/libqhull_r/qh-user_r.htm @@ -0,0 +1,271 @@ + + + + +user_r.c -- user-definable operations + + + + +

    Up: Home page for Qhull
    +Up: Qhull manual: Table of Contents
    +Up: Programs +• Options +• Output +• Formats +• Geomview +• Print +• Qhull +• Precision +• Trace +• Functions
    +Up: Qhull code: Table of Contents
    +To: Qhull functions, macros, and data structures
    +To: GeomGlobal +• IoMem +• MergePoly +• QhullSet +• StatUser +

    +
    +

    user_r.c -- user-definable operations

    +
    +

    This section contains functions and constants that the +user may want to change.

    + +
    +

    Copyright © 1995-2015 C.B. Barber

    +
    +

    » Geom + Global +• IoMem +• MergePoly +• QhullSet +• StatUser +

    +

    Index to user_r.c, usermem_r.c, userprintf_r.c, userprintf_rbox_r.c and +user_r.h

    + + +

    »Qhull library constants

    + + + +

    »user_r.h data +types and configuration macros

    + + +

    »definition constants

    +
      +
    • qh_DEFAULTbox +define default box size for rbox, 'Qbb', and 'QbB' (Geomview expects 0.5)
    • +
    • qh_INFINITE on +output, indicates Voronoi center at infinity
    • +
    • qh_ORIENTclock +define convention for orienting facets
    • +
    • qh_ZEROdelaunay +define facets that are ignored in Delaunay triangulations
    • +
    + +

    »joggle constants

    + + +

    »performance +related constants

    + + +

    »memory constants

    + + +

    »conditional compilation

    +
      +
    • compiler defined symbols, +e.g., _STDC_ and _cplusplus + +
    • qh_COMPUTEfurthest + compute furthest distance to an outside point instead of storing it with the facet +
    • qh_KEEPstatistics + enable statistic gathering and reporting with option 'Ts' +
    • qh_MAXoutside +record outer plane for each facet +
    • qh_NOmerge +disable facet merging +
    • qh_NOtrace +disable tracing with option 'T4' +
    • qh_QHpointer +access global data with pointer or static structure +
    • qh_QUICKhelp +use abbreviated help messages, e.g., for degenerate inputs +
    + +

    »merge +constants

    + + +

    »user_r.c +functions

    + + +

    »usermem_r.c +functions

    +
      +
    • qh_exit exit program, same as exit(). May be redefined as throw "QH10003.." by libqhullcpp/usermem_r-cpp.cpp
    • +
    • qh_fprintf_stderr print to stderr when qh->ferr is not defined.
    • +
    • qh_free free memory, same as free().
    • +
    • qh_malloc allocate memory, same as malloc()
    • +
    + +

    »userprintf_r.c + and userprintf_rbox,c functions

    +
      +
    • qh_fprintf print +information from Qhull, sames as fprintf().
    • +
    • qh_fprintf_rbox print +information from Rbox, sames as fprintf().
    • +
    + +

    +
    +

    Up: +Home page for +Qhull
    +Up: Qhull manual: Table of Contents
    +Up: Programs +• Options +• Output +• Formats +• Geomview +• Print +• Qhull +• Precision +• Trace +• Functions
    +Up: Qhull code: Table of Contents
    +To: Qhull functions, macros, and data structures
    +To: Geom • +GlobalIo +• MemMerge +• PolyQhull +• SetStat +• User
    +

    +

    +
    +

    The +Geometry Center Home Page

    +

    Comments to: qhull@qhull.org +
    +Created: May 2, 1997 --- Last modified: see top

    + + diff --git a/xs/src/qhull/src/libqhull_r/qhull_r-exports.def b/xs/src/qhull/src/libqhull_r/qhull_r-exports.def new file mode 100644 index 000000000..325d57c3b --- /dev/null +++ b/xs/src/qhull/src/libqhull_r/qhull_r-exports.def @@ -0,0 +1,404 @@ +; qhull_r-exports.def -- msvc module-definition file +; +; Generated from depends.exe by cut-and-paste of exported symbols by mingw gcc +; [jan'14] 391 symbols +; Same as ../libqhullp/qhull-exports.def without DATA items (reentrant) +; +; $Id: //main/2015/qhull/src/libqhull_r/qhull_r-exports.def#3 $$Change: 2047 $ +; $DateTime: 2016/01/04 22:03:18 $$Author: bbarber $ +; +; Define qhull_VERSION in CMakeLists.txt, Makefile, qhull-exports.def, qhull_p-exports.def, qhull_r-exports.def, and qhull-warn.pri +VERSION 7.0 +EXPORTS +qh_addhash +qh_addpoint +qh_all_merges +qh_allstatA +qh_allstatB +qh_allstatC +qh_allstatD +qh_allstatE +qh_allstatE2 +qh_allstatF +qh_allstatG +qh_allstatH +qh_allstatI +qh_allstatistics +qh_appendfacet +qh_appendmergeset +qh_appendprint +qh_appendvertex +qh_argv_to_command +qh_argv_to_command_size +qh_attachnewfacets +qh_backnormal +qh_basevertices +qh_build_withrestart +qh_buildhull +qh_buildtracing +qh_check_bestdist +qh_check_dupridge +qh_check_maxout +qh_check_output +qh_check_point +qh_check_points +qh_checkconnect +qh_checkconvex +qh_checkfacet +qh_checkflags +qh_checkflipped +qh_checkflipped_all +qh_checkpolygon +qh_checkvertex +qh_checkzero +qh_clear_outputflags +qh_clearcenters +qh_clock +qh_collectstatistics +qh_compare_facetarea +qh_compare_facetmerge +qh_compare_facetvisit +qh_compareangle +qh_comparemerge +qh_comparevisit +qh_copyfilename +qh_copynonconvex +qh_copypoints +qh_countfacets +qh_createsimplex +qh_crossproduct +qh_degen_redundant_facet +qh_degen_redundant_neighbors +qh_deletevisible +qh_delfacet +qh_delridge +qh_delvertex +qh_determinant +qh_detjoggle +qh_detroundoff +qh_detsimplex +qh_detvnorm +qh_detvridge +qh_detvridge3 +qh_dfacet +qh_distnorm +qh_distplane +qh_distround +qh_divzero +qh_dvertex +qh_eachvoronoi +qh_eachvoronoi_all +qh_errexit +qh_errexit2 +qh_errexit_rbox +qh_errprint +qh_exit +qh_facet2point +qh_facet3vertex +qh_facetarea +qh_facetarea_simplex +qh_facetcenter +qh_facetintersect +qh_facetvertices +qh_find_newvertex +qh_findbest +qh_findbest_test +qh_findbestfacet +qh_findbesthorizon +qh_findbestlower +qh_findbestneighbor +qh_findbestnew +qh_findfacet_all +qh_findgood +qh_findgood_all +qh_findgooddist +qh_findhorizon +qh_flippedmerges +qh_forcedmerges +qh_fprintf +qh_fprintf_rbox +qh_fprintf_stderr +qh_free +qh_freebuffers +qh_freebuild +qh_freeqhull +qh_furthestnext +qh_furthestout +qh_gausselim +qh_geomplanes +qh_getangle +qh_getarea +qh_getcenter +qh_getcentrum +qh_getdistance +qh_gethash +qh_getmergeset +qh_getmergeset_initial +qh_gram_schmidt +qh_hashridge +qh_hashridge_find +qh_infiniteloop +qh_init_A +qh_init_B +qh_init_qhull_command +qh_initbuild +qh_initflags +qh_initialhull +qh_initialvertices +qh_initqhull_buffers +qh_initqhull_globals +qh_initqhull_mem +qh_initqhull_outputflags +qh_initqhull_start +qh_initqhull_start2 +qh_initstatistics +qh_initthresholds +qh_inthresholds +qh_isvertex +qh_joggleinput +qh_lib_check +qh_makenew_nonsimplicial +qh_makenew_simplicial +qh_makenewfacet +qh_makenewfacets +qh_makenewplanes +qh_makeridges +qh_malloc +qh_mark_dupridges +qh_markkeep +qh_markvoronoi +qh_matchduplicates +qh_matchneighbor +qh_matchnewfacets +qh_matchvertices +qh_maxabsval +qh_maxmin +qh_maxouter +qh_maxsimplex +qh_maydropneighbor +qh_memalloc +qh_memfree +qh_memfreeshort +qh_meminit +qh_meminitbuffers +qh_memsetup +qh_memsize +qh_memstatistics +qh_memtotal +qh_merge_degenredundant +qh_merge_nonconvex +qh_mergecycle +qh_mergecycle_all +qh_mergecycle_facets +qh_mergecycle_neighbors +qh_mergecycle_ridges +qh_mergecycle_vneighbors +qh_mergefacet +qh_mergefacet2d +qh_mergeneighbors +qh_mergeridges +qh_mergesimplex +qh_mergevertex_del +qh_mergevertex_neighbors +qh_mergevertices +qh_minabsval +qh_mindiff +qh_nearcoplanar +qh_nearvertex +qh_neighbor_intersections +qh_new_qhull +qh_newfacet +qh_newhashtable +qh_newridge +qh_newstats +qh_newvertex +qh_newvertices +qh_nextfurthest +qh_nextridge3d +qh_normalize +qh_normalize2 +qh_nostatistic +qh_option +qh_order_vertexneighbors +qh_orientoutside +qh_out1 +qh_out2n +qh_out3n +qh_outcoplanar +qh_outerinner +qh_partitionall +qh_partitioncoplanar +qh_partitionpoint +qh_partitionvisible +qh_point +qh_point_add +qh_pointdist +qh_pointfacet +qh_pointid +qh_pointvertex +qh_postmerge +qh_precision +qh_premerge +qh_prepare_output +qh_prependfacet +qh_printafacet +qh_printallstatistics +qh_printbegin +qh_printcenter +qh_printcentrum +qh_printend +qh_printend4geom +qh_printextremes +qh_printextremes_2d +qh_printextremes_d +qh_printfacet +qh_printfacet2geom +qh_printfacet2geom_points +qh_printfacet2math +qh_printfacet3geom_nonsimplicial +qh_printfacet3geom_points +qh_printfacet3geom_simplicial +qh_printfacet3math +qh_printfacet3vertex +qh_printfacet4geom_nonsimplicial +qh_printfacet4geom_simplicial +qh_printfacetNvertex_nonsimplicial +qh_printfacetNvertex_simplicial +qh_printfacetheader +qh_printfacetlist +qh_printfacetridges +qh_printfacets +qh_printhashtable +qh_printhelp_degenerate +qh_printhelp_narrowhull +qh_printhelp_singular +qh_printhyperplaneintersection +qh_printline3geom +qh_printlists +qh_printmatrix +qh_printneighborhood +qh_printpoint +qh_printpoint3 +qh_printpointid +qh_printpoints +qh_printpoints_out +qh_printpointvect +qh_printpointvect2 +qh_printridge +qh_printspheres +qh_printstatistics +qh_printstatlevel +qh_printstats +qh_printsummary +qh_printvdiagram +qh_printvdiagram2 +qh_printvertex +qh_printvertexlist +qh_printvertices +qh_printvneighbors +qh_printvnorm +qh_printvoronoi +qh_printvridge +qh_produce_output +qh_produce_output2 +qh_projectdim3 +qh_projectinput +qh_projectpoint +qh_projectpoints +qh_qhull +qh_rand +qh_randomfactor +qh_randommatrix +qh_rboxpoints +qh_readfeasible +qh_readpoints +qh_reducevertices +qh_redundant_vertex +qh_remove_extravertices +qh_removefacet +qh_removevertex +qh_rename_sharedvertex +qh_renameridgevertex +qh_renamevertex +qh_resetlists +qh_rotateinput +qh_rotatepoints +qh_roundi +qh_scaleinput +qh_scalelast +qh_scalepoints +qh_setaddnth +qh_setaddsorted +qh_setappend +qh_setappend2ndlast +qh_setappend_set +qh_setcheck +qh_setcompact +qh_setcopy +qh_setdel +qh_setdelaunay +qh_setdellast +qh_setdelnth +qh_setdelnthsorted +qh_setdelsorted +qh_setduplicate +qh_setequal +qh_setequal_except +qh_setequal_skip +qh_setfacetplane +qh_setfeasible +qh_setfree +qh_setfree2 +qh_setfreelong +qh_sethalfspace +qh_sethalfspace_all +qh_sethyperplane_det +qh_sethyperplane_gauss +qh_setin +qh_setindex +qh_setlarger +qh_setlast +qh_setnew +qh_setnew_delnthsorted +qh_setprint +qh_setreplace +qh_setsize +qh_settemp +qh_settempfree +qh_settempfree_all +qh_settemppop +qh_settemppush +qh_settruncate +qh_setunique +qh_setvoronoi_all +qh_setzero +qh_sharpnewfacets +qh_skipfacet +qh_skipfilename +qh_srand +qh_stddev +qh_strtod +qh_strtol +qh_test_appendmerge +qh_test_vneighbors +qh_tracemerge +qh_tracemerging +qh_triangulate +qh_triangulate_facet +qh_triangulate_link +qh_triangulate_mirror +qh_triangulate_null +qh_updatetested +qh_updatevertices +qh_user_memsizes +qh_version +qh_version2 +qh_vertexintersect +qh_vertexintersect_new +qh_vertexneighbors +qh_vertexridges +qh_vertexridges_facet +qh_vertexsubset +qh_voronoi_center +qh_willdelete +qh_zero diff --git a/xs/src/qhull/src/libqhull_r/qhull_ra.h b/xs/src/qhull/src/libqhull_r/qhull_ra.h new file mode 100644 index 000000000..5c5bd8779 --- /dev/null +++ b/xs/src/qhull/src/libqhull_r/qhull_ra.h @@ -0,0 +1,158 @@ +/*
      ---------------------------------
    +
    +   qhull_ra.h
    +   all header files for compiling qhull with reentrant code
    +   included before C++ headers for user_r.h:QHULL_CRTDBG
    +
    +   see qh-qhull.htm
    +
    +   see libqhull_r.h for user-level definitions
    +
    +   see user_r.h for user-definable constants
    +
    +   defines internal functions for libqhull_r.c global_r.c
    +
    +   Copyright (c) 1993-2015 The Geometry Center.
    +   $Id: //main/2015/qhull/src/libqhull_r/qhull_ra.h#6 $$Change: 2079 $
    +   $DateTime: 2016/02/07 17:43:34 $$Author: bbarber $
    +
    +   Notes:  grep for ((" and (" to catch fprintf("lkasdjf");
    +           full parens around (x?y:z)
    +           use '#include "libqhull_r/qhull_ra.h"' to avoid name clashes
    +*/
    +
    +#ifndef qhDEFqhulla
    +#define qhDEFqhulla 1
    +
    +#include "libqhull_r.h"  /* Includes user_r.h and data types */
    +
    +#include "stat_r.h"
    +#include "random_r.h"
    +#include "mem_r.h"
    +#include "qset_r.h"
    +#include "geom_r.h"
    +#include "merge_r.h"
    +#include "poly_r.h"
    +#include "io_r.h"
    +
    +#include 
    +#include 
    +#include 
    +#include     /* some compilers will not need float.h */
    +#include 
    +#include 
    +#include 
    +#include 
    +#include 
    +/*** uncomment here and qset_r.c
    +     if string.h does not define memcpy()
    +#include 
    +*/
    +
    +#if qh_CLOCKtype == 2  /* defined in user_r.h from libqhull_r.h */
    +#include 
    +#include 
    +#include 
    +#endif
    +
    +#ifdef _MSC_VER  /* Microsoft Visual C++ -- warning level 4 */
    +#pragma warning( disable : 4100)  /* unreferenced formal parameter */
    +#pragma warning( disable : 4127)  /* conditional expression is constant */
    +#pragma warning( disable : 4706)  /* assignment within conditional function */
    +#pragma warning( disable : 4996)  /* function was declared deprecated(strcpy, localtime, etc.) */
    +#endif
    +
    +/* ======= -macros- =========== */
    +
    +/*----------------------------------
    +
    +  traceN((qh, qh->ferr, 0Nnnn, "format\n", vars));
    +    calls qh_fprintf if qh.IStracing >= N
    +
    +    Add debugging traps to the end of qh_fprintf
    +
    +  notes:
    +    removing tracing reduces code size but doesn't change execution speed
    +*/
    +#ifndef qh_NOtrace
    +#define trace0(args) {if (qh->IStracing) qh_fprintf args;}
    +#define trace1(args) {if (qh->IStracing >= 1) qh_fprintf args;}
    +#define trace2(args) {if (qh->IStracing >= 2) qh_fprintf args;}
    +#define trace3(args) {if (qh->IStracing >= 3) qh_fprintf args;}
    +#define trace4(args) {if (qh->IStracing >= 4) qh_fprintf args;}
    +#define trace5(args) {if (qh->IStracing >= 5) qh_fprintf args;}
    +#else /* qh_NOtrace */
    +#define trace0(args) {}
    +#define trace1(args) {}
    +#define trace2(args) {}
    +#define trace3(args) {}
    +#define trace4(args) {}
    +#define trace5(args) {}
    +#endif /* qh_NOtrace */
    +
    +/*----------------------------------
    +
    +  Define an unused variable to avoid compiler warnings
    +
    +  Derived from Qt's corelib/global/qglobal.h
    +
    +*/
    +
    +#if defined(__cplusplus) && defined(__INTEL_COMPILER) && !defined(QHULL_OS_WIN)
    +template 
    +inline void qhullUnused(T &x) { (void)x; }
    +#  define QHULL_UNUSED(x) qhullUnused(x);
    +#else
    +#  define QHULL_UNUSED(x) (void)x;
    +#endif
    +
    +#ifdef __cplusplus
    +extern "C" {
    +#endif
    +
    +/***** -libqhull_r.c prototypes (alphabetical after qhull) ********************/
    +
    +void    qh_qhull(qhT *qh);
    +boolT   qh_addpoint(qhT *qh, pointT *furthest, facetT *facet, boolT checkdist);
    +void    qh_buildhull(qhT *qh);
    +void    qh_buildtracing(qhT *qh, pointT *furthest, facetT *facet);
    +void    qh_build_withrestart(qhT *qh);
    +void    qh_errexit2(qhT *qh, int exitcode, facetT *facet, facetT *otherfacet);
    +void    qh_findhorizon(qhT *qh, pointT *point, facetT *facet, int *goodvisible,int *goodhorizon);
    +pointT *qh_nextfurthest(qhT *qh, facetT **visible);
    +void    qh_partitionall(qhT *qh, setT *vertices, pointT *points,int npoints);
    +void    qh_partitioncoplanar(qhT *qh, pointT *point, facetT *facet, realT *dist);
    +void    qh_partitionpoint(qhT *qh, pointT *point, facetT *facet);
    +void    qh_partitionvisible(qhT *qh, boolT allpoints, int *numpoints);
    +void    qh_precision(qhT *qh, const char *reason);
    +void    qh_printsummary(qhT *qh, FILE *fp);
    +
    +/***** -global_r.c internal prototypes (alphabetical) ***********************/
    +
    +void    qh_appendprint(qhT *qh, qh_PRINT format);
    +void    qh_freebuild(qhT *qh, boolT allmem);
    +void    qh_freebuffers(qhT *qh);
    +void    qh_initbuffers(qhT *qh, coordT *points, int numpoints, int dim, boolT ismalloc);
    +
    +/***** -stat_r.c internal prototypes (alphabetical) ***********************/
    +
    +void    qh_allstatA(qhT *qh);
    +void    qh_allstatB(qhT *qh);
    +void    qh_allstatC(qhT *qh);
    +void    qh_allstatD(qhT *qh);
    +void    qh_allstatE(qhT *qh);
    +void    qh_allstatE2(qhT *qh);
    +void    qh_allstatF(qhT *qh);
    +void    qh_allstatG(qhT *qh);
    +void    qh_allstatH(qhT *qh);
    +void    qh_freebuffers(qhT *qh);
    +void    qh_initbuffers(qhT *qh, coordT *points, int numpoints, int dim, boolT ismalloc);
    +
    +#ifdef __cplusplus
    +} /* extern "C" */
    +#endif
    +
    +#endif /* qhDEFqhulla */
    diff --git a/xs/src/qhull/src/libqhull_r/qset_r.c b/xs/src/qhull/src/libqhull_r/qset_r.c
    new file mode 100644
    index 000000000..15cd3c0e2
    --- /dev/null
    +++ b/xs/src/qhull/src/libqhull_r/qset_r.c
    @@ -0,0 +1,1340 @@
    +/*
      ---------------------------------
    +
    +   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 
    +#include 
    +/*** uncomment here and qhull_ra.h
    +     if string.h does not define memcpy()
    +#include 
    +*/
    +
    +#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 ===================*/
    +
    +/*----------------------------------
    +
    +  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 */
    +
    +
    +/*----------------------------------
    +
    +  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 */
    +
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  qh_setzero(qh, set, index, size )
    +    zero elements from index on
    +    set actual size of set to size
    +
    +  notes:
    +    set must be defined
    +    the set becomes an indexed set (can not use FOREACH...)
    +
    +  see also:
    +    qh_settruncate
    +
    +  design:
    +    check index and size
    +    update actual size
    +    zero elements starting at e[index]
    +*/
    +void qh_setzero(qhT *qh, setT *set, int idx, int size) {
    +  int count;
    +
    +  if (idx < 0 || idx >= size || size > set->maxsize) {
    +    qh_fprintf(qh, qh->qhmem.ferr, 6182, "qhull internal error (qh_setzero): index %d or size %d out of bounds for set:\n", idx, size);
    +    qh_setprint(qh, qh->qhmem.ferr, "", set);
    +    qh_errexit(qh, qhmem_ERRqhull, NULL, NULL);
    +  }
    +  set->e[set->maxsize].i=  size+1;  /* may be overwritten */
    +  count= size - idx + 1;   /* +1 for NULL terminator */
    +  memset((char *)SETelemaddr_(set, idx, void), 0, (size_t)count * SETelemsize);
    +} /* setzero */
    +
    +
    diff --git a/xs/src/qhull/src/libqhull_r/qset_r.h b/xs/src/qhull/src/libqhull_r/qset_r.h
    new file mode 100644
    index 000000000..7ba199d6f
    --- /dev/null
    +++ b/xs/src/qhull/src/libqhull_r/qset_r.h
    @@ -0,0 +1,502 @@
    +/*
      ---------------------------------
    +
    +   qset_r.h
    +     header file for qset_r.c that implements set
    +
    +   see qh-set_r.htm and qset_r.c
    +
    +   only uses mem_r.c, malloc/free
    +
    +   for error handling, writes message and calls
    +      qh_errexit(qhT *qh, qhmem_ERRqhull, NULL, NULL);
    +
    +   set operations satisfy the following properties:
    +    - sets have a max size, the actual size (if different) is stored at the end
    +    - every set is NULL terminated
    +    - sets may be sorted or unsorted, the caller must distinguish this
    +
    +   Copyright (c) 1993-2015 The Geometry Center.
    +   $Id: //main/2015/qhull/src/libqhull_r/qset_r.h#4 $$Change: 2079 $
    +   $DateTime: 2016/02/07 17:43:34 $$Author: bbarber $
    +*/
    +
    +#ifndef qhDEFset
    +#define qhDEFset 1
    +
    +#include 
    +
    +/*================= -structures- ===============*/
    +
    +#ifndef DEFsetT
    +#define DEFsetT 1
    +typedef struct setT setT;   /* a set is a sorted or unsorted array of pointers */
    +#endif
    +
    +#ifndef DEFqhT
    +#define DEFqhT 1
    +typedef struct qhT qhT;          /* defined in libqhull_r.h */
    +#endif
    +
    +/* [jan'15] Decided not to use countT.  Most sets are small.  The code uses signed tests */
    +
    +/*------------------------------------------
    +
    +setT
    +  a set or list of pointers with maximum size and actual size.
    +
    +variations:
    +  unsorted, unique   -- a list of unique pointers with NULL terminator
    +                           user guarantees uniqueness
    +  sorted             -- a sorted list of unique pointers with NULL terminator
    +                           qset_r.c guarantees uniqueness
    +  unsorted           -- a list of pointers terminated with NULL
    +  indexed            -- an array of pointers with NULL elements
    +
    +structure for set of n elements:
    +
    +        --------------
    +        |  maxsize
    +        --------------
    +        |  e[0] - a pointer, may be NULL for indexed sets
    +        --------------
    +        |  e[1]
    +
    +        --------------
    +        |  ...
    +        --------------
    +        |  e[n-1]
    +        --------------
    +        |  e[n] = NULL
    +        --------------
    +        |  ...
    +        --------------
    +        |  e[maxsize] - n+1 or NULL (determines actual size of set)
    +        --------------
    +
    +*/
    +
    +/*-- setelemT -- internal type to allow both pointers and indices
    +*/
    +typedef union setelemT setelemT;
    +union setelemT {
    +  void    *p;
    +  int   i;         /* integer used for e[maxSize] */
    +};
    +
    +struct setT {
    +  int maxsize;          /* maximum number of elements (except NULL) */
    +  setelemT e[1];        /* array of pointers, tail is NULL */
    +                        /* last slot (unless NULL) is actual size+1
    +                           e[maxsize]==NULL or e[e[maxsize]-1]==NULL */
    +                        /* this may generate a warning since e[] contains
    +                           maxsize elements */
    +};
    +
    +/*=========== -constants- =========================*/
    +
    +/*-------------------------------------
    +
    +  SETelemsize
    +    size of a set element in bytes
    +*/
    +#define SETelemsize ((int)sizeof(setelemT))
    +
    +
    +/*=========== -macros- =========================*/
    +
    +/*-------------------------------------
    +
    +   FOREACHsetelement_(type, set, variable)
    +     define FOREACH iterator
    +
    +   declare:
    +     assumes *variable and **variablep are declared
    +     no space in "variable)" [DEC Alpha cc compiler]
    +
    +   each iteration:
    +     variable is set element
    +     variablep is one beyond variable.
    +
    +   to repeat an element:
    +     variablep--; / *repeat* /
    +
    +   at exit:
    +     variable is NULL at end of loop
    +
    +   example:
    +     #define FOREACHfacet_( facets ) FOREACHsetelement_( facetT, facets, facet )
    +
    +   notes:
    +     use FOREACHsetelement_i_() if need index or include NULLs
    +
    +   WARNING:
    +     nested loops can't use the same variable (define another FOREACH)
    +
    +     needs braces if nested inside another FOREACH
    +     this includes intervening blocks, e.g. FOREACH...{ if () FOREACH...} )
    +*/
    +#define FOREACHsetelement_(type, set, variable) \
    +        if (((variable= NULL), set)) for (\
    +          variable##p= (type **)&((set)->e[0].p); \
    +          (variable= *variable##p++);)
    +
    +/*------------------------------------------
    +
    +   FOREACHsetelement_i_(qh, type, set, variable)
    +     define indexed FOREACH iterator
    +
    +   declare:
    +     type *variable, variable_n, variable_i;
    +
    +   each iteration:
    +     variable is set element, may be NULL
    +     variable_i is index, variable_n is qh_setsize()
    +
    +   to repeat an element:
    +     variable_i--; variable_n-- repeats for deleted element
    +
    +   at exit:
    +     variable==NULL and variable_i==variable_n
    +
    +   example:
    +     #define FOREACHfacet_i_( qh, facets ) FOREACHsetelement_i_( qh, facetT, facets, facet )
    +
    +   WARNING:
    +     nested loops can't use the same variable (define another FOREACH)
    +
    +     needs braces if nested inside another FOREACH
    +     this includes intervening blocks, e.g. FOREACH...{ if () FOREACH...} )
    +*/
    +#define FOREACHsetelement_i_(qh, type, set, variable) \
    +        if (((variable= NULL), set)) for (\
    +          variable##_i= 0, variable= (type *)((set)->e[0].p), \
    +                   variable##_n= qh_setsize(qh, set);\
    +          variable##_i < variable##_n;\
    +          variable= (type *)((set)->e[++variable##_i].p) )
    +
    +/*----------------------------------------
    +
    +   FOREACHsetelementreverse_(qh, type, set, variable)-
    +     define FOREACH iterator in reverse order
    +
    +   declare:
    +     assumes *variable and **variablep are declared
    +     also declare 'int variabletemp'
    +
    +   each iteration:
    +     variable is set element
    +
    +   to repeat an element:
    +     variabletemp++; / *repeat* /
    +
    +   at exit:
    +     variable is NULL
    +
    +   example:
    +     #define FOREACHvertexreverse_( vertices ) FOREACHsetelementreverse_( vertexT, vertices, vertex )
    +
    +   notes:
    +     use FOREACHsetelementreverse12_() to reverse first two elements
    +     WARNING: needs braces if nested inside another FOREACH
    +*/
    +#define FOREACHsetelementreverse_(qh, type, set, variable) \
    +        if (((variable= NULL), set)) for (\
    +           variable##temp= qh_setsize(qh, set)-1, variable= qh_setlast(qh, set);\
    +           variable; variable= \
    +           ((--variable##temp >= 0) ? SETelemt_(set, variable##temp, type) : NULL))
    +
    +/*-------------------------------------
    +
    +   FOREACHsetelementreverse12_(type, set, variable)-
    +     define FOREACH iterator with e[1] and e[0] reversed
    +
    +   declare:
    +     assumes *variable and **variablep are declared
    +
    +   each iteration:
    +     variable is set element
    +     variablep is one after variable.
    +
    +   to repeat an element:
    +     variablep--; / *repeat* /
    +
    +   at exit:
    +     variable is NULL at end of loop
    +
    +   example
    +     #define FOREACHvertexreverse12_( vertices ) FOREACHsetelementreverse12_( vertexT, vertices, vertex )
    +
    +   notes:
    +     WARNING: needs braces if nested inside another FOREACH
    +*/
    +#define FOREACHsetelementreverse12_(type, set, variable) \
    +        if (((variable= NULL), set)) for (\
    +          variable##p= (type **)&((set)->e[1].p); \
    +          (variable= *variable##p); \
    +          variable##p == ((type **)&((set)->e[0].p))?variable##p += 2: \
    +              (variable##p == ((type **)&((set)->e[1].p))?variable##p--:variable##p++))
    +
    +/*-------------------------------------
    +
    +   FOREACHelem_( set )-
    +     iterate elements in a set
    +
    +   declare:
    +     void *elem, *elemp;
    +
    +   each iteration:
    +     elem is set element
    +     elemp is one beyond
    +
    +   to repeat an element:
    +     elemp--; / *repeat* /
    +
    +   at exit:
    +     elem == NULL at end of loop
    +
    +   example:
    +     FOREACHelem_(set) {
    +
    +   notes:
    +     WARNING: needs braces if nested inside another FOREACH
    +*/
    +#define FOREACHelem_(set) FOREACHsetelement_(void, set, elem)
    +
    +/*-------------------------------------
    +
    +   FOREACHset_( set )-
    +     iterate a set of sets
    +
    +   declare:
    +     setT *set, **setp;
    +
    +   each iteration:
    +     set is set element
    +     setp is one beyond
    +
    +   to repeat an element:
    +     setp--; / *repeat* /
    +
    +   at exit:
    +     set == NULL at end of loop
    +
    +   example
    +     FOREACHset_(sets) {
    +
    +   notes:
    +     WARNING: needs braces if nested inside another FOREACH
    +*/
    +#define FOREACHset_(sets) FOREACHsetelement_(setT, sets, set)
    +
    +/*-------------------------------------------
    +
    +   SETindex_( set, elem )
    +     return index of elem in set
    +
    +   notes:
    +     for use with FOREACH iteration
    +     WARN64 -- Maximum set size is 2G
    +
    +   example:
    +     i= SETindex_(ridges, ridge)
    +*/
    +#define SETindex_(set, elem) ((int)((void **)elem##p - (void **)&(set)->e[1].p))
    +
    +/*-----------------------------------------
    +
    +   SETref_( elem )
    +     l.h.s. for modifying the current element in a FOREACH iteration
    +
    +   example:
    +     SETref_(ridge)= anotherridge;
    +*/
    +#define SETref_(elem) (elem##p[-1])
    +
    +/*-----------------------------------------
    +
    +   SETelem_(set, n)
    +     return the n'th element of set
    +
    +   notes:
    +      assumes that n is valid [0..size] and that set is defined
    +      use SETelemt_() for type cast
    +*/
    +#define SETelem_(set, n)           ((set)->e[n].p)
    +
    +/*-----------------------------------------
    +
    +   SETelemt_(set, n, type)
    +     return the n'th element of set as a type
    +
    +   notes:
    +      assumes that n is valid [0..size] and that set is defined
    +*/
    +#define SETelemt_(set, n, type)    ((type*)((set)->e[n].p))
    +
    +/*-----------------------------------------
    +
    +   SETelemaddr_(set, n, type)
    +     return address of the n'th element of a set
    +
    +   notes:
    +      assumes that n is valid [0..size] and set is defined
    +*/
    +#define SETelemaddr_(set, n, type) ((type **)(&((set)->e[n].p)))
    +
    +/*-----------------------------------------
    +
    +   SETfirst_(set)
    +     return first element of set
    +
    +*/
    +#define SETfirst_(set)             ((set)->e[0].p)
    +
    +/*-----------------------------------------
    +
    +   SETfirstt_(set, type)
    +     return first element of set as a type
    +
    +*/
    +#define SETfirstt_(set, type)      ((type*)((set)->e[0].p))
    +
    +/*-----------------------------------------
    +
    +   SETsecond_(set)
    +     return second element of set
    +
    +*/
    +#define SETsecond_(set)            ((set)->e[1].p)
    +
    +/*-----------------------------------------
    +
    +   SETsecondt_(set, type)
    +     return second element of set as a type
    +*/
    +#define SETsecondt_(set, type)     ((type*)((set)->e[1].p))
    +
    +/*-----------------------------------------
    +
    +   SETaddr_(set, type)
    +       return address of set's elements
    +*/
    +#define SETaddr_(set,type)         ((type **)(&((set)->e[0].p)))
    +
    +/*-----------------------------------------
    +
    +   SETreturnsize_(set, size)
    +     return size of a set
    +
    +   notes:
    +      set must be defined
    +      use qh_setsize(qhT *qh, set) unless speed is critical
    +*/
    +#define SETreturnsize_(set, size) (((size)= ((set)->e[(set)->maxsize].i))?(--(size)):((size)= (set)->maxsize))
    +
    +/*-----------------------------------------
    +
    +   SETempty_(set)
    +     return true(1) if set is empty
    +
    +   notes:
    +      set may be NULL
    +*/
    +#define SETempty_(set)            (!set || (SETfirst_(set) ? 0 : 1))
    +
    +/*---------------------------------
    +
    +  SETsizeaddr_(set)
    +    return pointer to 'actual size+1' of set (set CANNOT be NULL!!)
    +    Its type is setelemT* for strict aliasing
    +    All SETelemaddr_ must be cast to setelemT
    +
    +
    +  notes:
    +    *SETsizeaddr==NULL or e[*SETsizeaddr-1].p==NULL
    +*/
    +#define SETsizeaddr_(set) (&((set)->e[(set)->maxsize]))
    +
    +/*-----------------------------------------
    +
    +   SETtruncate_(set, size)
    +     truncate set to size
    +
    +   see:
    +     qh_settruncate()
    +
    +*/
    +#define SETtruncate_(set, size) {set->e[set->maxsize].i= size+1; /* maybe overwritten */ \
    +      set->e[size].p= NULL;}
    +
    +/*======= prototypes in alphabetical order ============*/
    +
    +#ifdef __cplusplus
    +extern "C" {
    +#endif
    +
    +void  qh_setaddsorted(qhT *qh, setT **setp, void *elem);
    +void  qh_setaddnth(qhT *qh, setT **setp, int nth, void *newelem);
    +void  qh_setappend(qhT *qh, setT **setp, void *elem);
    +void  qh_setappend_set(qhT *qh, setT **setp, setT *setA);
    +void  qh_setappend2ndlast(qhT *qh, setT **setp, void *elem);
    +void  qh_setcheck(qhT *qh, setT *set, const char *tname, unsigned id);
    +void  qh_setcompact(qhT *qh, setT *set);
    +setT *qh_setcopy(qhT *qh, setT *set, int extra);
    +void *qh_setdel(setT *set, void *elem);
    +void *qh_setdellast(setT *set);
    +void *qh_setdelnth(qhT *qh, setT *set, int nth);
    +void *qh_setdelnthsorted(qhT *qh, setT *set, int nth);
    +void *qh_setdelsorted(setT *set, void *newelem);
    +setT *qh_setduplicate(qhT *qh, setT *set, int elemsize);
    +void **qh_setendpointer(setT *set);
    +int   qh_setequal(setT *setA, setT *setB);
    +int   qh_setequal_except(setT *setA, void *skipelemA, setT *setB, void *skipelemB);
    +int   qh_setequal_skip(setT *setA, int skipA, setT *setB, int skipB);
    +void  qh_setfree(qhT *qh, setT **set);
    +void  qh_setfree2(qhT *qh, setT **setp, int elemsize);
    +void  qh_setfreelong(qhT *qh, setT **set);
    +int   qh_setin(setT *set, void *setelem);
    +int qh_setindex(setT *set, void *setelem);
    +void  qh_setlarger(qhT *qh, setT **setp);
    +void *qh_setlast(setT *set);
    +setT *qh_setnew(qhT *qh, int size);
    +setT *qh_setnew_delnthsorted(qhT *qh, setT *set, int size, int nth, int prepend);
    +void  qh_setprint(qhT *qh, FILE *fp, const char* string, setT *set);
    +void  qh_setreplace(qhT *qh, setT *set, void *oldelem, void *newelem);
    +int qh_setsize(qhT *qh, setT *set);
    +setT *qh_settemp(qhT *qh, int setsize);
    +void  qh_settempfree(qhT *qh, setT **set);
    +void  qh_settempfree_all(qhT *qh);
    +setT *qh_settemppop(qhT *qh);
    +void  qh_settemppush(qhT *qh, setT *set);
    +void  qh_settruncate(qhT *qh, setT *set, int size);
    +int   qh_setunique(qhT *qh, setT **set, void *elem);
    +void  qh_setzero(qhT *qh, setT *set, int idx, int size);
    +
    +#ifdef __cplusplus
    +} /* extern "C" */
    +#endif
    +
    +#endif /* qhDEFset */
    diff --git a/xs/src/qhull/src/libqhull_r/random_r.c b/xs/src/qhull/src/libqhull_r/random_r.c
    new file mode 100644
    index 000000000..1fefb51c3
    --- /dev/null
    +++ b/xs/src/qhull/src/libqhull_r/random_r.c
    @@ -0,0 +1,247 @@
    +/*
      ---------------------------------
    +
    +   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 
    +#include 
    +#include 
    +
    +#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
    +
    +/*---------------------------------
    +
    + 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 */
    +
    +/*---------------------------------
    +
    +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; i0 && strchr(argv[i], ' ')) {
    +        count += 2;  /* quote delimiters */
    +        for (s=argv[i]; *s; s++) {
    +          if (*s == '"') {
    +            count++;
    +          }
    +        }
    +      }
    +    }
    +    return count;
    +} /* argv_to_command_size */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +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 */
    +
    +/*---------------------------------
    +
    +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 */
    +
    +/*---------------------------------
    +
    +  qh_strtol( s, endp) qh_strtod( s, endp)
    +    internal versions of strtol() and strtod()
    +    does not skip trailing spaces
    +  notes:
    +    some implementations of strtol()/strtod() skip trailing spaces
    +*/
    +double qh_strtod(const char *s, char **endp) {
    +  double result;
    +
    +  result= strtod(s, endp);
    +  if (s < (*endp) && (*endp)[-1] == ' ')
    +    (*endp)--;
    +  return result;
    +} /* strtod */
    +
    +int qh_strtol(const char *s, char **endp) {
    +  int result;
    +
    +  result= (int) strtol(s, endp, 10);     /* WARN64 */
    +  if (s< (*endp) && (*endp)[-1] == ' ')
    +    (*endp)--;
    +  return result;
    +} /* strtol */
    diff --git a/xs/src/qhull/src/libqhull_r/random_r.h b/xs/src/qhull/src/libqhull_r/random_r.h
    new file mode 100644
    index 000000000..dc884b33c
    --- /dev/null
    +++ b/xs/src/qhull/src/libqhull_r/random_r.h
    @@ -0,0 +1,41 @@
    +/*
      ---------------------------------
    +
    +  random.h
    +    header file for random and utility routines
    +
    +   see qh-geom_r.htm and random_r.c
    +
    +   Copyright (c) 1993-2015 The Geometry Center.
    +   $Id: //main/2015/qhull/src/libqhull_r/random_r.h#4 $$Change: 2079 $
    +   $DateTime: 2016/02/07 17:43:34 $$Author: bbarber $
    +*/
    +
    +#ifndef qhDEFrandom
    +#define qhDEFrandom 1
    +
    +#include "libqhull_r.h"
    +
    +/*============= prototypes in alphabetical order ======= */
    +
    +#ifdef __cplusplus
    +extern "C" {
    +#endif
    +
    +int     qh_argv_to_command(int argc, char *argv[], char* command, int max_size);
    +int     qh_argv_to_command_size(int argc, char *argv[]);
    +int     qh_rand(qhT *qh);
    +void    qh_srand(qhT *qh, int seed);
    +realT   qh_randomfactor(qhT *qh, realT scale, realT offset);
    +void    qh_randommatrix(qhT *qh, realT *buffer, int dim, realT **row);
    +int     qh_strtol(const char *s, char **endp);
    +double  qh_strtod(const char *s, char **endp);
    +
    +#ifdef __cplusplus
    +} /* extern "C" */
    +#endif
    +
    +#endif /* qhDEFrandom */
    +
    +
    +
    diff --git a/xs/src/qhull/src/libqhull_r/rboxlib_r.c b/xs/src/qhull/src/libqhull_r/rboxlib_r.c
    new file mode 100644
    index 000000000..6c0c7e35e
    --- /dev/null
    +++ b/xs/src/qhull/src/libqhull_r/rboxlib_r.c
    @@ -0,0 +1,842 @@
    +/*
      ---------------------------------
    +
    +   rboxlib_r.c
    +     Generate input points
    +
    +   notes:
    +     For documentation, see prompt[] of rbox_r.c
    +     50 points generated for 'rbox D4'
    +
    +   WARNING:
    +     incorrect range if qh_RANDOMmax is defined wrong (user_r.h)
    +*/
    +
    +#include "libqhull_r.h"  /* First for user_r.h */
    +#include "random_r.h"
    +
    +#include 
    +#include 
    +#include 
    +#include 
    +#include 
    +#include 
    +#include 
    +#include 
    +
    +#ifdef _MSC_VER  /* Microsoft Visual C++ */
    +#pragma warning( disable : 4706)  /* assignment within conditional expression. */
    +#pragma warning( disable : 4996)  /* this function (strncat,sprintf,strcpy) or variable may be unsafe. */
    +#endif
    +
    +#define MAXdim 200
    +#define PI 3.1415926535897932384
    +
    +/* ------------------------------ prototypes ----------------*/
    +int qh_roundi(qhT *qh, double a);
    +void qh_out1(qhT *qh, double a);
    +void qh_out2n(qhT *qh, double a, double b);
    +void qh_out3n(qhT *qh, double a, double b, double c);
    +void qh_outcoord(qhT *qh, int iscdd, double *coord, int dim);
    +void qh_outcoincident(qhT *qh, int coincidentpoints, double radius, int iscdd, double *coord, int dim);
    +
    +void    qh_fprintf_rbox(qhT *qh, FILE *fp, int msgcode, const char *fmt, ... );
    +void    qh_free(void *mem);
    +void   *qh_malloc(size_t size);
    +int     qh_rand(qhT *qh);
    +void    qh_srand(qhT *qh, int seed);
    +
    +/*---------------------------------
    +
    +  qh_rboxpoints(qh, rbox_command )
    +    Generate points to qh->fout according to rbox options
    +    Report errors on qh->ferr
    +
    +  returns:
    +    0 (qh_ERRnone) on success
    +    1 (qh_ERRinput) on input error
    +    4 (qh_ERRmem) on memory error
    +    5 (qh_ERRqhull) on internal error
    +
    +  notes:
    +    To avoid using stdio, redefine qh_malloc, qh_free, and qh_fprintf_rbox (user_r.c)
    +
    +  design:
    +    Straight line code (consider defining a struct and functions):
    +
    +    Parse arguments into variables
    +    Determine the number of points
    +    Generate the points
    +*/
    +int qh_rboxpoints(qhT *qh, char* rbox_command) {
    +  int i,j,k;
    +  int gendim;
    +  int coincidentcount=0, coincidenttotal=0, coincidentpoints=0;
    +  int cubesize, diamondsize, seed=0, count, apex;
    +  int dim=3 , numpoints= 0, totpoints, addpoints=0;
    +  int issphere=0, isaxis=0,  iscdd= 0, islens= 0, isregular=0, iswidth=0, addcube=0;
    +  int isgap=0, isspiral=0, NOcommand= 0, adddiamond=0;
    +  int israndom=0, istime=0;
    +  int isbox=0, issimplex=0, issimplex2=0, ismesh=0;
    +  double width=0.0, gap=0.0, radius=0.0, coincidentradius=0.0;
    +  double coord[MAXdim], offset, meshm=3.0, meshn=4.0, meshr=5.0;
    +  double *coordp, *simplex= NULL, *simplexp;
    +  int nthroot, mult[MAXdim];
    +  double norm, factor, randr, rangap, lensangle= 0, lensbase= 1;
    +  double anglediff, angle, x, y, cube= 0.0, diamond= 0.0;
    +  double box= qh_DEFAULTbox; /* scale all numbers before output */
    +  double randmax= qh_RANDOMmax;
    +  char command[200], seedbuf[200];
    +  char *s= command, *t, *first_point= NULL;
    +  time_t timedata;
    +  int exitcode;
    +
    +  exitcode= setjmp(qh->rbox_errexit);
    +  if (exitcode) {
    +    /* same code for error exit and normal return, qh->NOerrexit is set */
    +    if (simplex)
    +        qh_free(simplex);
    +    return exitcode;
    +  }
    +
    +  *command= '\0';
    +  strncat(command, rbox_command, sizeof(command)-strlen(command)-1);
    +
    +  while (*s && !isspace(*s))  /* skip program name */
    +    s++;
    +  while (*s) {
    +    while (*s && isspace(*s))
    +      s++;
    +    if (*s == '-')
    +      s++;
    +    if (!*s)
    +      break;
    +    if (isdigit(*s)) {
    +      numpoints= qh_strtol(s, &s);
    +      continue;
    +    }
    +    /* ============= read flags =============== */
    +    switch (*s++) {
    +    case 'c':
    +      addcube= 1;
    +      t= s;
    +      while (isspace(*t))
    +        t++;
    +      if (*t == 'G')
    +        cube= qh_strtod(++t, &s);
    +      break;
    +    case 'd':
    +      adddiamond= 1;
    +      t= s;
    +      while (isspace(*t))
    +        t++;
    +      if (*t == 'G')
    +        diamond= qh_strtod(++t, &s);
    +      break;
    +    case 'h':
    +      iscdd= 1;
    +      break;
    +    case 'l':
    +      isspiral= 1;
    +      break;
    +    case 'n':
    +      NOcommand= 1;
    +      break;
    +    case 'r':
    +      isregular= 1;
    +      break;
    +    case 's':
    +      issphere= 1;
    +      break;
    +    case 't':
    +      istime= 1;
    +      if (isdigit(*s)) {
    +        seed= qh_strtol(s, &s);
    +        israndom= 0;
    +      }else
    +        israndom= 1;
    +      break;
    +    case 'x':
    +      issimplex= 1;
    +      break;
    +    case 'y':
    +      issimplex2= 1;
    +      break;
    +    case 'z':
    +      qh->rbox_isinteger= 1;
    +      break;
    +    case 'B':
    +      box= qh_strtod(s, &s);
    +      isbox= 1;
    +      break;
    +    case 'C':
    +      if (*s)
    +        coincidentpoints=  qh_strtol(s, &s);
    +      if (*s == ',') {
    +        ++s;
    +        coincidentradius=  qh_strtod(s, &s);
    +      }
    +      if (*s == ',') {
    +        ++s;
    +        coincidenttotal=  qh_strtol(s, &s);
    +      }
    +      if (*s && !isspace(*s)) {
    +        qh_fprintf_rbox(qh, qh->ferr, 7080, "rbox error: arguments for 'Cn,r,m' are not 'int', 'float', and 'int'.  Remaining string is '%s'\n", s);
    +        qh_errexit_rbox(qh, qh_ERRinput);
    +      }
    +      if (coincidentpoints==0){
    +        qh_fprintf_rbox(qh, qh->ferr, 6268, "rbox error: missing arguments for 'Cn,r,m' where n is the number of coincident points, r is the radius (default 0.0), and m is the number of points\n");
    +        qh_errexit_rbox(qh, qh_ERRinput);
    +      }
    +      if (coincidentpoints<0 || coincidenttotal<0 || coincidentradius<0.0){
    +        qh_fprintf_rbox(qh, qh->ferr, 6269, "rbox error: negative arguments for 'Cn,m,r' where n (%d) is the number of coincident points, m (%d) is the number of points, and r (%.2g) is the radius (default 0.0)\n", coincidentpoints, coincidenttotal, coincidentradius);
    +        qh_errexit_rbox(qh, qh_ERRinput);
    +      }
    +      break;
    +    case 'D':
    +      dim= qh_strtol(s, &s);
    +      if (dim < 1
    +      || dim > MAXdim) {
    +        qh_fprintf_rbox(qh, qh->ferr, 6189, "rbox error: dimension, D%d, out of bounds (>=%d or <=0)", dim, MAXdim);
    +        qh_errexit_rbox(qh, qh_ERRinput);
    +      }
    +      break;
    +    case 'G':
    +      if (isdigit(*s))
    +        gap= qh_strtod(s, &s);
    +      else
    +        gap= 0.5;
    +      isgap= 1;
    +      break;
    +    case 'L':
    +      if (isdigit(*s))
    +        radius= qh_strtod(s, &s);
    +      else
    +        radius= 10;
    +      islens= 1;
    +      break;
    +    case 'M':
    +      ismesh= 1;
    +      if (*s)
    +        meshn= qh_strtod(s, &s);
    +      if (*s == ',') {
    +        ++s;
    +        meshm= qh_strtod(s, &s);
    +      }else
    +        meshm= 0.0;
    +      if (*s == ',') {
    +        ++s;
    +        meshr= qh_strtod(s, &s);
    +      }else
    +        meshr= sqrt(meshn*meshn + meshm*meshm);
    +      if (*s && !isspace(*s)) {
    +        qh_fprintf_rbox(qh, qh->ferr, 7069, "rbox warning: assuming 'M3,4,5' since mesh args are not integers or reals\n");
    +        meshn= 3.0, meshm=4.0, meshr=5.0;
    +      }
    +      break;
    +    case 'O':
    +      qh->rbox_out_offset= qh_strtod(s, &s);
    +      break;
    +    case 'P':
    +      if (!first_point)
    +        first_point= s-1;
    +      addpoints++;
    +      while (*s && !isspace(*s))   /* read points later */
    +        s++;
    +      break;
    +    case 'W':
    +      width= qh_strtod(s, &s);
    +      iswidth= 1;
    +      break;
    +    case 'Z':
    +      if (isdigit(*s))
    +        radius= qh_strtod(s, &s);
    +      else
    +        radius= 1.0;
    +      isaxis= 1;
    +      break;
    +    default:
    +      qh_fprintf_rbox(qh, qh->ferr, 7070, "rbox error: unknown flag at %s.\nExecute 'rbox' without arguments for documentation.\n", s);
    +      qh_errexit_rbox(qh, qh_ERRinput);
    +    }
    +    if (*s && !isspace(*s)) {
    +      qh_fprintf_rbox(qh, qh->ferr, 7071, "rbox error: missing space between flags at %s.\n", s);
    +      qh_errexit_rbox(qh, qh_ERRinput);
    +    }
    +  }
    +
    +  /* ============= defaults, constants, and sizes =============== */
    +  if (qh->rbox_isinteger && !isbox)
    +    box= qh_DEFAULTzbox;
    +  if (addcube) {
    +    cubesize= (int)floor(ldexp(1.0,dim)+0.5);
    +    if (cube == 0.0)
    +      cube= box;
    +  }else
    +    cubesize= 0;
    +  if (adddiamond) {
    +    diamondsize= 2*dim;
    +    if (diamond == 0.0)
    +      diamond= box;
    +  }else
    +    diamondsize= 0;
    +  if (islens) {
    +    if (isaxis) {
    +        qh_fprintf_rbox(qh, qh->ferr, 6190, "rbox error: can not combine 'Ln' with 'Zn'\n");
    +        qh_errexit_rbox(qh, qh_ERRinput);
    +    }
    +    if (radius <= 1.0) {
    +        qh_fprintf_rbox(qh, qh->ferr, 6191, "rbox error: lens radius %.2g should be greater than 1.0\n",
    +               radius);
    +        qh_errexit_rbox(qh, qh_ERRinput);
    +    }
    +    lensangle= asin(1.0/radius);
    +    lensbase= radius * cos(lensangle);
    +  }
    +
    +  if (!numpoints) {
    +    if (issimplex2)
    +        ; /* ok */
    +    else if (isregular + issimplex + islens + issphere + isaxis + isspiral + iswidth + ismesh) {
    +        qh_fprintf_rbox(qh, qh->ferr, 6192, "rbox error: missing count\n");
    +        qh_errexit_rbox(qh, qh_ERRinput);
    +    }else if (adddiamond + addcube + addpoints)
    +        ; /* ok */
    +    else {
    +        numpoints= 50;  /* ./rbox D4 is the test case */
    +        issphere= 1;
    +    }
    +  }
    +  if ((issimplex + islens + isspiral + ismesh > 1)
    +  || (issimplex + issphere + isspiral + ismesh > 1)) {
    +    qh_fprintf_rbox(qh, qh->ferr, 6193, "rbox error: can only specify one of 'l', 's', 'x', 'Ln', or 'Mn,m,r' ('Ln s' is ok).\n");
    +    qh_errexit_rbox(qh, qh_ERRinput);
    +  }
    +  if (coincidentpoints>0 && (numpoints == 0 || coincidenttotal > numpoints)) {
    +    qh_fprintf_rbox(qh, qh->ferr, 6270, "rbox error: 'Cn,r,m' requested n coincident points for each of m points.  Either there is no points or m (%d) is greater than the number of points (%d).\n", coincidenttotal, numpoints);
    +    qh_errexit_rbox(qh, qh_ERRinput);
    +  }
    +  if (coincidenttotal == 0)
    +    coincidenttotal= numpoints;
    +
    +  /* ============= print header with total points =============== */
    +  if (issimplex || ismesh)
    +    totpoints= numpoints;
    +  else if (issimplex2)
    +    totpoints= numpoints+dim+1;
    +  else if (isregular) {
    +    totpoints= numpoints;
    +    if (dim == 2) {
    +        if (islens)
    +          totpoints += numpoints - 2;
    +    }else if (dim == 3) {
    +        if (islens)
    +          totpoints += 2 * numpoints;
    +      else if (isgap)
    +        totpoints += 1 + numpoints;
    +      else
    +        totpoints += 2;
    +    }
    +  }else
    +    totpoints= numpoints + isaxis;
    +  totpoints += cubesize + diamondsize + addpoints;
    +  totpoints += coincidentpoints*coincidenttotal;
    +
    +  /* ============= seed randoms =============== */
    +  if (istime == 0) {
    +    for (s=command; *s; s++) {
    +      if (issimplex2 && *s == 'y') /* make 'y' same seed as 'x' */
    +        i= 'x';
    +      else
    +        i= *s;
    +      seed= 11*seed + i;
    +    }
    +  }else if (israndom) {
    +    seed= (int)time(&timedata);
    +    sprintf(seedbuf, " t%d", seed);  /* appends an extra t, not worth removing */
    +    strncat(command, seedbuf, sizeof(command)-strlen(command)-1);
    +    t= strstr(command, " t ");
    +    if (t)
    +      strcpy(t+1, t+3); /* remove " t " */
    +  } /* else, seed explicitly set to n */
    +  qh_RANDOMseed_(qh, seed);
    +
    +  /* ============= print header =============== */
    +
    +  if (iscdd)
    +      qh_fprintf_rbox(qh, qh->fout, 9391, "%s\nbegin\n        %d %d %s\n",
    +      NOcommand ? "" : command,
    +      totpoints, dim+1,
    +      qh->rbox_isinteger ? "integer" : "real");
    +  else if (NOcommand)
    +      qh_fprintf_rbox(qh, qh->fout, 9392, "%d\n%d\n", dim, totpoints);
    +  else
    +      /* qh_fprintf_rbox special cases 9393 to append 'command' to the RboxPoints.comment() */
    +      qh_fprintf_rbox(qh, qh->fout, 9393, "%d %s\n%d\n", dim, command, totpoints);
    +
    +  /* ============= explicit points =============== */
    +  if ((s= first_point)) {
    +    while (s && *s) { /* 'P' */
    +      count= 0;
    +      if (iscdd)
    +        qh_out1(qh, 1.0);
    +      while (*++s) {
    +        qh_out1(qh, qh_strtod(s, &s));
    +        count++;
    +        if (isspace(*s) || !*s)
    +          break;
    +        if (*s != ',') {
    +          qh_fprintf_rbox(qh, qh->ferr, 6194, "rbox error: missing comma after coordinate in %s\n\n", s);
    +          qh_errexit_rbox(qh, qh_ERRinput);
    +        }
    +      }
    +      if (count < dim) {
    +        for (k=dim-count; k--; )
    +          qh_out1(qh, 0.0);
    +      }else if (count > dim) {
    +        qh_fprintf_rbox(qh, qh->ferr, 6195, "rbox error: %d coordinates instead of %d coordinates in %s\n\n",
    +                  count, dim, s);
    +        qh_errexit_rbox(qh, qh_ERRinput);
    +      }
    +      qh_fprintf_rbox(qh, qh->fout, 9394, "\n");
    +      while ((s= strchr(s, 'P'))) {
    +        if (isspace(s[-1]))
    +          break;
    +      }
    +    }
    +  }
    +
    +  /* ============= simplex distribution =============== */
    +  if (issimplex+issimplex2) {
    +    if (!(simplex= (double*)qh_malloc( dim * (dim+1) * sizeof(double)))) {
    +      qh_fprintf_rbox(qh, qh->ferr, 6196, "rbox error: insufficient memory for simplex\n");
    +      qh_errexit_rbox(qh, qh_ERRmem); /* qh_ERRmem */
    +    }
    +    simplexp= simplex;
    +    if (isregular) {
    +      for (i=0; ifout, 9395, "\n");
    +      }
    +    }
    +    for (j=0; jferr, 6197, "rbox error: regular points can be used only in 2-d and 3-d\n\n");
    +      qh_errexit_rbox(qh, qh_ERRinput);
    +    }
    +    if (!isaxis || radius == 0.0) {
    +      isaxis= 1;
    +      radius= 1.0;
    +    }
    +    if (dim == 3) {
    +      if (iscdd)
    +        qh_out1(qh, 1.0);
    +      qh_out3n(qh, 0.0, 0.0, -box);
    +      if (!isgap) {
    +        if (iscdd)
    +          qh_out1(qh, 1.0);
    +        qh_out3n(qh, 0.0, 0.0, box);
    +      }
    +    }
    +    angle= 0.0;
    +    anglediff= 2.0 * PI/numpoints;
    +    for (i=0; i < numpoints; i++) {
    +      angle += anglediff;
    +      x= radius * cos(angle);
    +      y= radius * sin(angle);
    +      if (dim == 2) {
    +        if (iscdd)
    +          qh_out1(qh, 1.0);
    +        qh_out2n(qh, x*box, y*box);
    +      }else {
    +        norm= sqrt(1.0 + x*x + y*y);
    +        if (iscdd)
    +          qh_out1(qh, 1.0);
    +        qh_out3n(qh, box*x/norm, box*y/norm, box/norm);
    +        if (isgap) {
    +          x *= 1-gap;
    +          y *= 1-gap;
    +          norm= sqrt(1.0 + x*x + y*y);
    +          if (iscdd)
    +            qh_out1(qh, 1.0);
    +          qh_out3n(qh, box*x/norm, box*y/norm, box/norm);
    +        }
    +      }
    +    }
    +  }
    +  /* ============= regular points for 'r Ln D2' =============== */
    +  else if (isregular && islens && dim == 2) {
    +    double cos_0;
    +
    +    angle= lensangle;
    +    anglediff= 2 * lensangle/(numpoints - 1);
    +    cos_0= cos(lensangle);
    +    for (i=0; i < numpoints; i++, angle -= anglediff) {
    +      x= radius * sin(angle);
    +      y= radius * (cos(angle) - cos_0);
    +      if (iscdd)
    +        qh_out1(qh, 1.0);
    +      qh_out2n(qh, x*box, y*box);
    +      if (i != 0 && i != numpoints - 1) {
    +        if (iscdd)
    +          qh_out1(qh, 1.0);
    +        qh_out2n(qh, x*box, -y*box);
    +      }
    +    }
    +  }
    +  /* ============= regular points for 'r Ln D3' =============== */
    +  else if (isregular && islens && dim != 2) {
    +    if (dim != 3) {
    +      qh_free(simplex);
    +      qh_fprintf_rbox(qh, qh->ferr, 6198, "rbox error: regular points can be used only in 2-d and 3-d\n\n");
    +      qh_errexit_rbox(qh, qh_ERRinput);
    +    }
    +    angle= 0.0;
    +    anglediff= 2* PI/numpoints;
    +    if (!isgap) {
    +      isgap= 1;
    +      gap= 0.5;
    +    }
    +    offset= sqrt(radius * radius - (1-gap)*(1-gap)) - lensbase;
    +    for (i=0; i < numpoints; i++, angle += anglediff) {
    +      x= cos(angle);
    +      y= sin(angle);
    +      if (iscdd)
    +        qh_out1(qh, 1.0);
    +      qh_out3n(qh, box*x, box*y, 0.0);
    +      x *= 1-gap;
    +      y *= 1-gap;
    +      if (iscdd)
    +        qh_out1(qh, 1.0);
    +      qh_out3n(qh, box*x, box*y, box * offset);
    +      if (iscdd)
    +        qh_out1(qh, 1.0);
    +      qh_out3n(qh, box*x, box*y, -box * offset);
    +    }
    +  }
    +  /* ============= apex of 'Zn' distribution + gendim =============== */
    +  else {
    +    if (isaxis) {
    +      gendim= dim-1;
    +      if (iscdd)
    +        qh_out1(qh, 1.0);
    +      for (j=0; j < gendim; j++)
    +        qh_out1(qh, 0.0);
    +      qh_out1(qh, -box);
    +      qh_fprintf_rbox(qh, qh->fout, 9398, "\n");
    +    }else if (islens)
    +      gendim= dim-1;
    +    else
    +      gendim= dim;
    +    /* ============= generate random point in unit cube =============== */
    +    for (i=0; i < numpoints; i++) {
    +      norm= 0.0;
    +      for (j=0; j < gendim; j++) {
    +        randr= qh_RANDOMint;
    +        coord[j]= 2.0 * randr/randmax - 1.0;
    +        norm += coord[j] * coord[j];
    +      }
    +      norm= sqrt(norm);
    +      /* ============= dim-1 point of 'Zn' distribution ========== */
    +      if (isaxis) {
    +        if (!isgap) {
    +          isgap= 1;
    +          gap= 1.0;
    +        }
    +        randr= qh_RANDOMint;
    +        rangap= 1.0 - gap * randr/randmax;
    +        factor= radius * rangap / norm;
    +        for (j=0; jferr, 6199, "rbox error: spiral distribution is available only in 3d\n\n");
    +          qh_errexit_rbox(qh, qh_ERRinput);
    +        }
    +        coord[0]= cos(2*PI*i/(numpoints - 1));
    +        coord[1]= sin(2*PI*i/(numpoints - 1));
    +        coord[2]= 2.0*(double)i/(double)(numpoints-1) - 1.0;
    +      /* ============= point of 's' distribution =============== */
    +      }else if (issphere) {
    +        factor= 1.0/norm;
    +        if (iswidth) {
    +          randr= qh_RANDOMint;
    +          factor *= 1.0 - width * randr/randmax;
    +        }
    +        for (j=0; j randmax/2)
    +          coord[dim-1]= -coord[dim-1];
    +      /* ============= project 'Wn' point toward boundary =============== */
    +      }else if (iswidth && !issphere) {
    +        j= qh_RANDOMint % gendim;
    +        if (coord[j] < 0)
    +          coord[j]= -1.0 - coord[j] * width;
    +        else
    +          coord[j]= 1.0 - coord[j] * width;
    +      }
    +      /* ============= scale point to box =============== */
    +      for (k=0; k=0; k--) {
    +        if (j & ( 1 << k))
    +          qh_out1(qh, cube);
    +        else
    +          qh_out1(qh, -cube);
    +      }
    +      qh_fprintf_rbox(qh, qh->fout, 9400, "\n");
    +    }
    +  }
    +
    +  /* ============= write diamond vertices =============== */
    +  if (adddiamond) {
    +    for (j=0; j=0; k--) {
    +        if (j/2 != k)
    +          qh_out1(qh, 0.0);
    +        else if (j & 0x1)
    +          qh_out1(qh, diamond);
    +        else
    +          qh_out1(qh, -diamond);
    +      }
    +      qh_fprintf_rbox(qh, qh->fout, 9401, "\n");
    +    }
    +  }
    +
    +  if (iscdd)
    +    qh_fprintf_rbox(qh, qh->fout, 9402, "end\nhull\n");
    +
    +  /* same code for error exit and normal return */
    +  qh_free(simplex);
    +  return qh_ERRnone;
    +} /* rboxpoints */
    +
    +/*------------------------------------------------
    +outxxx - output functions for qh_rboxpoints
    +*/
    +int qh_roundi(qhT *qh, double a) {
    +  if (a < 0.0) {
    +    if (a - 0.5 < INT_MIN) {
    +      qh_fprintf_rbox(qh, qh->ferr, 6200, "rbox input error: negative coordinate %2.2g is too large.  Reduce 'Bn'\n", a);
    +      qh_errexit_rbox(qh, qh_ERRinput);
    +    }
    +    return (int)(a - 0.5);
    +  }else {
    +    if (a + 0.5 > INT_MAX) {
    +      qh_fprintf_rbox(qh, qh->ferr, 6201, "rbox input error: coordinate %2.2g is too large.  Reduce 'Bn'\n", a);
    +      qh_errexit_rbox(qh, qh_ERRinput);
    +    }
    +    return (int)(a + 0.5);
    +  }
    +} /* qh_roundi */
    +
    +void qh_out1(qhT *qh, double a) {
    +
    +  if (qh->rbox_isinteger)
    +    qh_fprintf_rbox(qh, qh->fout, 9403, "%d ", qh_roundi(qh, a+qh->rbox_out_offset));
    +  else
    +    qh_fprintf_rbox(qh, qh->fout, 9404, qh_REAL_1, a+qh->rbox_out_offset);
    +} /* qh_out1 */
    +
    +void qh_out2n(qhT *qh, double a, double b) {
    +
    +  if (qh->rbox_isinteger)
    +    qh_fprintf_rbox(qh, qh->fout, 9405, "%d %d\n", qh_roundi(qh, a+qh->rbox_out_offset), qh_roundi(qh, b+qh->rbox_out_offset));
    +  else
    +    qh_fprintf_rbox(qh, qh->fout, 9406, qh_REAL_2n, a+qh->rbox_out_offset, b+qh->rbox_out_offset);
    +} /* qh_out2n */
    +
    +void qh_out3n(qhT *qh, double a, double b, double c) {
    +
    +  if (qh->rbox_isinteger)
    +    qh_fprintf_rbox(qh, qh->fout, 9407, "%d %d %d\n", qh_roundi(qh, a+qh->rbox_out_offset), qh_roundi(qh, b+qh->rbox_out_offset), qh_roundi(qh, c+qh->rbox_out_offset));
    +  else
    +    qh_fprintf_rbox(qh, qh->fout, 9408, qh_REAL_3n, a+qh->rbox_out_offset, b+qh->rbox_out_offset, c+qh->rbox_out_offset);
    +} /* qh_out3n */
    +
    +void qh_outcoord(qhT *qh, int iscdd, double *coord, int dim) {
    +    double *p= coord;
    +    int k;
    +
    +    if (iscdd)
    +      qh_out1(qh, 1.0);
    +    for (k=0; k < dim; k++)
    +      qh_out1(qh, *(p++));
    +    qh_fprintf_rbox(qh, qh->fout, 9396, "\n");
    +} /* qh_outcoord */
    +
    +void qh_outcoincident(qhT *qh, int coincidentpoints, double radius, int iscdd, double *coord, int dim) {
    +  double *p;
    +  double randr, delta;
    +  int i,k;
    +  double randmax= qh_RANDOMmax;
    +
    +  for (i= 0; ifout, 9410, "\n");
    +  }
    +} /* qh_outcoincident */
    +
    +/*------------------------------------------------
    +   Only called from qh_rboxpoints or qh_fprintf_rbox
    +   qh_fprintf_rbox is only called from qh_rboxpoints
    +*/
    +void qh_errexit_rbox(qhT *qh, int exitcode)
    +{
    +    longjmp(qh->rbox_errexit, exitcode);
    +} /* qh_errexit_rbox */
    +
    diff --git a/xs/src/qhull/src/libqhull_r/stat_r.c b/xs/src/qhull/src/libqhull_r/stat_r.c
    new file mode 100644
    index 000000000..0f3ff0d3d
    --- /dev/null
    +++ b/xs/src/qhull/src/libqhull_r/stat_r.c
    @@ -0,0 +1,682 @@
    +/*
      ---------------------------------
    +
    +   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 ================*/
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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
    +/*---------------------------------
    +
    +  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);
    +}
    +
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +
    +/*---------------------------------
    +
    +  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; jqhstat.id[j]);
    +  }
    +  if (nextindex)
    +    *nextindex= nexti;
    +} /* printstats */
    +
    +#if qh_KEEPstatistics
    +
    +/*---------------------------------
    +
    +  qh_stddev(num, tot, tot2, ave )
    +    compute the standard deviation and average from statistics
    +
    +    tot2 is the sum of the squares
    +  notes:
    +    computes r.m.s.:
    +      (x-ave)^2
    +      == x^2 - 2x tot/num +   (tot/num)^2
    +      == tot2 - 2 tot tot/num + tot tot/num
    +      == tot2 - tot ave
    +*/
    +realT qh_stddev(int num, realT tot, realT tot2, realT *ave) {
    +  realT stddev;
    +
    +  *ave= tot/num;
    +  stddev= sqrt(tot2/num - *ave * *ave);
    +  return stddev;
    +} /* stddev */
    +
    +#endif /* qh_KEEPstatistics */
    +
    +#if !qh_KEEPstatistics
    +void    qh_collectstatistics(qhT *qh) {}
    +void    qh_printallstatistics(qhT *qh, FILE *fp, char *string) {};
    +void    qh_printstatistics(qhT *qh, FILE *fp, char *string) {}
    +#endif
    +
    diff --git a/xs/src/qhull/src/libqhull_r/stat_r.h b/xs/src/qhull/src/libqhull_r/stat_r.h
    new file mode 100644
    index 000000000..75e7d1057
    --- /dev/null
    +++ b/xs/src/qhull/src/libqhull_r/stat_r.h
    @@ -0,0 +1,533 @@
    +/*
      ---------------------------------
    +
    +   stat_r.h
    +     contains all statistics that are collected for qhull
    +
    +   see qh-stat_r.htm and stat_r.c
    +
    +   Copyright (c) 1993-2015 The Geometry Center.
    +   $Id: //main/2015/qhull/src/libqhull_r/stat_r.h#5 $$Change: 2079 $
    +   $DateTime: 2016/02/07 17:43:34 $$Author: bbarber $
    +
    +   recompile qhull if you change this file
    +
    +   Integer statistics are Z* while real statistics are W*.
    +
    +   define MAYdebugx to call a routine at every statistic event
    +
    +*/
    +
    +#ifndef qhDEFstat
    +#define qhDEFstat 1
    +
    +/* Depends on realT.  Do not include libqhull_r to avoid circular dependency */
    +
    +#ifndef DEFqhT
    +#define DEFqhT 1
    +typedef struct qhT qhT;         /* Defined by libqhull_r.h */
    +#endif
    +
    +#ifndef DEFqhstatT
    +#define DEFqhstatT 1
    +typedef struct qhstatT qhstatT; /* Defined here */
    +#endif
    +
    +/*---------------------------------
    +
    +  qh_KEEPstatistics
    +    0 turns off statistic gathering (except zzdef/zzinc/zzadd/zzval/wwval)
    +*/
    +#ifndef qh_KEEPstatistics
    +#define qh_KEEPstatistics 1
    +#endif
    +
    +/*---------------------------------
    +
    +  Zxxx for integers, Wxxx for reals
    +
    +  notes:
    +    be sure that all statistics are defined in stat_r.c
    +      otherwise initialization may core dump
    +    can pick up all statistics by:
    +      grep '[zw].*_[(][ZW]' *.c >z.x
    +    remove trailers with query">-
    +    remove leaders with  query-replace-regexp [ ^I]+  (
    +*/
    +#if qh_KEEPstatistics
    +enum qh_statistics {     /* alphabetical after Z/W */
    +    Zacoplanar,
    +    Wacoplanarmax,
    +    Wacoplanartot,
    +    Zangle,
    +    Wangle,
    +    Wanglemax,
    +    Wanglemin,
    +    Zangletests,
    +    Wareatot,
    +    Wareamax,
    +    Wareamin,
    +    Zavoidold,
    +    Wavoidoldmax,
    +    Wavoidoldtot,
    +    Zback0,
    +    Zbestcentrum,
    +    Zbestdist,
    +    Zbestlower,
    +    Zbestlowerall,
    +    Zbestloweralln,
    +    Zbestlowerv,
    +    Zcentrumtests,
    +    Zcheckpart,
    +    Zcomputefurthest,
    +    Zconcave,
    +    Wconcavemax,
    +    Wconcavetot,
    +    Zconcaveridges,
    +    Zconcaveridge,
    +    Zcoplanar,
    +    Wcoplanarmax,
    +    Wcoplanartot,
    +    Zcoplanarangle,
    +    Zcoplanarcentrum,
    +    Zcoplanarhorizon,
    +    Zcoplanarinside,
    +    Zcoplanarpart,
    +    Zcoplanarridges,
    +    Wcpu,
    +    Zcyclefacetmax,
    +    Zcyclefacettot,
    +    Zcyclehorizon,
    +    Zcyclevertex,
    +    Zdegen,
    +    Wdegenmax,
    +    Wdegentot,
    +    Zdegenvertex,
    +    Zdelfacetdup,
    +    Zdelridge,
    +    Zdelvertextot,
    +    Zdelvertexmax,
    +    Zdetsimplex,
    +    Zdistcheck,
    +    Zdistconvex,
    +    Zdistgood,
    +    Zdistio,
    +    Zdistplane,
    +    Zdiststat,
    +    Zdistvertex,
    +    Zdistzero,
    +    Zdoc1,
    +    Zdoc2,
    +    Zdoc3,
    +    Zdoc4,
    +    Zdoc5,
    +    Zdoc6,
    +    Zdoc7,
    +    Zdoc8,
    +    Zdoc9,
    +    Zdoc10,
    +    Zdoc11,
    +    Zdoc12,
    +    Zdropdegen,
    +    Zdropneighbor,
    +    Zdupflip,
    +    Zduplicate,
    +    Wduplicatemax,
    +    Wduplicatetot,
    +    Zdupridge,
    +    Zdupsame,
    +    Zflipped,
    +    Wflippedmax,
    +    Wflippedtot,
    +    Zflippedfacets,
    +    Zfindbest,
    +    Zfindbestmax,
    +    Zfindbesttot,
    +    Zfindcoplanar,
    +    Zfindfail,
    +    Zfindhorizon,
    +    Zfindhorizonmax,
    +    Zfindhorizontot,
    +    Zfindjump,
    +    Zfindnew,
    +    Zfindnewmax,
    +    Zfindnewtot,
    +    Zfindnewjump,
    +    Zfindnewsharp,
    +    Zgauss0,
    +    Zgoodfacet,
    +    Zhashlookup,
    +    Zhashridge,
    +    Zhashridgetest,
    +    Zhashtests,
    +    Zinsidevisible,
    +    Zintersect,
    +    Zintersectfail,
    +    Zintersectmax,
    +    Zintersectnum,
    +    Zintersecttot,
    +    Zmaxneighbors,
    +    Wmaxout,
    +    Wmaxoutside,
    +    Zmaxridges,
    +    Zmaxvertex,
    +    Zmaxvertices,
    +    Zmaxvneighbors,
    +    Zmemfacets,
    +    Zmempoints,
    +    Zmemridges,
    +    Zmemvertices,
    +    Zmergeflipdup,
    +    Zmergehorizon,
    +    Zmergeinittot,
    +    Zmergeinitmax,
    +    Zmergeinittot2,
    +    Zmergeintohorizon,
    +    Zmergenew,
    +    Zmergesettot,
    +    Zmergesetmax,
    +    Zmergesettot2,
    +    Zmergesimplex,
    +    Zmergevertex,
    +    Wmindenom,
    +    Wminvertex,
    +    Zminnorm,
    +    Zmultiridge,
    +    Znearlysingular,
    +    Zneighbor,
    +    Wnewbalance,
    +    Wnewbalance2,
    +    Znewfacettot,
    +    Znewfacetmax,
    +    Znewvertex,
    +    Wnewvertex,
    +    Wnewvertexmax,
    +    Znoarea,
    +    Znonsimplicial,
    +    Znowsimplicial,
    +    Znotgood,
    +    Znotgoodnew,
    +    Znotmax,
    +    Znumfacets,
    +    Znummergemax,
    +    Znummergetot,
    +    Znumneighbors,
    +    Znumridges,
    +    Znumvertices,
    +    Znumvisibility,
    +    Znumvneighbors,
    +    Zonehorizon,
    +    Zpartangle,
    +    Zpartcoplanar,
    +    Zpartflip,
    +    Zparthorizon,
    +    Zpartinside,
    +    Zpartition,
    +    Zpartitionall,
    +    Zpartnear,
    +    Zpbalance,
    +    Wpbalance,
    +    Wpbalance2,
    +    Zpostfacets,
    +    Zpremergetot,
    +    Zprocessed,
    +    Zremvertex,
    +    Zremvertexdel,
    +    Zrenameall,
    +    Zrenamepinch,
    +    Zrenameshare,
    +    Zretry,
    +    Wretrymax,
    +    Zridge,
    +    Wridge,
    +    Wridgemax,
    +    Zridge0,
    +    Wridge0,
    +    Wridge0max,
    +    Zridgemid,
    +    Wridgemid,
    +    Wridgemidmax,
    +    Zridgeok,
    +    Wridgeok,
    +    Wridgeokmax,
    +    Zsearchpoints,
    +    Zsetplane,
    +    Ztestvneighbor,
    +    Ztotcheck,
    +    Ztothorizon,
    +    Ztotmerge,
    +    Ztotpartcoplanar,
    +    Ztotpartition,
    +    Ztotridges,
    +    Ztotvertices,
    +    Ztotvisible,
    +    Ztricoplanar,
    +    Ztricoplanarmax,
    +    Ztricoplanartot,
    +    Ztridegen,
    +    Ztrimirror,
    +    Ztrinull,
    +    Wvertexmax,
    +    Wvertexmin,
    +    Zvertexridge,
    +    Zvertexridgetot,
    +    Zvertexridgemax,
    +    Zvertices,
    +    Zvisfacettot,
    +    Zvisfacetmax,
    +    Zvisit,
    +    Zvisit2max,
    +    Zvisvertextot,
    +    Zvisvertexmax,
    +    Zvvisit,
    +    Zvvisit2max,
    +    Zwidefacet,
    +    Zwidevertices,
    +    ZEND};
    +
    +/*---------------------------------
    +
    +  Zxxx/Wxxx statistics that remain defined if qh_KEEPstatistics=0
    +
    +  notes:
    +    be sure to use zzdef, zzinc, etc. with these statistics (no double checking!)
    +*/
    +#else
    +enum qh_statistics {     /* for zzdef etc. macros */
    +  Zback0,
    +  Zbestdist,
    +  Zcentrumtests,
    +  Zcheckpart,
    +  Zconcaveridges,
    +  Zcoplanarhorizon,
    +  Zcoplanarpart,
    +  Zcoplanarridges,
    +  Zcyclefacettot,
    +  Zcyclehorizon,
    +  Zdelvertextot,
    +  Zdistcheck,
    +  Zdistconvex,
    +  Zdistzero,
    +  Zdoc1,
    +  Zdoc2,
    +  Zdoc3,
    +  Zdoc11,
    +  Zflippedfacets,
    +  Zgauss0,
    +  Zminnorm,
    +  Zmultiridge,
    +  Znearlysingular,
    +  Wnewvertexmax,
    +  Znumvisibility,
    +  Zpartcoplanar,
    +  Zpartition,
    +  Zpartitionall,
    +  Zprocessed,
    +  Zretry,
    +  Zridge,
    +  Wridge,
    +  Wridgemax,
    +  Zridge0,
    +  Wridge0,
    +  Wridge0max,
    +  Zridgemid,
    +  Wridgemid,
    +  Wridgemidmax,
    +  Zridgeok,
    +  Wridgeok,
    +  Wridgeokmax,
    +  Zsetplane,
    +  Ztotcheck,
    +  Ztotmerge,
    +    ZEND};
    +#endif
    +
    +/*---------------------------------
    +
    +  ztype
    +    the type of a statistic sets its initial value.
    +
    +  notes:
    +    The type should be the same as the macro for collecting the statistic
    +*/
    +enum ztypes {zdoc,zinc,zadd,zmax,zmin,ZTYPEreal,wadd,wmax,wmin,ZTYPEend};
    +
    +/*========== macros and constants =============*/
    +
    +/*----------------------------------
    +
    +  MAYdebugx
    +    define as maydebug() to be called frequently for error trapping
    +*/
    +#define MAYdebugx
    +
    +/*----------------------------------
    +
    +  zzdef_, zdef_( type, name, doc, -1)
    +    define a statistic (assumes 'qhstat.next= 0;')
    +
    +  zdef_( type, name, doc, count)
    +    define an averaged statistic
    +    printed as name/count
    +*/
    +#define zzdef_(stype,name,string,cnt) qh->qhstat.id[qh->qhstat.next++]=name; \
    +   qh->qhstat.doc[name]= string; qh->qhstat.count[name]= cnt; qh->qhstat.type[name]= stype
    +#if qh_KEEPstatistics
    +#define zdef_(stype,name,string,cnt) qh->qhstat.id[qh->qhstat.next++]=name; \
    +   qh->qhstat.doc[name]= string; qh->qhstat.count[name]= cnt; qh->qhstat.type[name]= stype
    +#else
    +#define zdef_(type,name,doc,count)
    +#endif
    +
    +/*----------------------------------
    +
    +  zzinc_( name ), zinc_( name)
    +    increment an integer statistic
    +*/
    +#define zzinc_(id) {MAYdebugx; qh->qhstat.stats[id].i++;}
    +#if qh_KEEPstatistics
    +#define zinc_(id) {MAYdebugx; qh->qhstat.stats[id].i++;}
    +#else
    +#define zinc_(id) {}
    +#endif
    +
    +/*----------------------------------
    +
    +  zzadd_( name, value ), zadd_( name, value ), wadd_( name, value )
    +    add value to an integer or real statistic
    +*/
    +#define zzadd_(id, val) {MAYdebugx; qh->qhstat.stats[id].i += (val);}
    +#define wwadd_(id, val) {MAYdebugx; qh->qhstat.stats[id].r += (val);}
    +#if qh_KEEPstatistics
    +#define zadd_(id, val) {MAYdebugx; qh->qhstat.stats[id].i += (val);}
    +#define wadd_(id, val) {MAYdebugx; qh->qhstat.stats[id].r += (val);}
    +#else
    +#define zadd_(id, val) {}
    +#define wadd_(id, val) {}
    +#endif
    +
    +/*----------------------------------
    +
    +  zzval_( name ), zval_( name ), wwval_( name )
    +    set or return value of a statistic
    +*/
    +#define zzval_(id) ((qh->qhstat.stats[id]).i)
    +#define wwval_(id) ((qh->qhstat.stats[id]).r)
    +#if qh_KEEPstatistics
    +#define zval_(id) ((qh->qhstat.stats[id]).i)
    +#define wval_(id) ((qh->qhstat.stats[id]).r)
    +#else
    +#define zval_(id) qh->qhstat.tempi
    +#define wval_(id) qh->qhstat.tempr
    +#endif
    +
    +/*----------------------------------
    +
    +  zmax_( id, val ), wmax_( id, value )
    +    maximize id with val
    +*/
    +#define wwmax_(id, val) {MAYdebugx; maximize_(qh->qhstat.stats[id].r,(val));}
    +#if qh_KEEPstatistics
    +#define zmax_(id, val) {MAYdebugx; maximize_(qh->qhstat.stats[id].i,(val));}
    +#define wmax_(id, val) {MAYdebugx; maximize_(qh->qhstat.stats[id].r,(val));}
    +#else
    +#define zmax_(id, val) {}
    +#define wmax_(id, val) {}
    +#endif
    +
    +/*----------------------------------
    +
    +  zmin_( id, val ), wmin_( id, value )
    +    minimize id with val
    +*/
    +#if qh_KEEPstatistics
    +#define zmin_(id, val) {MAYdebugx; minimize_(qh->qhstat.stats[id].i,(val));}
    +#define wmin_(id, val) {MAYdebugx; minimize_(qh->qhstat.stats[id].r,(val));}
    +#else
    +#define zmin_(id, val) {}
    +#define wmin_(id, val) {}
    +#endif
    +
    +/*================== stat_r.h types ==============*/
    +
    +
    +/*----------------------------------
    +
    +  intrealT
    +    union of integer and real, used for statistics
    +*/
    +typedef union intrealT intrealT;    /* union of int and realT */
    +union intrealT {
    +    int i;
    +    realT r;
    +};
    +
    +/*----------------------------------
    +
    +  qhstat
    +    Data structure for statistics, similar to qh and qhrbox
    +
    +    Allocated as part of qhT (libqhull_r.h)
    +*/
    +
    +struct qhstatT {
    +  intrealT   stats[ZEND];     /* integer and real statistics */
    +  unsigned   char id[ZEND+10]; /* id's in print order */
    +  const char *doc[ZEND];       /* array of documentation strings */
    +  short int  count[ZEND];     /* -1 if none, else index of count to use */
    +  char       type[ZEND];      /* type, see ztypes above */
    +  char       printed[ZEND];   /* true, if statistic has been printed */
    +  intrealT   init[ZTYPEend];  /* initial values by types, set initstatistics */
    +
    +  int        next;            /* next index for zdef_ */
    +  int        precision;       /* index for precision problems */
    +  int        vridges;         /* index for Voronoi ridges */
    +  int        tempi;
    +  realT      tempr;
    +};
    +
    +/*========== function prototypes ===========*/
    +
    +#ifdef __cplusplus
    +extern "C" {
    +#endif
    +
    +void    qh_allstatA(qhT *qh);
    +void    qh_allstatB(qhT *qh);
    +void    qh_allstatC(qhT *qh);
    +void    qh_allstatD(qhT *qh);
    +void    qh_allstatE(qhT *qh);
    +void    qh_allstatE2(qhT *qh);
    +void    qh_allstatF(qhT *qh);
    +void    qh_allstatG(qhT *qh);
    +void    qh_allstatH(qhT *qh);
    +void    qh_allstatI(qhT *qh);
    +void    qh_allstatistics(qhT *qh);
    +void    qh_collectstatistics(qhT *qh);
    +void    qh_initstatistics(qhT *qh);
    +boolT   qh_newstats(qhT *qh, int idx, int *nextindex);
    +boolT   qh_nostatistic(qhT *qh, int i);
    +void    qh_printallstatistics(qhT *qh, FILE *fp, const char *string);
    +void    qh_printstatistics(qhT *qh, FILE *fp, const char *string);
    +void    qh_printstatlevel(qhT *qh, FILE *fp, int id);
    +void    qh_printstats(qhT *qh, FILE *fp, int idx, int *nextindex);
    +realT   qh_stddev(int num, realT tot, realT tot2, realT *ave);
    +
    +#ifdef __cplusplus
    +} /* extern "C" */
    +#endif
    +
    +#endif   /* qhDEFstat */
    diff --git a/xs/src/qhull/src/libqhull_r/user_r.c b/xs/src/qhull/src/libqhull_r/user_r.c
    new file mode 100644
    index 000000000..bf7ed1d8d
    --- /dev/null
    +++ b/xs/src/qhull/src/libqhull_r/user_r.c
    @@ -0,0 +1,527 @@
    +/*
      ---------------------------------
    +
    +   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 
    +
    +/*---------------------------------
    +
    +  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
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  qh_user_memsizes(qh)
    +    allocate up to 10 additional, quick allocation sizes
    +
    +  notes:
    +    increase maximum number of allocations in qh_initqhull_mem()
    +*/
    +void qh_user_memsizes(qhT *qh) {
    +
    +  QHULL_UNUSED(qh)
    +  /* qh_memsize(qh, size); */
    +} /* user_memsizes */
    +
    +
    diff --git a/xs/src/qhull/src/libqhull_r/user_r.h b/xs/src/qhull/src/libqhull_r/user_r.h
    new file mode 100644
    index 000000000..7cca65a80
    --- /dev/null
    +++ b/xs/src/qhull/src/libqhull_r/user_r.h
    @@ -0,0 +1,882 @@
    +/*
      ---------------------------------
    +
    +   user.h
    +   user redefinable constants
    +
    +   for each source file, user_r.h is included first
    +
    +   see qh-user_r.htm.  see COPYING for copyright information.
    +
    +   See user_r.c for sample code.
    +
    +   before reading any code, review libqhull_r.h for data structure definitions
    +
    +Sections:
    +   ============= qhull library constants ======================
    +   ============= data types and configuration macros ==========
    +   ============= performance related constants ================
    +   ============= memory constants =============================
    +   ============= joggle constants =============================
    +   ============= conditional compilation ======================
    +   ============= -merge constants- ============================
    +
    +Code flags --
    +  NOerrors -- the code does not call qh_errexit()
    +  WARN64 -- the code may be incompatible with 64-bit pointers
    +
    +*/
    +
    +#include 
    +
    +#ifndef qhDEFuser
    +#define qhDEFuser 1
    +
    +/* Derived from Qt's corelib/global/qglobal.h */
    +#if !defined(SAG_COM) && !defined(__CYGWIN__) && (defined(WIN64) || defined(_WIN64) || defined(__WIN64__) || defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__))
    +#   define QHULL_OS_WIN
    +#elif defined(__MWERKS__) && defined(__INTEL__) /* Metrowerks discontinued before the release of Intel Macs */
    +#   define QHULL_OS_WIN
    +#endif
    +
    +/*============================================================*/
    +/*============= qhull library constants ======================*/
    +/*============================================================*/
    +
    +/*----------------------------------
    +
    +  FILENAMElen -- max length for TI and TO filenames
    +
    +*/
    +
    +#define qh_FILENAMElen 500
    +
    +/*----------------------------------
    +
    +  msgcode -- Unique message codes for qh_fprintf
    +
    +  If add new messages, assign these values and increment in user.h and user_r.h
    +  See QhullError.h for 10000 errors.
    +
    +  def counters =  [27, 1048, 2059, 3026, 4068, 5003,
    +     6273, 7081, 8147, 9411, 10000, 11029]
    +
    +  See: qh_ERR* [libqhull_r.h]
    +*/
    +
    +#define MSG_TRACE0 0
    +#define MSG_TRACE1 1000
    +#define MSG_TRACE2 2000
    +#define MSG_TRACE3 3000
    +#define MSG_TRACE4 4000
    +#define MSG_TRACE5 5000
    +#define MSG_ERROR  6000   /* errors written to qh.ferr */
    +#define MSG_WARNING 7000
    +#define MSG_STDERR  8000  /* log messages Written to qh.ferr */
    +#define MSG_OUTPUT  9000
    +#define MSG_QHULL_ERROR 10000 /* errors thrown by QhullError.cpp (QHULLlastError is in QhullError.h) */
    +#define MSG_FIXUP  11000  /* FIXUP QH11... */
    +#define MSG_MAXLEN  3000 /* qh_printhelp_degenerate() in user.c */
    +
    +
    +/*----------------------------------
    +
    +  qh_OPTIONline -- max length of an option line 'FO'
    +*/
    +#define qh_OPTIONline 80
    +
    +/*============================================================*/
    +/*============= data types and configuration macros ==========*/
    +/*============================================================*/
    +
    +/*----------------------------------
    +
    +  realT
    +    set the size of floating point numbers
    +
    +  qh_REALdigits
    +    maximimum number of significant digits
    +
    +  qh_REAL_1, qh_REAL_2n, qh_REAL_3n
    +    format strings for printf
    +
    +  qh_REALmax, qh_REALmin
    +    maximum and minimum (near zero) values
    +
    +  qh_REALepsilon
    +    machine roundoff.  Maximum roundoff error for addition and multiplication.
    +
    +  notes:
    +   Select whether to store floating point numbers in single precision (float)
    +   or double precision (double).
    +
    +   Use 'float' to save about 8% in time and 25% in space.  This is particularly
    +   helpful if high-d where convex hulls are space limited.  Using 'float' also
    +   reduces the printed size of Qhull's output since numbers have 8 digits of
    +   precision.
    +
    +   Use 'double' when greater arithmetic precision is needed.  This is needed
    +   for Delaunay triangulations and Voronoi diagrams when you are not merging
    +   facets.
    +
    +   If 'double' gives insufficient precision, your data probably includes
    +   degeneracies.  If so you should use facet merging (done by default)
    +   or exact arithmetic (see imprecision section of manual, qh-impre.htm).
    +   You may also use option 'Po' to force output despite precision errors.
    +
    +   You may use 'long double', but many format statements need to be changed
    +   and you may need a 'long double' square root routine.  S. Grundmann
    +   (sg@eeiwzb.et.tu-dresden.de) has done this.  He reports that the code runs
    +   much slower with little gain in precision.
    +
    +   WARNING: on some machines,    int f(){realT a= REALmax;return (a == REALmax);}
    +      returns False.  Use (a > REALmax/2) instead of (a == REALmax).
    +
    +   REALfloat =   1      all numbers are 'float' type
    +             =   0      all numbers are 'double' type
    +*/
    +#define REALfloat 1
    +
    +#if (REALfloat == 1)
    +#define realT float
    +#define REALmax FLT_MAX
    +#define REALmin FLT_MIN
    +#define REALepsilon FLT_EPSILON
    +#define qh_REALdigits 8   /* maximum number of significant digits */
    +#define qh_REAL_1 "%6.8g "
    +#define qh_REAL_2n "%6.8g %6.8g\n"
    +#define qh_REAL_3n "%6.8g %6.8g %6.8g\n"
    +
    +#elif (REALfloat == 0)
    +#define realT double
    +#define REALmax DBL_MAX
    +#define REALmin DBL_MIN
    +#define REALepsilon DBL_EPSILON
    +#define qh_REALdigits 16    /* maximum number of significant digits */
    +#define qh_REAL_1 "%6.16g "
    +#define qh_REAL_2n "%6.16g %6.16g\n"
    +#define qh_REAL_3n "%6.16g %6.16g %6.16g\n"
    +
    +#else
    +#error unknown float option
    +#endif
    +
    +/*----------------------------------
    +
    +  countT
    +    The type for counts and identifiers (e.g., the number of points, vertex identifiers)
    +    Currently used by C++ code-only.  Decided against using it for setT because most sets are small.
    +
    +    Defined as 'int' for C-code compatibility and QH11026
    +
    +    FIXUP QH11026 countT may be defined as a unsigned value, but several code issues need to be solved first.  See countT in Changes.txt
    +*/
    +
    +#ifndef DEFcountT
    +#define DEFcountT 1
    +typedef int countT;
    +#endif
    +#define COUNTmax 0x7fffffff
    +
    +
    +/*----------------------------------
    +
    +  qh_CPUclock
    +    define the clock() function for reporting the total time spent by Qhull
    +    returns CPU ticks as a 'long int'
    +    qh_CPUclock is only used for reporting the total time spent by Qhull
    +
    +  qh_SECticks
    +    the number of clock ticks per second
    +
    +  notes:
    +    looks for CLOCKS_PER_SEC, CLOCKS_PER_SECOND, or assumes microseconds
    +    to define a custom clock, set qh_CLOCKtype to 0
    +
    +    if your system does not use clock() to return CPU ticks, replace
    +    qh_CPUclock with the corresponding function.  It is converted
    +    to 'unsigned long' to prevent wrap-around during long runs.  By default,
    +     defines clock_t as 'long'
    +
    +   Set qh_CLOCKtype to
    +
    +     1          for CLOCKS_PER_SEC, CLOCKS_PER_SECOND, or microsecond
    +                Note:  may fail if more than 1 hour elapsed time
    +
    +     2          use qh_clock() with POSIX times() (see global_r.c)
    +*/
    +#define qh_CLOCKtype 1  /* change to the desired number */
    +
    +#if (qh_CLOCKtype == 1)
    +
    +#if defined(CLOCKS_PER_SECOND)
    +#define qh_CPUclock    ((unsigned long)clock())  /* return CPU clock */
    +#define qh_SECticks CLOCKS_PER_SECOND
    +
    +#elif defined(CLOCKS_PER_SEC)
    +#define qh_CPUclock    ((unsigned long)clock())  /* return CPU clock */
    +#define qh_SECticks CLOCKS_PER_SEC
    +
    +#elif defined(CLK_TCK)
    +#define qh_CPUclock    ((unsigned long)clock())  /* return CPU clock */
    +#define qh_SECticks CLK_TCK
    +
    +#else
    +#define qh_CPUclock    ((unsigned long)clock())  /* return CPU clock */
    +#define qh_SECticks 1E6
    +#endif
    +
    +#elif (qh_CLOCKtype == 2)
    +#define qh_CPUclock    qh_clock()  /* return CPU clock */
    +#define qh_SECticks 100
    +
    +#else /* qh_CLOCKtype == ? */
    +#error unknown clock option
    +#endif
    +
    +/*----------------------------------
    +
    +  qh_RANDOMtype, qh_RANDOMmax, qh_RANDOMseed
    +    define random number generator
    +
    +    qh_RANDOMint generates a random integer between 0 and qh_RANDOMmax.
    +    qh_RANDOMseed sets the random number seed for qh_RANDOMint
    +
    +  Set qh_RANDOMtype (default 5) to:
    +    1       for random() with 31 bits (UCB)
    +    2       for rand() with RAND_MAX or 15 bits (system 5)
    +    3       for rand() with 31 bits (Sun)
    +    4       for lrand48() with 31 bits (Solaris)
    +    5       for qh_rand(qh) with 31 bits (included with Qhull, requires 'qh')
    +
    +  notes:
    +    Random numbers are used by rbox to generate point sets.  Random
    +    numbers are used by Qhull to rotate the input ('QRn' option),
    +    simulate a randomized algorithm ('Qr' option), and to simulate
    +    roundoff errors ('Rn' option).
    +
    +    Random number generators differ between systems.  Most systems provide
    +    rand() but the period varies.  The period of rand() is not critical
    +    since qhull does not normally use random numbers.
    +
    +    The default generator is Park & Miller's minimal standard random
    +    number generator [CACM 31:1195 '88].  It is included with Qhull.
    +
    +    If qh_RANDOMmax is wrong, qhull will report a warning and Geomview
    +    output will likely be invisible.
    +*/
    +#define qh_RANDOMtype 5   /* *** change to the desired number *** */
    +
    +#if (qh_RANDOMtype == 1)
    +#define qh_RANDOMmax ((realT)0x7fffffffUL)  /* 31 bits, random()/MAX */
    +#define qh_RANDOMint random()
    +#define qh_RANDOMseed_(qh, seed) srandom(seed);
    +
    +#elif (qh_RANDOMtype == 2)
    +#ifdef RAND_MAX
    +#define qh_RANDOMmax ((realT)RAND_MAX)
    +#else
    +#define qh_RANDOMmax ((realT)32767)   /* 15 bits (System 5) */
    +#endif
    +#define qh_RANDOMint  rand()
    +#define qh_RANDOMseed_(qh, seed) srand((unsigned)seed);
    +
    +#elif (qh_RANDOMtype == 3)
    +#define qh_RANDOMmax ((realT)0x7fffffffUL)  /* 31 bits, Sun */
    +#define qh_RANDOMint  rand()
    +#define qh_RANDOMseed_(qh, seed) srand((unsigned)seed);
    +
    +#elif (qh_RANDOMtype == 4)
    +#define qh_RANDOMmax ((realT)0x7fffffffUL)  /* 31 bits, lrand38()/MAX */
    +#define qh_RANDOMint lrand48()
    +#define qh_RANDOMseed_(qh, seed) srand48(seed);
    +
    +#elif (qh_RANDOMtype == 5)  /* 'qh' is an implicit parameter */
    +#define qh_RANDOMmax ((realT)2147483646UL)  /* 31 bits, qh_rand/MAX */
    +#define qh_RANDOMint qh_rand(qh)
    +#define qh_RANDOMseed_(qh, seed) qh_srand(qh, seed);
    +/* unlike rand(), never returns 0 */
    +
    +#else
    +#error: unknown random option
    +#endif
    +
    +/*----------------------------------
    +
    +  qh_ORIENTclock
    +    0 for inward pointing normals by Geomview convention
    +*/
    +#define qh_ORIENTclock 0
    +
    +
    +/*============================================================*/
    +/*============= joggle constants =============================*/
    +/*============================================================*/
    +
    +/*----------------------------------
    +
    +qh_JOGGLEdefault
    +default qh.JOGGLEmax is qh.DISTround * qh_JOGGLEdefault
    +
    +notes:
    +rbox s r 100 | qhull QJ1e-15 QR0 generates 90% faults at distround 7e-16
    +rbox s r 100 | qhull QJ1e-14 QR0 generates 70% faults
    +rbox s r 100 | qhull QJ1e-13 QR0 generates 35% faults
    +rbox s r 100 | qhull QJ1e-12 QR0 generates 8% faults
    +rbox s r 100 | qhull QJ1e-11 QR0 generates 1% faults
    +rbox s r 100 | qhull QJ1e-10 QR0 generates 0% faults
    +rbox 1000 W0 | qhull QJ1e-12 QR0 generates 86% faults
    +rbox 1000 W0 | qhull QJ1e-11 QR0 generates 20% faults
    +rbox 1000 W0 | qhull QJ1e-10 QR0 generates 2% faults
    +the later have about 20 points per facet, each of which may interfere
    +
    +pick a value large enough to avoid retries on most inputs
    +*/
    +#define qh_JOGGLEdefault 30000.0
    +
    +/*----------------------------------
    +
    +qh_JOGGLEincrease
    +factor to increase qh.JOGGLEmax on qh_JOGGLEretry or qh_JOGGLEagain
    +*/
    +#define qh_JOGGLEincrease 10.0
    +
    +/*----------------------------------
    +
    +qh_JOGGLEretry
    +if ZZretry = qh_JOGGLEretry, increase qh.JOGGLEmax
    +
    +notes:
    +try twice at the original value in case of bad luck the first time
    +*/
    +#define qh_JOGGLEretry 2
    +
    +/*----------------------------------
    +
    +qh_JOGGLEagain
    +every following qh_JOGGLEagain, increase qh.JOGGLEmax
    +
    +notes:
    +1 is OK since it's already failed qh_JOGGLEretry times
    +*/
    +#define qh_JOGGLEagain 1
    +
    +/*----------------------------------
    +
    +qh_JOGGLEmaxincrease
    +maximum qh.JOGGLEmax due to qh_JOGGLEincrease
    +relative to qh.MAXwidth
    +
    +notes:
    +qh.joggleinput will retry at this value until qh_JOGGLEmaxretry
    +*/
    +#define qh_JOGGLEmaxincrease 1e-2
    +
    +/*----------------------------------
    +
    +qh_JOGGLEmaxretry
    +stop after qh_JOGGLEmaxretry attempts
    +*/
    +#define qh_JOGGLEmaxretry 100
    +
    +/*============================================================*/
    +/*============= performance related constants ================*/
    +/*============================================================*/
    +
    +/*----------------------------------
    +
    +  qh_HASHfactor
    +    total hash slots / used hash slots.  Must be at least 1.1.
    +
    +  notes:
    +    =2 for at worst 50% occupancy for qh.hash_table and normally 25% occupancy
    +*/
    +#define qh_HASHfactor 2
    +
    +/*----------------------------------
    +
    +  qh_VERIFYdirect
    +    with 'Tv' verify all points against all facets if op count is smaller
    +
    +  notes:
    +    if greater, calls qh_check_bestdist() instead
    +*/
    +#define qh_VERIFYdirect 1000000
    +
    +/*----------------------------------
    +
    +  qh_INITIALsearch
    +     if qh_INITIALmax, search points up to this dimension
    +*/
    +#define qh_INITIALsearch 6
    +
    +/*----------------------------------
    +
    +  qh_INITIALmax
    +    if dim >= qh_INITIALmax, use min/max coordinate points for initial simplex
    +
    +  notes:
    +    from points with non-zero determinants
    +    use option 'Qs' to override (much slower)
    +*/
    +#define qh_INITIALmax 8
    +
    +/*============================================================*/
    +/*============= memory constants =============================*/
    +/*============================================================*/
    +
    +/*----------------------------------
    +
    +  qh_MEMalign
    +    memory alignment for qh_meminitbuffers() in global_r.c
    +
    +  notes:
    +    to avoid bus errors, memory allocation must consider alignment requirements.
    +    malloc() automatically takes care of alignment.   Since mem_r.c manages
    +    its own memory, we need to explicitly specify alignment in
    +    qh_meminitbuffers().
    +
    +    A safe choice is sizeof(double).  sizeof(float) may be used if doubles
    +    do not occur in data structures and pointers are the same size.  Be careful
    +    of machines (e.g., DEC Alpha) with large pointers.
    +
    +    If using gcc, best alignment is [fmax_() is defined in geom_r.h]
    +              #define qh_MEMalign fmax_(__alignof__(realT),__alignof__(void *))
    +*/
    +#define qh_MEMalign ((int)(fmax_(sizeof(realT), sizeof(void *))))
    +
    +/*----------------------------------
    +
    +  qh_MEMbufsize
    +    size of additional memory buffers
    +
    +  notes:
    +    used for qh_meminitbuffers() in global_r.c
    +*/
    +#define qh_MEMbufsize 0x10000       /* allocate 64K memory buffers */
    +
    +/*----------------------------------
    +
    +  qh_MEMinitbuf
    +    size of initial memory buffer
    +
    +  notes:
    +    use for qh_meminitbuffers() in global_r.c
    +*/
    +#define qh_MEMinitbuf 0x20000      /* initially allocate 128K buffer */
    +
    +/*----------------------------------
    +
    +  qh_INFINITE
    +    on output, indicates Voronoi center at infinity
    +*/
    +#define qh_INFINITE  -10.101
    +
    +/*----------------------------------
    +
    +  qh_DEFAULTbox
    +    default box size (Geomview expects 0.5)
    +
    +  qh_DEFAULTbox
    +    default box size for integer coorindate (rbox only)
    +*/
    +#define qh_DEFAULTbox 0.5
    +#define qh_DEFAULTzbox 1e6
    +
    +/*============================================================*/
    +/*============= conditional compilation ======================*/
    +/*============================================================*/
    +
    +/*----------------------------------
    +
    +  __cplusplus
    +    defined by C++ compilers
    +
    +  __MSC_VER
    +    defined by Microsoft Visual C++
    +
    +  __MWERKS__ && __INTEL__
    +    defined by Metrowerks when compiling for Windows (not Intel-based Macintosh)
    +
    +  __MWERKS__ && __POWERPC__
    +    defined by Metrowerks when compiling for PowerPC-based Macintosh
    +
    +  __STDC__
    +    defined for strict ANSI C
    +*/
    +
    +/*----------------------------------
    +
    +  qh_COMPUTEfurthest
    +    compute furthest distance to an outside point instead of storing it with the facet
    +    =1 to compute furthest
    +
    +  notes:
    +    computing furthest saves memory but costs time
    +      about 40% more distance tests for partitioning
    +      removes facet->furthestdist
    +*/
    +#define qh_COMPUTEfurthest 0
    +
    +/*----------------------------------
    +
    +  qh_KEEPstatistics
    +    =0 removes most of statistic gathering and reporting
    +
    +  notes:
    +    if 0, code size is reduced by about 4%.
    +*/
    +#define qh_KEEPstatistics 1
    +
    +/*----------------------------------
    +
    +  qh_MAXoutside
    +    record outer plane for each facet
    +    =1 to record facet->maxoutside
    +
    +  notes:
    +    this takes a realT per facet and slightly slows down qhull
    +    it produces better outer planes for geomview output
    +*/
    +#define qh_MAXoutside 1
    +
    +/*----------------------------------
    +
    +  qh_NOmerge
    +    disables facet merging if defined
    +
    +  notes:
    +    This saves about 10% space.
    +
    +    Unless 'Q0'
    +      qh_NOmerge sets 'QJ' to avoid precision errors
    +
    +    #define qh_NOmerge
    +
    +  see:
    +    qh_NOmem in mem_r.c
    +
    +    see user_r.c/user_eg.c for removing io_r.o
    +*/
    +
    +/*----------------------------------
    +
    +  qh_NOtrace
    +    no tracing if defined
    +
    +  notes:
    +    This saves about 5% space.
    +
    +    #define qh_NOtrace
    +*/
    +
    +#if 0  /* sample code */
    +    exitcode= qh_new_qhull(qhT *qh, dim, numpoints, points, ismalloc,
    +                      flags, outfile, errfile);
    +    qh_freeqhull(qhT *qh, !qh_ALL); /* frees long memory used by second call */
    +    qh_memfreeshort(qhT *qh, &curlong, &totlong);  /* frees short memory and memory allocator */
    +#endif
    +
    +/*----------------------------------
    +
    +  qh_QUICKhelp
    +    =1 to use abbreviated help messages, e.g., for degenerate inputs
    +*/
    +#define qh_QUICKhelp    0
    +
    +/*============================================================*/
    +/*============= -merge constants- ============================*/
    +/*============================================================*/
    +/*
    +   These constants effect facet merging.  You probably will not need
    +   to modify them.  They effect the performance of facet merging.
    +*/
    +
    +/*----------------------------------
    +
    +  qh_DIMmergeVertex
    +    max dimension for vertex merging (it is not effective in high-d)
    +*/
    +#define qh_DIMmergeVertex 6
    +
    +/*----------------------------------
    +
    +  qh_DIMreduceBuild
    +     max dimension for vertex reduction during build (slow in high-d)
    +*/
    +#define qh_DIMreduceBuild 5
    +
    +/*----------------------------------
    +
    +  qh_BESTcentrum
    +     if > 2*dim+n vertices, qh_findbestneighbor() tests centrums (faster)
    +     else, qh_findbestneighbor() tests all vertices (much better merges)
    +
    +  qh_BESTcentrum2
    +     if qh_BESTcentrum2 * DIM3 + BESTcentrum < #vertices tests centrums
    +*/
    +#define qh_BESTcentrum 20
    +#define qh_BESTcentrum2 2
    +
    +/*----------------------------------
    +
    +  qh_BESTnonconvex
    +    if > dim+n neighbors, qh_findbestneighbor() tests nonconvex ridges.
    +
    +  notes:
    +    It is needed because qh_findbestneighbor is slow for large facets
    +*/
    +#define qh_BESTnonconvex 15
    +
    +/*----------------------------------
    +
    +  qh_MAXnewmerges
    +    if >n newmerges, qh_merge_nonconvex() calls qh_reducevertices_centrums.
    +
    +  notes:
    +    It is needed because postmerge can merge many facets at once
    +*/
    +#define qh_MAXnewmerges 2
    +
    +/*----------------------------------
    +
    +  qh_MAXnewcentrum
    +    if <= dim+n vertices (n approximates the number of merges),
    +      reset the centrum in qh_updatetested() and qh_mergecycle_facets()
    +
    +  notes:
    +    needed to reduce cost and because centrums may move too much if
    +    many vertices in high-d
    +*/
    +#define qh_MAXnewcentrum 5
    +
    +/*----------------------------------
    +
    +  qh_COPLANARratio
    +    for 3-d+ merging, qh.MINvisible is n*premerge_centrum
    +
    +  notes:
    +    for non-merging, it's DISTround
    +*/
    +#define qh_COPLANARratio 3
    +
    +/*----------------------------------
    +
    +  qh_DISToutside
    +    When is a point clearly outside of a facet?
    +    Stops search in qh_findbestnew or qh_partitionall
    +    qh_findbest uses qh.MINoutside since since it is only called if no merges.
    +
    +  notes:
    +    'Qf' always searches for best facet
    +    if !qh.MERGING, same as qh.MINoutside.
    +    if qh_USEfindbestnew, increase value since neighboring facets may be ill-behaved
    +      [Note: Zdelvertextot occurs normally with interior points]
    +            RBOX 1000 s Z1 G1e-13 t1001188774 | QHULL Tv
    +    When there is a sharp edge, need to move points to a
    +    clearly good facet; otherwise may be lost in another partitioning.
    +    if too big then O(n^2) behavior for partitioning in cone
    +    if very small then important points not processed
    +    Needed in qh_partitionall for
    +      RBOX 1000 s Z1 G1e-13 t1001032651 | QHULL Tv
    +    Needed in qh_findbestnew for many instances of
    +      RBOX 1000 s Z1 G1e-13 t | QHULL Tv
    +
    +  See:
    +    qh_DISToutside -- when is a point clearly outside of a facet
    +    qh_SEARCHdist -- when is facet coplanar with the best facet?
    +    qh_USEfindbestnew -- when to use qh_findbestnew for qh_partitionpoint()
    +*/
    +#define qh_DISToutside ((qh_USEfindbestnew ? 2 : 1) * \
    +     fmax_((qh->MERGING ? 2 : 1)*qh->MINoutside, qh->max_outside))
    +
    +/*----------------------------------
    +
    +  qh_RATIOnearinside
    +    ratio of qh.NEARinside to qh.ONEmerge for retaining inside points for
    +    qh_check_maxout().
    +
    +  notes:
    +    This is overkill since do not know the correct value.
    +    It effects whether 'Qc' reports all coplanar points
    +    Not used for 'd' since non-extreme points are coplanar
    +*/
    +#define qh_RATIOnearinside 5
    +
    +/*----------------------------------
    +
    +  qh_SEARCHdist
    +    When is a facet coplanar with the best facet?
    +    qh_findbesthorizon: all coplanar facets of the best facet need to be searched.
    +
    +  See:
    +    qh_DISToutside -- when is a point clearly outside of a facet
    +    qh_SEARCHdist -- when is facet coplanar with the best facet?
    +    qh_USEfindbestnew -- when to use qh_findbestnew for qh_partitionpoint()
    +*/
    +#define qh_SEARCHdist ((qh_USEfindbestnew ? 2 : 1) * \
    +      (qh->max_outside + 2 * qh->DISTround + fmax_( qh->MINvisible, qh->MAXcoplanar)));
    +
    +/*----------------------------------
    +
    +  qh_USEfindbestnew
    +     Always use qh_findbestnew for qh_partitionpoint, otherwise use
    +     qh_findbestnew if merged new facet or sharpnewfacets.
    +
    +  See:
    +    qh_DISToutside -- when is a point clearly outside of a facet
    +    qh_SEARCHdist -- when is facet coplanar with the best facet?
    +    qh_USEfindbestnew -- when to use qh_findbestnew for qh_partitionpoint()
    +*/
    +#define qh_USEfindbestnew (zzval_(Ztotmerge) > 50)
    +
    +/*----------------------------------
    +
    +  qh_WIDEcoplanar
    +    n*MAXcoplanar or n*MINvisible for a WIDEfacet
    +
    +    if vertex is further than qh.WIDEfacet from the hyperplane
    +    then its ridges are not counted in computing the area, and
    +    the facet's centrum is frozen.
    +
    +  notes:
    +   qh.WIDEfacet= max(qh.MAXoutside,qh_WIDEcoplanar*qh.MAXcoplanar,
    +      qh_WIDEcoplanar * qh.MINvisible);
    +*/
    +#define qh_WIDEcoplanar 6
    +
    +/*----------------------------------
    +
    +  qh_WIDEduplicate
    +    Merge ratio for errexit from qh_forcedmerges due to duplicate ridge
    +    Override with option Q12 no-wide-duplicate
    +
    +    Notes:
    +      Merging a duplicate ridge can lead to very wide facets.
    +      A future release of qhull will avoid duplicate ridges by removing duplicate sub-ridges from the horizon
    +*/
    +#define qh_WIDEduplicate 100
    +
    +/*----------------------------------
    +
    +  qh_MAXnarrow
    +    max. cosine in initial hull that sets qh.NARROWhull
    +
    +  notes:
    +    If qh.NARROWhull, the initial partition does not make
    +    coplanar points.  If narrow, a coplanar point can be
    +    coplanar to two facets of opposite orientations and
    +    distant from the exact convex hull.
    +
    +    Conservative estimate.  Don't actually see problems until it is -1.0
    +*/
    +#define qh_MAXnarrow -0.99999999
    +
    +/*----------------------------------
    +
    +  qh_WARNnarrow
    +    max. cosine in initial hull to warn about qh.NARROWhull
    +
    +  notes:
    +    this is a conservative estimate.
    +    Don't actually see problems until it is -1.0.  See qh-impre.htm
    +*/
    +#define qh_WARNnarrow -0.999999999999999
    +
    +/*----------------------------------
    +
    +  qh_ZEROdelaunay
    +    a zero Delaunay facet occurs for input sites coplanar with their convex hull
    +    the last normal coefficient of a zero Delaunay facet is within
    +        qh_ZEROdelaunay * qh.ANGLEround of 0
    +
    +  notes:
    +    qh_ZEROdelaunay does not allow for joggled input ('QJ').
    +
    +    You can avoid zero Delaunay facets by surrounding the input with a box.
    +
    +    Use option 'PDk:-n' to explicitly define zero Delaunay facets
    +      k= dimension of input sites (e.g., 3 for 3-d Delaunay triangulation)
    +      n= the cutoff for zero Delaunay facets (e.g., 'PD3:-1e-12')
    +*/
    +#define qh_ZEROdelaunay 2
    +
    +/*============================================================*/
    +/*============= Microsoft DevStudio ==========================*/
    +/*============================================================*/
    +
    +/*
    +   Finding Memory Leaks Using the CRT Library
    +   https://msdn.microsoft.com/en-us/library/x98tx3cf(v=vs.100).aspx
    +
    +   Reports enabled in qh_lib_check for Debug window and stderr
    +
    +   From 2005=>msvcr80d, 2010=>msvcr100d, 2012=>msvcr110d
    +
    +   Watch: {,,msvcr80d.dll}_crtBreakAlloc  Value from {n} in the leak report
    +   _CrtSetBreakAlloc(689); // qh_lib_check() [global_r.c]
    +
    +   Examples
    +     http://free-cad.sourceforge.net/SrcDocu/d2/d7f/MemDebug_8cpp_source.html
    +     https://github.com/illlust/Game/blob/master/library/MemoryLeak.cpp
    +*/
    +#if 0   /* off (0) by default for QHULL_CRTDBG */
    +#define QHULL_CRTDBG
    +#endif
    +
    +#if defined(_MSC_VER) && defined(_DEBUG) && defined(QHULL_CRTDBG)
    +#define _CRTDBG_MAP_ALLOC
    +#include 
    +#include 
    +#endif
    +
    +#endif /* qh_DEFuser */
    +
    +
    +
    diff --git a/xs/src/qhull/src/libqhull_r/usermem_r.c b/xs/src/qhull/src/libqhull_r/usermem_r.c
    new file mode 100644
    index 000000000..3297b0318
    --- /dev/null
    +++ b/xs/src/qhull/src/libqhull_r/usermem_r.c
    @@ -0,0 +1,94 @@
    +/*
      ---------------------------------
    +
    +   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 
    +#include 
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  qh_free(qhT *qh, mem )
    +    free memory
    +
    +  notes:
    +    same as free()
    +    No calls to qh_errexit() 
    +*/
    +void qh_free(void *mem) {
    +    free(mem);
    +} /* free */
    +
    +/*---------------------------------
    +
    +    qh_malloc( mem )
    +      allocate memory
    +
    +    notes:
    +      same as malloc()
    +*/
    +void *qh_malloc(size_t size) {
    +    return malloc(size);
    +} /* malloc */
    +
    +
    diff --git a/xs/src/qhull/src/libqhull_r/userprintf_r.c b/xs/src/qhull/src/libqhull_r/userprintf_r.c
    new file mode 100644
    index 000000000..6004491a1
    --- /dev/null
    +++ b/xs/src/qhull/src/libqhull_r/userprintf_r.c
    @@ -0,0 +1,65 @@
    +/*
      ---------------------------------
    +
    +   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 
    +#include 
    +#include 
    +
    +/*---------------------------------
    +
    +   qh_fprintf(qh, fp, msgcode, format, list of args )
    +     print arguments to *fp according to format
    +     Use qh_fprintf_rbox() for rboxlib_r.c
    +
    +   notes:
    +     same as fprintf()
    +     fgets() is not trapped like fprintf()
    +     exit qh_fprintf via qh_errexit()
    +     may be called for errors in qh_initstatistics and qh_meminit
    +*/
    +
    +void qh_fprintf(qhT *qh, FILE *fp, int msgcode, const char *fmt, ... ) {
    +    va_list args;
    +
    +    if (!fp) {
    +        if(!qh){
    +            qh_fprintf_stderr(6241, "userprintf_r.c: fp and qh not defined for qh_fprintf '%s'", fmt);
    +            qh_exit(qhmem_ERRqhull);  /* can not use qh_errexit() */
    +        }
    +        /* could use qh->qhmem.ferr, but probably better to be cautious */
    +        qh_fprintf_stderr(6232, "Qhull internal error (userprintf_r.c): fp is 0.  Wrong qh_fprintf called.\n");
    +        qh_errexit(qh, 6232, NULL, NULL);
    +    }
    +    va_start(args, fmt);
    +    if (qh && qh->ANNOTATEoutput) {
    +      fprintf(fp, "[QH%.4d]", msgcode);
    +    }else if (msgcode >= MSG_ERROR && msgcode < MSG_STDERR ) {
    +      fprintf(fp, "QH%.4d ", msgcode);
    +    }
    +    vfprintf(fp, fmt, args);
    +    va_end(args);
    +
    +    /* Place debugging traps here. Use with option 'Tn' */
    +
    +} /* qh_fprintf */
    +
    diff --git a/xs/src/qhull/src/libqhull_r/userprintf_rbox_r.c b/xs/src/qhull/src/libqhull_r/userprintf_rbox_r.c
    new file mode 100644
    index 000000000..1e721a22a
    --- /dev/null
    +++ b/xs/src/qhull/src/libqhull_r/userprintf_rbox_r.c
    @@ -0,0 +1,53 @@
    +/*
      ---------------------------------
    +
    +   userprintf_rbox_r.c
    +   qh_fprintf_rbox()
    +
    +   see README.txt  see COPYING.txt for copyright information.
    +
    +   If you recompile and load this file, then userprintf_rbox_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 
    +#include 
    +#include 
    +
    +/*---------------------------------
    +
    +   qh_fprintf_rbox(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_rbox via qh_errexit_rbox()
    +*/
    +
    +void qh_fprintf_rbox(qhT *qh, FILE *fp, int msgcode, const char *fmt, ... ) {
    +    va_list args;
    +
    +    if (!fp) {
    +        qh_fprintf_stderr(6231, "Qhull internal error (userprintf_rbox_r.c): fp is 0.  Wrong qh_fprintf_rbox called.\n");
    +        qh_errexit_rbox(qh, 6231);
    +    }
    +    if (msgcode >= MSG_ERROR && msgcode < MSG_STDERR)
    +      fprintf(fp, "QH%.4d ", msgcode);
    +    va_start(args, fmt);
    +    vfprintf(fp, fmt, args);
    +    va_end(args);
    +} /* qh_fprintf_rbox */
    +
    diff --git a/xs/src/qhull/src/libqhullcpp/Coordinates.cpp b/xs/src/qhull/src/libqhullcpp/Coordinates.cpp
    new file mode 100644
    index 000000000..806b438ab
    --- /dev/null
    +++ b/xs/src/qhull/src/libqhullcpp/Coordinates.cpp
    @@ -0,0 +1,198 @@
    +/****************************************************************************
    +**
    +** Copyright (c) 2009-2015 C.B. Barber. All rights reserved.
    +** $Id: //main/2015/qhull/src/libqhullcpp/Coordinates.cpp#4 $$Change: 2066 $
    +** $DateTime: 2016/01/18 19:29:17 $$Author: bbarber $
    +**
    +****************************************************************************/
    +
    +#include "libqhullcpp/Coordinates.h"
    +
    +#include "libqhullcpp/functionObjects.h"
    +#include "libqhullcpp/QhullError.h"
    +
    +#include 
    +#include 
    +#include 
    +
    +#ifdef _MSC_VER  // Microsoft Visual C++ -- warning level 4
    +#endif
    +
    +namespace orgQhull {
    +
    +#//! Coordinates -- vector of coordT (normally double)
    +
    +#//!\name Constructor
    +
    +#//!\name Element access
    +
    +// Inefficient without result-value-optimization or implicitly shared object
    +Coordinates Coordinates::
    +mid(countT idx, countT length) const
    +{
    +    countT newLength= length;
    +    if(length<0 || idx+length > count()){
    +        newLength= count()-idx;
    +    }
    +    Coordinates result;
    +    if(newLength>0){
    +        std::copy(begin()+idx, begin()+(idx+newLength), std::back_inserter(result));
    +    }
    +    return result;
    +}//mid
    +
    +coordT Coordinates::
    +value(countT idx, const coordT &defaultValue) const
    +{
    +    return ((idx < 0 || idx >= count()) ? defaultValue : (*this)[idx]);
    +}//value
    +
    +#//!\name GetSet
    +
    +Coordinates Coordinates::
    +operator+(const Coordinates &other) const
    +{
    +    Coordinates result(*this);
    +    std::copy(other.begin(), other.end(), std::back_inserter(result));
    +    return result;
    +}//operator+
    +
    +Coordinates & Coordinates::
    +operator+=(const Coordinates &other)
    +{
    +    if(&other==this){
    +        Coordinates clone(other);
    +        std::copy(clone.begin(), clone.end(), std::back_inserter(*this));
    +    }else{
    +        std::copy(other.begin(), other.end(), std::back_inserter(*this));
    +    }
    +    return *this;
    +}//operator+=
    +
    +#//!\name Read-write
    +
    +void Coordinates::
    +append(int pointDimension, coordT *c)
    +{
    +    if(c){
    +        coordT *p= c;
    +        for(int i= 0; i(i-begin())); // WARN64 coordinate index
    +            }
    +            ++i;
    +        }
    +    }
    +    return -1;
    +}//indexOf
    +
    +countT Coordinates::
    +lastIndexOf(const coordT &t, countT from) const
    +{
    +    if(from<0){
    +        from += count();
    +    }else if(from>=count()){
    +        from= count()-1;
    +    }
    +    if(from>=0){
    +        const_iterator i= begin()+from+1;
    +        while(i-- != constBegin()){
    +            if(*i==t){
    +                return (static_cast(i-begin())); // WARN64 coordinate index
    +            }
    +        }
    +    }
    +    return -1;
    +}//lastIndexOf
    +
    +void Coordinates::
    +removeAll(const coordT &t)
    +{
    +    MutableCoordinatesIterator i(*this);
    +    while(i.findNext(t)){
    +        i.remove();
    +    }
    +}//removeAll
    +
    +}//namespace orgQhull
    +
    +#//!\name Global functions
    +
    +using std::endl;
    +using std::istream;
    +using std::ostream;
    +using std::string;
    +using std::ws;
    +using orgQhull::Coordinates;
    +
    +ostream &
    +operator<<(ostream &os, const Coordinates &cs)
    +{
    +    Coordinates::const_iterator c= cs.begin();
    +    for(countT i=cs.count(); i--; ){
    +        os << *c++ << " ";
    +    }
    +    return os;
    +}//operator<<
    +
    diff --git a/xs/src/qhull/src/libqhullcpp/Coordinates.h b/xs/src/qhull/src/libqhullcpp/Coordinates.h
    new file mode 100644
    index 000000000..df8bd1138
    --- /dev/null
    +++ b/xs/src/qhull/src/libqhullcpp/Coordinates.h
    @@ -0,0 +1,303 @@
    +/****************************************************************************
    +**
    +** Copyright (c) 2009-2015 C.B. Barber. All rights reserved.
    +** $Id: //main/2015/qhull/src/libqhullcpp/Coordinates.h#6 $$Change: 2079 $
    +** $DateTime: 2016/02/07 17:43:34 $$Author: bbarber $
    +**
    +****************************************************************************/
    +
    +#ifndef QHCOORDINATES_H
    +#define QHCOORDINATES_H
    +
    +#include "libqhull_r/qhull_ra.h"
    +#include "libqhullcpp/QhullError.h"
    +#include "libqhullcpp/QhullIterator.h"
    +
    +#include  // ptrdiff_t, size_t
    +#include 
    +// Requires STL vector class.  Can use with another vector class such as QList.
    +#include 
    +
    +namespace orgQhull {
    +
    +#//!\name Defined here
    +    //! An std::vector of point coordinates independent of dimension
    +    //! Used by PointCoordinates for RboxPoints and by Qhull for feasiblePoint
    +    //! A QhullPoint refers to previously allocated coordinates
    +    class Coordinates;
    +    class MutableCoordinatesIterator;
    +
    +class Coordinates {
    +
    +private:
    +#//!\name Fields
    +    std::vector coordinate_array;
    +
    +public:
    +#//!\name Subtypes
    +
    +    class const_iterator;
    +    class iterator;
    +    typedef iterator Iterator;
    +    typedef const_iterator ConstIterator;
    +
    +    typedef coordT              value_type;
    +    typedef const value_type   *const_pointer;
    +    typedef const value_type &  const_reference;
    +    typedef value_type *        pointer;
    +    typedef value_type &        reference;
    +    typedef ptrdiff_t           difference_type;
    +    typedef countT              size_type;
    +
    +#//!\name Construct
    +                        Coordinates() {};
    +    explicit            Coordinates(const std::vector &other) : coordinate_array(other) {}
    +                        Coordinates(const Coordinates &other) : coordinate_array(other.coordinate_array) {}
    +    Coordinates &       operator=(const Coordinates &other) { coordinate_array= other.coordinate_array; return *this; }
    +    Coordinates &       operator=(const std::vector &other) { coordinate_array= other; return *this; }
    +                        ~Coordinates() {}
    +
    +#//!\name Conversion
    +
    +#ifndef QHULL_NO_STL
    +    std::vector toStdVector() const { return coordinate_array; }
    +#endif //QHULL_NO_STL
    +#ifdef QHULL_USES_QT
    +    QList       toQList() const;
    +#endif //QHULL_USES_QT
    +
    +#//!\name GetSet
    +    countT              count() const { return static_cast(size()); }
    +    coordT *            data() { return isEmpty() ? 0 : &at(0); }
    +    const coordT *      data() const { return const_cast(isEmpty() ? 0 : &at(0)); }
    +    bool                isEmpty() const { return coordinate_array.empty(); }
    +    bool                operator==(const Coordinates &other) const  { return coordinate_array==other.coordinate_array; }
    +    bool                operator!=(const Coordinates &other) const  { return coordinate_array!=other.coordinate_array; }
    +    size_t              size() const { return coordinate_array.size(); }
    +
    +#//!\name Element access
    +    coordT &            at(countT idx) { return coordinate_array.at(idx); }
    +    const coordT &      at(countT idx) const { return coordinate_array.at(idx); }
    +    coordT &            back() { return coordinate_array.back(); }
    +    const coordT &      back() const { return coordinate_array.back(); }
    +    coordT &            first() { return front(); }
    +    const coordT &      first() const { return front(); }
    +    coordT &            front() { return coordinate_array.front(); }
    +    const coordT &      front() const { return coordinate_array.front(); }
    +    coordT &            last() { return back(); }
    +    const coordT &      last() const { return back(); }
    +    Coordinates         mid(countT idx, countT length= -1) const; //!<\todo countT -1 indicates
    +    coordT &            operator[](countT idx) { return coordinate_array.operator[](idx); }
    +    const coordT &      operator[](countT idx) const { return coordinate_array.operator[](idx); }
    +    coordT              value(countT idx, const coordT &defaultValue) const;
    +
    +#//!\name Iterator
    +    iterator            begin() { return iterator(coordinate_array.begin()); }
    +    const_iterator      begin() const { return const_iterator(coordinate_array.begin()); }
    +    const_iterator      constBegin() const { return begin(); }
    +    const_iterator      constEnd() const { return end(); }
    +    iterator            end() { return iterator(coordinate_array.end()); }
    +    const_iterator      end() const { return const_iterator(coordinate_array.end()); }
    +
    +#//!\name GetSet
    +    Coordinates         operator+(const Coordinates &other) const;
    +
    +#//!\name Modify
    +    void                append(int pointDimension, coordT *c);
    +    void                append(const coordT &c) { push_back(c); }
    +    void                clear() { coordinate_array.clear(); }
    +    iterator            erase(iterator idx) { return iterator(coordinate_array.erase(idx.base())); }
    +    iterator            erase(iterator beginIterator, iterator endIterator) { return iterator(coordinate_array.erase(beginIterator.base(), endIterator.base())); }
    +    void                insert(countT before, const coordT &c) { insert(begin()+before, c); }
    +    iterator            insert(iterator before, const coordT &c) { return iterator(coordinate_array.insert(before.base(), c)); }
    +    void                move(countT from, countT to) { insert(to, takeAt(from)); }
    +    Coordinates &       operator+=(const Coordinates &other);
    +    Coordinates &       operator+=(const coordT &c) { append(c); return *this; }
    +    Coordinates &       operator<<(const Coordinates &other) { return *this += other; }
    +    Coordinates &       operator<<(const coordT &c) { return *this += c; }
    +    void                pop_back() { coordinate_array.pop_back(); }
    +    void                pop_front() { removeFirst(); }
    +    void                prepend(const coordT &c) { insert(begin(), c); }
    +    void                push_back(const coordT &c) { coordinate_array.push_back(c); }
    +    void                push_front(const coordT &c) { insert(begin(), c); }
    +                        //removeAll below
    +    void                removeAt(countT idx) { erase(begin()+idx); }
    +    void                removeFirst() { erase(begin()); }
    +    void                removeLast() { erase(--end()); }
    +    void                replace(countT idx, const coordT &c) { (*this)[idx]= c; }
    +    void                reserve(countT i) { coordinate_array.reserve(i); }
    +    void                swap(countT idx, countT other);
    +    coordT              takeAt(countT idx);
    +    coordT              takeFirst() { return takeAt(0); }
    +    coordT              takeLast();
    +
    +#//!\name Search
    +    bool                contains(const coordT &t) const;
    +    countT              count(const coordT &t) const;
    +    countT              indexOf(const coordT &t, countT from = 0) const;
    +    countT              lastIndexOf(const coordT &t, countT from = -1) const;
    +    void                removeAll(const coordT &t);
    +
    +#//!\name Coordinates::iterator -- from QhullPoints, forwarding to coordinate_array
    +    // before const_iterator for conversion with comparison operators
    +    // Reviewed corelib/tools/qlist.h and corelib/tools/qvector.h w/o QT_STRICT_ITERATORS
    +    class iterator {
    +
    +    private:
    +        std::vector::iterator i;
    +        friend class const_iterator;
    +
    +    public:
    +        typedef std::random_access_iterator_tag  iterator_category;
    +        typedef coordT      value_type;
    +        typedef value_type *pointer;
    +        typedef value_type &reference;
    +        typedef ptrdiff_t   difference_type;
    +
    +                        iterator() {}
    +                        iterator(const iterator &other) { i= other.i; }
    +        explicit        iterator(const std::vector::iterator &vi) { i= vi; }
    +        iterator &      operator=(const iterator &other) { i= other.i; return *this; }
    +        std::vector::iterator &base() { return i; }
    +        coordT &        operator*() const { return *i; }
    +        // No operator->() when the base type is double
    +        coordT &        operator[](countT idx) const { return i[idx]; }
    +
    +        bool            operator==(const iterator &other) const { return i==other.i; }
    +        bool            operator!=(const iterator &other) const { return i!=other.i; }
    +        bool            operator<(const iterator &other) const { return i(const iterator &other) const { return i>other.i; }
    +        bool            operator>=(const iterator &other) const { return i>=other.i; }
    +              // reinterpret_cast to break circular dependency
    +        bool            operator==(const Coordinates::const_iterator &other) const { return *this==reinterpret_cast(other); }
    +        bool            operator!=(const Coordinates::const_iterator &other) const { return *this!=reinterpret_cast(other); }
    +        bool            operator<(const Coordinates::const_iterator &other) const { return *this(other); }
    +        bool            operator<=(const Coordinates::const_iterator &other) const { return *this<=reinterpret_cast(other); }
    +        bool            operator>(const Coordinates::const_iterator &other) const { return *this>reinterpret_cast(other); }
    +        bool            operator>=(const Coordinates::const_iterator &other) const { return *this>=reinterpret_cast(other); }
    +
    +        iterator &      operator++() { ++i; return *this; }
    +        iterator        operator++(int) { return iterator(i++); }
    +        iterator &      operator--() { --i; return *this; }
    +        iterator        operator--(int) { return iterator(i--); }
    +        iterator &      operator+=(countT idx) { i += idx; return *this; }
    +        iterator &      operator-=(countT idx) { i -= idx; return *this; }
    +        iterator        operator+(countT idx) const { return iterator(i+idx); }
    +        iterator        operator-(countT idx) const { return iterator(i-idx); }
    +        difference_type operator-(iterator other) const { return i-other.i; }
    +    };//Coordinates::iterator
    +
    +#//!\name Coordinates::const_iterator
    +    class const_iterator {
    +
    +    private:
    +        std::vector::const_iterator i;
    +
    +    public:
    +        typedef std::random_access_iterator_tag  iterator_category;
    +        typedef coordT            value_type;
    +        typedef const value_type *pointer;
    +        typedef const value_type &reference;
    +        typedef ptrdiff_t         difference_type;
    +
    +                        const_iterator() {}
    +                        const_iterator(const const_iterator &other) { i= other.i; }
    +                        const_iterator(const iterator &o) : i(o.i) {}
    +        explicit        const_iterator(const std::vector::const_iterator &vi) { i= vi; }
    +        const_iterator &operator=(const const_iterator &other) { i= other.i; return *this; }
    +        const coordT &  operator*() const { return *i; }
    +        // No operator->() when the base type is double
    +        const coordT &  operator[](countT idx) const { return i[idx]; }
    +
    +        bool            operator==(const const_iterator &other) const { return i==other.i; }
    +        bool            operator!=(const const_iterator &other) const { return i!=other.i; }
    +        bool            operator<(const const_iterator &other) const { return i(const const_iterator &other) const { return i>other.i; }
    +        bool            operator>=(const const_iterator &other) const { return i>=other.i; }
    +
    +        const_iterator & operator++() { ++i; return *this; } 
    +        const_iterator  operator++(int) { return const_iterator(i++); }
    +        const_iterator & operator--() { --i; return *this; }
    +        const_iterator  operator--(int) { return const_iterator(i--); }
    +        const_iterator & operator+=(countT idx) { i += idx; return *this; }
    +        const_iterator & operator-=(countT idx) { i -= idx; return *this; }
    +        const_iterator  operator+(countT idx) const { return const_iterator(i+idx); }
    +        const_iterator  operator-(countT idx) const { return const_iterator(i-idx); }
    +        difference_type operator-(const_iterator other) const { return i-other.i; }
    +    };//Coordinates::const_iterator
    +
    +};//Coordinates
    +
    +//class CoordinatesIterator
    +//QHULL_DECLARE_SEQUENTIAL_ITERATOR(Coordinates, coordT)
    +
    +class CoordinatesIterator
    +{
    +    typedef Coordinates::const_iterator const_iterator;
    +
    +private:
    +    const Coordinates * c;
    +    const_iterator      i;
    +
    +public:
    +                        CoordinatesIterator(const Coordinates &container): c(&container), i(c->constBegin()) {}
    +    CoordinatesIterator &operator=(const Coordinates &container) { c= &container; i= c->constBegin(); return *this; }
    +                        ~CoordinatesIterator() {}
    +
    +    bool                findNext(const coordT &t) { while (i != c->constEnd()) if(*i++ == t){ return true;} return false; }
    +    bool                findPrevious(const coordT &t) { while (i != c->constBegin())if (*(--i) == t){ return true;} return false;  }
    +    bool                hasNext() const { return i != c->constEnd(); }
    +    bool                hasPrevious() const { return i != c->constBegin(); }
    +    const coordT &      next() { return *i++; }
    +    const coordT &      previous() { return *--i; }
    +    const coordT &      peekNext() const { return *i; }
    +    const coordT &      peekPrevious() const { const_iterator p= i; return *--p; }
    +    void                toFront() { i= c->constBegin(); }
    +    void                toBack() { i= c->constEnd(); }
    +};//CoordinatesIterator
    +
    +//class MutableCoordinatesIterator
    +//QHULL_DECLARE_MUTABLE_SEQUENTIAL_ITERATOR(Coordinates, coordT)
    +class MutableCoordinatesIterator
    +{
    +    typedef Coordinates::iterator iterator;
    +    typedef Coordinates::const_iterator const_iterator;
    +
    +private:
    +    Coordinates *       c;
    +    iterator            i;
    +    iterator            n;
    +    bool                item_exists() const { return const_iterator(n) != c->constEnd(); }
    +
    +public:
    +                        MutableCoordinatesIterator(Coordinates &container) : c(&container) { i= c->begin(); n= c->end(); }
    +    MutableCoordinatesIterator &operator=(Coordinates &container) { c= &container; i= c->begin(); n= c->end(); return *this; }
    +                        ~MutableCoordinatesIterator() {}
    +
    +    bool                findNext(const coordT &t) { while(c->constEnd()!=const_iterator(n= i)){ if(*i++==t){ return true;}} return false; }
    +    bool                findPrevious(const coordT &t) { while(c->constBegin()!=const_iterator(i)){ if(*(n= --i)== t){ return true;}} n= c->end(); return false;  }
    +    bool                hasNext() const { return (c->constEnd()!=const_iterator(i)); }
    +    bool                hasPrevious() const { return (c->constBegin()!=const_iterator(i)); }
    +    void                insert(const coordT &t) { n= i= c->insert(i, t); ++i; }
    +    coordT &            next() { n= i++; return *n; }
    +    coordT &            peekNext() const { return *i; }
    +    coordT &            peekPrevious() const { iterator p= i; return *--p; }
    +    coordT &            previous() { n= --i; return *n; }
    +    void                remove() { if(c->constEnd()!=const_iterator(n)){ i= c->erase(n); n= c->end();} }
    +    void                setValue(const coordT &t) const { if(c->constEnd()!=const_iterator(n)){ *n= t;} }
    +    void                toFront() { i= c->begin(); n= c->end(); }
    +    void                toBack() { i= c->end(); n= i; }
    +    coordT &            value() { QHULL_ASSERT(item_exists()); return *n; }
    +    const coordT &      value() const { QHULL_ASSERT(item_exists()); return *n; }
    +};//MutableCoordinatesIterator
    +
    +
    +}//namespace orgQhull
    +
    +#//!\name Global
    +
    +std::ostream &operator<<(std::ostream &os, const orgQhull::Coordinates &c);
    +
    +#endif // QHCOORDINATES_H
    diff --git a/xs/src/qhull/src/libqhullcpp/PointCoordinates.cpp b/xs/src/qhull/src/libqhullcpp/PointCoordinates.cpp
    new file mode 100644
    index 000000000..a5b71e901
    --- /dev/null
    +++ b/xs/src/qhull/src/libqhullcpp/PointCoordinates.cpp
    @@ -0,0 +1,348 @@
    +/****************************************************************************
    +**
    +** Copyright (c) 2009-2015 C.B. Barber. All rights reserved.
    +** $Id: //main/2015/qhull/src/libqhullcpp/PointCoordinates.cpp#3 $$Change: 2066 $
    +** $DateTime: 2016/01/18 19:29:17 $$Author: bbarber $
    +**
    +****************************************************************************/
    +
    +#include "libqhullcpp/PointCoordinates.h"
    +
    +#include "libqhullcpp/QhullError.h"
    +#include "libqhullcpp/QhullPoint.h"
    +
    +#include 
    +#include 
    +
    +using std::istream;
    +using std::string;
    +using std::ws;
    +
    +#ifdef _MSC_VER  // Microsoft Visual C++ -- warning level 4
    +#pragma warning( disable : 4996)  // function was declared deprecated(strcpy, localtime, etc.)
    +#endif
    +
    +namespace orgQhull {
    +
    +#//! PointCoordinates -- vector of PointCoordinates
    +
    +#//!\name Constructors
    +
    +PointCoordinates::
    +PointCoordinates()
    +: QhullPoints()
    +, point_coordinates()
    +, describe_points()
    +{
    +}
    +
    +PointCoordinates::
    +PointCoordinates(const std::string &aComment)
    +: QhullPoints()
    +, point_coordinates()
    +, describe_points(aComment)
    +{
    +}
    +
    +PointCoordinates::
    +PointCoordinates(int pointDimension, const std::string &aComment)
    +: QhullPoints()
    +, point_coordinates()
    +, describe_points(aComment)
    +{
    +    setDimension(pointDimension);
    +}
    +
    +//! Qhull and QhullQh constructors are the same
    +PointCoordinates::
    +PointCoordinates(const Qhull &q)
    +: QhullPoints(q)
    +, point_coordinates()
    +, describe_points()
    +{
    +}
    +
    +PointCoordinates::
    +PointCoordinates(const Qhull &q, const std::string &aComment)
    +: QhullPoints(q)
    +, point_coordinates()
    +, describe_points(aComment)
    +{
    +}
    +
    +PointCoordinates::
    +PointCoordinates(const Qhull &q, int pointDimension, const std::string &aComment)
    +: QhullPoints(q)
    +, point_coordinates()
    +, describe_points(aComment)
    +{
    +    setDimension(pointDimension);
    +}
    +
    +PointCoordinates::
    +PointCoordinates(const Qhull &q, int pointDimension, const std::string &aComment, countT coordinatesCount, const coordT *c)
    +: QhullPoints(q)
    +, point_coordinates()
    +, describe_points(aComment)
    +{
    +    setDimension(pointDimension);
    +    append(coordinatesCount, c);
    +}
    +
    +PointCoordinates::
    +PointCoordinates(QhullQh *qqh)
    +: QhullPoints(qqh)
    +, point_coordinates()
    +, describe_points()
    +{
    +}
    +
    +PointCoordinates::
    +PointCoordinates(QhullQh *qqh, const std::string &aComment)
    +: QhullPoints(qqh)
    +, point_coordinates()
    +, describe_points(aComment)
    +{
    +}
    +
    +PointCoordinates::
    +PointCoordinates(QhullQh *qqh, int pointDimension, const std::string &aComment)
    +: QhullPoints(qqh)
    +, point_coordinates()
    +, describe_points(aComment)
    +{
    +    setDimension(pointDimension);
    +}
    +
    +PointCoordinates::
    +PointCoordinates(QhullQh *qqh, int pointDimension, const std::string &aComment, countT coordinatesCount, const coordT *c)
    +: QhullPoints(qqh)
    +, point_coordinates()
    +, describe_points(aComment)
    +{
    +    setDimension(pointDimension);
    +    append(coordinatesCount, c);
    +}
    +
    +PointCoordinates::
    +PointCoordinates(const PointCoordinates &other)
    +: QhullPoints(other)
    +, point_coordinates(other.point_coordinates)
    +, describe_points(other.describe_points)
    +{
    +    makeValid();  // Update point_first and point_end
    +}
    +
    +PointCoordinates & PointCoordinates::
    +operator=(const PointCoordinates &other)
    +{
    +    QhullPoints::operator=(other);
    +    point_coordinates= other.point_coordinates;
    +    describe_points= other.describe_points;
    +    makeValid(); // Update point_first and point_end
    +    return *this;
    +}//operator=
    +
    +PointCoordinates::
    +~PointCoordinates()
    +{ }
    +
    +#//!\name GetSet
    +
    +void PointCoordinates::
    +checkValid() const
    +{
    +    if(getCoordinates().data()!=data()
    +    || getCoordinates().count()!=coordinateCount()){
    +        throw QhullError(10060, "Qhull error: first point (%x) is not PointCoordinates.data() or count (%d) is not PointCoordinates.count (%d)", coordinateCount(), getCoordinates().count(), 0.0, data());
    +    }
    +}//checkValid
    +
    +void PointCoordinates::
    +setDimension(int i)
    +{
    +    if(i<0){
    +        throw QhullError(10062, "Qhull error: can not set PointCoordinates dimension to %d", i);
    +    }
    +    int currentDimension=QhullPoints::dimension();
    +    if(currentDimension!=0 && i!=currentDimension){
    +        throw QhullError(10063, "Qhull error: can not change PointCoordinates dimension (from %d to %d)", currentDimension, i);
    +    }
    +    QhullPoints::setDimension(i);
    +}//setDimension
    +
    +#//!\name Foreach
    +
    +Coordinates::ConstIterator PointCoordinates::
    +beginCoordinates(countT pointIndex) const
    +{
    +    return point_coordinates.begin()+indexOffset(pointIndex);
    +}
    +
    +Coordinates::Iterator PointCoordinates::
    +beginCoordinates(countT pointIndex)
    +{
    +    return point_coordinates.begin()+indexOffset(pointIndex);
    +}
    +
    +#//!\name Methods
    +
    +void PointCoordinates::
    +append(countT coordinatesCount, const coordT *c)
    +{
    +    if(coordinatesCount<=0){
    +        return;
    +    }
    +    if(includesCoordinates(c)){
    +        throw QhullError(10065, "Qhull error: can not append a subset of PointCoordinates to itself.  The coordinates for point %d may move.", indexOf(c, QhullError::NOthrow));
    +    }
    +    reserveCoordinates(coordinatesCount);
    +    std::copy(c, c+coordinatesCount, std::back_inserter(point_coordinates));
    +    makeValid();
    +}//append coordT
    +
    +void PointCoordinates::
    +append(const PointCoordinates &other)
    +{
    +    setDimension(other.dimension());
    +    append(other.coordinateCount(), other.data());
    +}//append PointCoordinates
    +
    +void PointCoordinates::
    +append(const QhullPoint &p)
    +{
    +    setDimension(p.dimension());
    +    append(p.dimension(), p.coordinates());
    +}//append QhullPoint
    +
    +void PointCoordinates::
    +appendComment(const std::string &s){
    +    if(char c= s[0] && describe_points.empty()){
    +        if(c=='-' || isdigit(c)){
    +            throw QhullError(10028, "Qhull argument error: comments can not start with a number or minus, %s", 0, 0, 0.0, s.c_str());
    +        }
    +    }
    +    describe_points += s;
    +}//appendComment
    +
    +//! Read PointCoordinates from istream.  First two numbers are dimension and count.  A non-digit starts a rboxCommand.
    +//! Overwrites describe_points.  See qh_readpoints [io.c]
    +void PointCoordinates::
    +appendPoints(istream &in)
    +{
    +    int inDimension;
    +    countT inCount;
    +    in >> ws >> inDimension >> ws;
    +    if(!in.good()){
    +        in.clear();
    +        string remainder;
    +        getline(in, remainder);
    +        throw QhullError(10005, "Qhull error: input did not start with dimension or count -- %s", 0, 0, 0, remainder.c_str());
    +    }
    +    char c= (char)in.peek();
    +    if(c!='-' && !isdigit(c)){         // Comments start with a non-digit
    +        getline(in, describe_points);
    +        in >> ws;
    +    }
    +    in >> inCount >> ws;
    +    if(!in.good()){
    +        in.clear();
    +        string remainder;
    +        getline(in, remainder);
    +        throw QhullError(10009, "Qhull error: input did not start with dimension and count -- %d %s", inDimension, 0, 0, remainder.c_str());
    +    }
    +    c= (char)in.peek();
    +    if(c!='-' && !isdigit(c)){         // Comments start with a non-digit
    +        getline(in, describe_points);
    +        in >> ws;
    +    }
    +    if(inCount> p >> ws;
    +        if(in.fail()){
    +            in.clear();
    +            string remainder;
    +            getline(in, remainder);
    +            throw QhullError(10008, "Qhull error: failed to read coordinate %d  of point %d\n   %s", coordinatesCount % inDimension, coordinatesCount/inDimension, 0, remainder.c_str());
    +        }else{
    +            point_coordinates.push_back(p);
    +            coordinatesCount++;
    +        }
    +    }
    +    if(coordinatesCount != inCount*inDimension){
    +        if(coordinatesCount%inDimension==0){
    +            throw QhullError(10006, "Qhull error: expected %d %d-d PointCoordinates but read %i PointCoordinates", int(inCount), inDimension, 0.0, int(coordinatesCount/inDimension));
    +        }else{
    +            throw QhullError(10012, "Qhull error: expected %d %d-d PointCoordinates but read %i PointCoordinates plus %f extra coordinates", inCount, inDimension, float(coordinatesCount%inDimension), coordinatesCount/inDimension);
    +        }
    +    }
    +    makeValid();
    +}//appendPoints istream
    +
    +PointCoordinates PointCoordinates::
    +operator+(const PointCoordinates &other) const
    +{
    +    PointCoordinates pc= *this;
    +    pc << other;
    +    return pc;
    +}//operator+
    +
    +void PointCoordinates::
    +reserveCoordinates(countT newCoordinates)
    +{
    +    // vector::reserve is not const
    +    point_coordinates.reserve((countT)point_coordinates.size()+newCoordinates); // WARN64
    +    makeValid();
    +}//reserveCoordinates
    +
    +#//!\name Helpers
    +
    +countT PointCoordinates::
    +indexOffset(countT i) const {
    +    countT n= i*dimension();
    +    countT coordinatesCount= point_coordinates.count();
    +    if(i<0 || n>coordinatesCount){
    +        throw QhullError(10061, "Qhull error: point_coordinates is too short (%d) for point %d", coordinatesCount, i);
    +    }
    +    return n;
    +}
    +
    +}//namespace orgQhull
    +
    +#//!\name Global functions
    +
    +using std::endl;
    +using std::ostream;
    +
    +using orgQhull::Coordinates;
    +using orgQhull::PointCoordinates;
    +
    +ostream&
    +operator<<(ostream &os, const PointCoordinates &p)
    +{
    +    p.checkValid();
    +    countT count= p.count();
    +    int dimension= p.dimension();
    +    string comment= p.comment();
    +    if(comment.empty()){
    +        os << dimension << endl;
    +    }else{
    +        os << dimension << " " << comment << endl;
    +    }
    +    os << count << endl;
    +    Coordinates::ConstIterator c= p.beginCoordinates();
    +    for(countT i=0; i
    +#include 
    +
    +#ifndef QHULL_NO_STL
    +#include 
    +#endif
    +
    +namespace orgQhull {
    +
    +#//!\name Defined here
    +    //! QhullPoints with Coordinates and description
    +    //! Inherited by RboxPoints
    +    class PointCoordinates;
    +
    +class PointCoordinates : public QhullPoints {
    +
    +private:
    +#//!\name Fields
    +    Coordinates         point_coordinates;      //! std::vector of point coordinates
    +                                                //! may have extraCoordinates()
    +    std::string         describe_points;          //! Comment describing PointCoordinates
    +
    +public:
    +#//!\name Construct
    +    //! QhullPoint, PointCoordinates, and QhullPoints have similar constructors
    +    //! If Qhull/QhullQh is not initialized, then dimension()==0                        PointCoordinates();
    +                        PointCoordinates();
    +    explicit            PointCoordinates(const std::string &aComment);
    +                        PointCoordinates(int pointDimension, const std::string &aComment);
    +                        //! Qhull/QhullQh used for dimension() and QhullPoint equality
    +    explicit            PointCoordinates(const Qhull &q);
    +                        PointCoordinates(const Qhull &q, const std::string &aComment);
    +                        PointCoordinates(const Qhull &q, int pointDimension, const std::string &aComment);
    +                        PointCoordinates(const Qhull &q, int pointDimension, const std::string &aComment, countT coordinatesCount, const coordT *c); // may be invalid
    +                        //! Use append() and appendPoints() for Coordinates and vector
    +    explicit            PointCoordinates(QhullQh *qqh);
    +                        PointCoordinates(QhullQh *qqh, const std::string &aComment);
    +                        PointCoordinates(QhullQh *qqh, int pointDimension, const std::string &aComment);
    +                        PointCoordinates(QhullQh *qqh, int pointDimension, const std::string &aComment, countT coordinatesCount, const coordT *c); // may be invalid
    +                        //! Use append() and appendPoints() for Coordinates and vector
    +                        PointCoordinates(const PointCoordinates &other);
    +    PointCoordinates &  operator=(const PointCoordinates &other);
    +                        ~PointCoordinates();
    +
    +#//!\name Convert
    +    //! QhullPoints coordinates, constData, data, count, size
    +#ifndef QHULL_NO_STL
    +    void                append(const std::vector &otherCoordinates) { if(!otherCoordinates.empty()){ append((int)otherCoordinates.size(), &otherCoordinates[0]); } }
    +    std::vector toStdVector() const { return point_coordinates.toStdVector(); }
    +#endif //QHULL_NO_STL
    +#ifdef QHULL_USES_QT
    +    void                append(const QList &pointCoordinates) { if(!pointCoordinates.isEmpty()){ append(pointCoordinates.count(), &pointCoordinates[0]); } }
    +    QList       toQList() const { return point_coordinates.toQList(); }
    +#endif //QHULL_USES_QT
    +
    +#//!\name GetSet
    +    //! See QhullPoints for coordinates, coordinateCount, dimension, empty, isEmpty, ==, !=
    +    void                checkValid() const;
    +    std::string         comment() const { return describe_points; }
    +    void                makeValid() { defineAs(point_coordinates.count(), point_coordinates.data()); }
    +    const Coordinates & getCoordinates() const { return point_coordinates; }
    +    void                setComment(const std::string &s) { describe_points= s; }
    +    void                setDimension(int i);
    +
    +private:
    +    //! disable QhullPoints.defineAs()
    +    void                defineAs(countT coordinatesCount, coordT *c) { QhullPoints::defineAs(coordinatesCount, c); }
    +public:
    +
    +#//!\name ElementAccess
    +    //! See QhullPoints for at, back, first, front, last, mid, [], value
    +
    +#//!\name Foreach
    +    //! See QhullPoints for begin, constBegin, end
    +    Coordinates::ConstIterator  beginCoordinates() const { return point_coordinates.begin(); }
    +    Coordinates::Iterator       beginCoordinates() { return point_coordinates.begin(); }
    +    Coordinates::ConstIterator  beginCoordinates(countT pointIndex) const;
    +    Coordinates::Iterator       beginCoordinates(countT pointIndex);
    +    Coordinates::ConstIterator  endCoordinates() const { return point_coordinates.end(); }
    +    Coordinates::Iterator       endCoordinates() { return point_coordinates.end(); }
    +
    +#//!\name Search
    +    //! See QhullPoints for contains, count, indexOf, lastIndexOf
    +
    +#//!\name GetSet
    +    PointCoordinates    operator+(const PointCoordinates &other) const;
    +
    +#//!\name Modify
    +    //FIXUP QH11001: Add clear() and other modify operators from Coordinates.h.  Include QhullPoint::operator=()
    +    void                append(countT coordinatesCount, const coordT *c);  //! Dimension previously defined
    +    void                append(const coordT &c) { append(1, &c); } //! Dimension previously defined
    +    void                append(const QhullPoint &p);
    +    //! See convert for std::vector and QList
    +    void                append(const Coordinates &c) { append(c.count(), c.data()); }
    +    void                append(const PointCoordinates &other);
    +    void                appendComment(const std::string &s);
    +    void                appendPoints(std::istream &in);
    +    PointCoordinates &  operator+=(const PointCoordinates &other) { append(other); return *this; }
    +    PointCoordinates &  operator+=(const coordT &c) { append(c); return *this; }
    +    PointCoordinates &  operator+=(const QhullPoint &p) { append(p); return *this; }
    +    PointCoordinates &  operator<<(const PointCoordinates &other) { return *this += other; }
    +    PointCoordinates &  operator<<(const coordT &c) { return *this += c; }
    +    PointCoordinates &  operator<<(const QhullPoint &p) { return *this += p; }
    +    // reserve() is non-const
    +    void                reserveCoordinates(countT newCoordinates);
    +
    +#//!\name Helpers
    +private:
    +    int                 indexOffset(int i) const;
    +
    +};//PointCoordinates
    +
    +// No references to QhullPoint.  Prevents use of QHULL_DECLARE_SEQUENTIAL_ITERATOR(PointCoordinates, QhullPoint)
    +class PointCoordinatesIterator
    +{
    +    typedef PointCoordinates::const_iterator const_iterator;
    +
    +private:
    +    const PointCoordinates *c;
    +    const_iterator      i;
    +
    +public:
    +                        PointCoordinatesIterator(const PointCoordinates &container) : c(&container), i(c->constBegin()) {}
    +                        PointCoordinatesIterator &operator=(const PointCoordinates &container) { c = &container; i = c->constBegin(); return *this; }
    +
    +    void                toFront() { i = c->constBegin(); }
    +    void                toBack() { i = c->constEnd(); }
    +    bool                hasNext() const { return i != c->constEnd(); }
    +    const QhullPoint    next() { return *i++; }
    +    const QhullPoint    peekNext() const { return *i; }
    +    bool                hasPrevious() const { return i != c->constBegin(); }
    +    const QhullPoint    previous() { return *--i; }
    +    const QhullPoint    peekPrevious() const { const_iterator p = i; return *--p; }
    +    bool                findNext(const QhullPoint &t) { while(i != c->constEnd()){ if (*i++ == t) return true;} return false; }
    +    bool                findPrevious(const QhullPoint &t) { while(i != c->constBegin()){ if (*(--i) == t) return true;} return false;  }
    +};//CoordinatesIterator
    +
    +// FIXUP QH11002:  Add MutablePointCoordinatesIterator after adding modify operators
    +\
    +}//namespace orgQhull
    +
    +#//!\name Global
    +
    +std::ostream &          operator<<(std::ostream &os, const orgQhull::PointCoordinates &p);
    +
    +#endif // QHPOINTCOORDINATES_H
    diff --git a/xs/src/qhull/src/libqhullcpp/Qhull.cpp b/xs/src/qhull/src/libqhullcpp/Qhull.cpp
    new file mode 100644
    index 000000000..7124a15cd
    --- /dev/null
    +++ b/xs/src/qhull/src/libqhullcpp/Qhull.cpp
    @@ -0,0 +1,352 @@
    +/****************************************************************************
    +**
    +** Copyright (c) 2008-2015 C.B. Barber. All rights reserved.
    +** $Id: //main/2015/qhull/src/libqhullcpp/Qhull.cpp#4 $$Change: 2078 $
    +** $DateTime: 2016/02/07 16:53:56 $$Author: bbarber $
    +**
    +****************************************************************************/
    +
    +#//! Qhull -- invoke qhull from C++
    +#//! Compile libqhull_r and Qhull together due to use of setjmp/longjmp()
    +
    +#include "libqhullcpp/Qhull.h"
    +
    +#include "libqhullcpp/QhullError.h"
    +#include "libqhullcpp/RboxPoints.h"
    +#include "libqhullcpp/QhullQh.h"
    +#include "libqhullcpp/QhullFacet.h"
    +#include "libqhullcpp/QhullFacetList.h"
    +
    +#include 
    +
    +using std::cerr;
    +using std::string;
    +using std::vector;
    +using std::ostream;
    +
    +#ifdef _MSC_VER  // Microsoft Visual C++ -- warning level 4
    +#pragma warning( disable : 4611)  // interaction between '_setjmp' and C++ object destruction is non-portable
    +#pragma warning( disable : 4996)  // function was declared deprecated(strcpy, localtime, etc.)
    +#endif
    +
    +namespace orgQhull {
    +
    +#//!\name Global variables
    +
    +const char s_unsupported_options[]=" Fd TI ";
    +const char s_not_output_options[]= " Fd TI A C d E H P Qb QbB Qbb Qc Qf Qg Qi Qm QJ Qr QR Qs Qt Qv Qx Qz Q0 Q1 Q2 Q3 Q4 Q5 Q6 Q7 Q8 Q9 Q10 Q11 R Tc TC TM TP TR Tv TV TW U v V W ";
    +
    +#//!\name Constructor, destructor, etc.
    +Qhull::
    +Qhull()
    +: qh_qh(0)
    +, origin_point()
    +, run_called(false)
    +, feasible_point()
    +{
    +    allocateQhullQh();
    +}//Qhull
    +
    +//! Invokes Qhull on rboxPoints
    +//! Same as runQhull()
    +//! For rbox commands, see http://www.qhull.org/html/rbox.htm or html/rbox.htm
    +//! For qhull commands, see http://www.qhull.org/html/qhull.htm or html/qhull.htm
    +Qhull::
    +Qhull(const RboxPoints &rboxPoints, const char *qhullCommand2)
    +: qh_qh(0)
    +, origin_point()
    +, run_called(false)
    +, feasible_point()
    +{
    +    allocateQhullQh();
    +    runQhull(rboxPoints, qhullCommand2);
    +}//Qhull rbox
    +
    +//! Invokes Qhull on a set of input points
    +//! Same as runQhull()
    +//! For qhull commands, see http://www.qhull.org/html/qhull.htm or html/qhull.htm
    +Qhull::
    +Qhull(const char *inputComment2, int pointDimension, int pointCount, const realT *pointCoordinates, const char *qhullCommand2)
    +: qh_qh(0)
    +, origin_point()
    +, run_called(false)
    +, feasible_point()
    +{
    +    allocateQhullQh();
    +    runQhull(inputComment2, pointDimension, pointCount, pointCoordinates, qhullCommand2);
    +}//Qhull points
    +
    +void Qhull::
    +allocateQhullQh()
    +{
    +  QHULL_LIB_CHECK /* Check for compatible library */
    +
    +    qh_qh= new QhullQh;
    +    void *p= qh_qh;
    +    void *p2= static_cast(qh_qh);
    +    char *s= static_cast(p);
    +    char *s2= static_cast(p2);
    +    if(s!=s2){
    +        throw QhullError(10074, "Qhull error: QhullQh at a different address than base type QhT (%d bytes).  Please report compiler to qhull.org", int(s2-s));
    +    }
    +}//allocateQhullQh
    +
    +Qhull::
    +~Qhull() throw()
    +{
    +    // Except for cerr, does not throw errors
    +    if(qh_qh->hasQhullMessage()){
    +        cerr<< "\nQhull output at end\n"; //FIXUP QH11005: where should error and log messages go on ~Qhull?
    +        cerr<< qh_qh->qhullMessage();
    +        qh_qh->clearQhullMessage();
    +    }
    +    delete qh_qh;
    +    qh_qh= 0;
    +}//~Qhull
    +
    +#//!\name GetSet
    +
    +void Qhull::
    +checkIfQhullInitialized()
    +{
    +    if(!initialized()){ // qh_initqhull_buffers() not called
    +        throw QhullError(10023, "Qhull error: checkIfQhullInitialized failed.  Call runQhull() first.");
    +    }
    +}//checkIfQhullInitialized
    +
    +//! Return feasiblePoint for halfspace intersection
    +//! If called before runQhull(), then it returns the value from setFeasiblePoint.  qh.feasible_string overrides this value if it is defined.
    +Coordinates Qhull::
    +feasiblePoint() const
    +{
    +    Coordinates result;
    +    if(qh_qh->feasible_point){
    +        result.append(qh_qh->hull_dim, qh_qh->feasible_point);
    +    }else{
    +        result= feasible_point;
    +    }
    +    return result;
    +}//feasiblePoint
    +
    +//! Return origin point for qh.input_dim
    +QhullPoint Qhull::
    +inputOrigin()
    +{
    +    QhullPoint result= origin();
    +    result.setDimension(qh_qh->input_dim);
    +    return result;
    +}//inputOrigin
    +
    +#//!\name GetValue
    +
    +double Qhull::
    +area(){
    +    checkIfQhullInitialized();
    +    if(!qh_qh->hasAreaVolume){
    +        QH_TRY_(qh_qh){ // no object creation -- destructors skipped on longjmp()
    +            qh_getarea(qh_qh, qh_qh->facet_list);
    +        }
    +        qh_qh->NOerrexit= true;
    +        qh_qh->maybeThrowQhullMessage(QH_TRY_status);
    +    }
    +    return qh_qh->totarea;
    +}//area
    +
    +double Qhull::
    +volume(){
    +    checkIfQhullInitialized();
    +    if(!qh_qh->hasAreaVolume){
    +        QH_TRY_(qh_qh){ // no object creation -- destructors skipped on longjmp()
    +            qh_getarea(qh_qh, qh_qh->facet_list);
    +        }
    +        qh_qh->NOerrexit= true;
    +        qh_qh->maybeThrowQhullMessage(QH_TRY_status);
    +    }
    +    return qh_qh->totvol;
    +}//volume
    +
    +#//!\name Foreach
    +
    +//! Define QhullVertex::neighborFacets().
    +//! Automatically called if merging facets or computing the Voronoi diagram.
    +//! Noop if called multiple times.
    +void Qhull::
    +defineVertexNeighborFacets(){
    +    checkIfQhullInitialized();
    +    if(!qh_qh->hasAreaVolume){
    +        QH_TRY_(qh_qh){ // no object creation -- destructors skipped on longjmp()
    +            qh_vertexneighbors(qh_qh);
    +        }
    +        qh_qh->NOerrexit= true;
    +        qh_qh->maybeThrowQhullMessage(QH_TRY_status);
    +    }
    +}//defineVertexNeighborFacets
    +
    +QhullFacetList Qhull::
    +facetList() const{
    +    return QhullFacetList(beginFacet(), endFacet());
    +}//facetList
    +
    +QhullPoints Qhull::
    +points() const
    +{
    +    return QhullPoints(qh_qh, qh_qh->hull_dim, qh_qh->num_points*qh_qh->hull_dim, qh_qh->first_point);
    +}//points
    +
    +QhullPointSet Qhull::
    +otherPoints() const
    +{
    +    return QhullPointSet(qh_qh, qh_qh->other_points);
    +}//otherPoints
    +
    +//! Return vertices of the convex hull.
    +QhullVertexList Qhull::
    +vertexList() const{
    +    return QhullVertexList(beginVertex(), endVertex());
    +}//vertexList
    +
    +#//!\name Methods
    +
    +void Qhull::
    +outputQhull()
    +{
    +    checkIfQhullInitialized();
    +    QH_TRY_(qh_qh){ // no object creation -- destructors skipped on longjmp()
    +        qh_produce_output2(qh_qh);
    +    }
    +    qh_qh->NOerrexit= true;
    +    qh_qh->maybeThrowQhullMessage(QH_TRY_status);
    +}//outputQhull
    +
    +void Qhull::
    +outputQhull(const char *outputflags)
    +{
    +    checkIfQhullInitialized();
    +    string cmd(" "); // qh_checkflags skips first word
    +    cmd += outputflags;
    +    char *command= const_cast(cmd.c_str());
    +    QH_TRY_(qh_qh){ // no object creation -- destructors skipped on longjmp()
    +        qh_clear_outputflags(qh_qh);
    +        char *s = qh_qh->qhull_command + strlen(qh_qh->qhull_command) + 1; //space
    +        strncat(qh_qh->qhull_command, command, sizeof(qh_qh->qhull_command)-strlen(qh_qh->qhull_command)-1);
    +        qh_checkflags(qh_qh, command, const_cast(s_not_output_options));
    +        qh_initflags(qh_qh, s);
    +        qh_initqhull_outputflags(qh_qh);
    +        if(qh_qh->KEEPminArea < REALmax/2
    +           || (0 != qh_qh->KEEParea + qh_qh->KEEPmerge + qh_qh->GOODvertex
    +                    + qh_qh->GOODthreshold + qh_qh->GOODpoint + qh_qh->SPLITthresholds)){
    +            facetT *facet;
    +            qh_qh->ONLYgood= False;
    +            FORALLfacet_(qh_qh->facet_list) {
    +                facet->good= True;
    +            }
    +            qh_prepare_output(qh_qh);
    +        }
    +        qh_produce_output2(qh_qh);
    +        if(qh_qh->VERIFYoutput && !qh_qh->STOPpoint && !qh_qh->STOPcone){
    +            qh_check_points(qh_qh);
    +        }
    +    }
    +    qh_qh->NOerrexit= true;
    +    qh_qh->maybeThrowQhullMessage(QH_TRY_status);
    +}//outputQhull
    +
    +//! For qhull commands, see http://www.qhull.org/html/qhull.htm or html/qhull.htm
    +void Qhull::
    +runQhull(const RboxPoints &rboxPoints, const char *qhullCommand2)
    +{
    +    runQhull(rboxPoints.comment().c_str(), rboxPoints.dimension(), rboxPoints.count(), &*rboxPoints.coordinates(), qhullCommand2);
    +}//runQhull, RboxPoints
    +
    +//! pointCoordinates is a array of points, input sites ('d' or 'v'), or halfspaces with offset last ('H')
    +//! Derived from qh_new_qhull [user.c]
    +//! For rbox commands, see http://www.qhull.org/html/rbox.htm or html/rbox.htm
    +//! For qhull commands, see http://www.qhull.org/html/qhull.htm or html/qhull.htm
    +void Qhull::
    +runQhull(const char *inputComment, int pointDimension, int pointCount, const realT *pointCoordinates, const char *qhullCommand)
    +{
    +  /* gcc may issue a "might be clobbered" warning for pointDimension and pointCoordinates [-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 */
    +    if(run_called){
    +        throw QhullError(10027, "Qhull error: runQhull called twice.  Only one call allowed.");
    +    }
    +    run_called= true;
    +    string s("qhull ");
    +    s += qhullCommand;
    +    char *command= const_cast(s.c_str());
    +    /************* Expansion of QH_TRY_ for debugging
    +    int QH_TRY_status;
    +    if(qh_qh->NOerrexit){
    +        qh_qh->NOerrexit= False;
    +        QH_TRY_status= setjmp(qh_qh->errexit);
    +    }else{
    +        QH_TRY_status= QH_TRY_ERROR;
    +    }
    +    if(!QH_TRY_status){
    +    *************/
    +    QH_TRY_(qh_qh){ // no object creation -- destructors are skipped on longjmp()
    +        qh_checkflags(qh_qh, command, const_cast(s_unsupported_options));
    +        qh_initflags(qh_qh, command);
    +        *qh_qh->rbox_command= '\0';
    +        strncat( qh_qh->rbox_command, inputComment, sizeof(qh_qh->rbox_command)-1);
    +        if(qh_qh->DELAUNAY){
    +            qh_qh->PROJECTdelaunay= True;   // qh_init_B() calls qh_projectinput()
    +        }
    +        pointT *newPoints= const_cast(pointCoordinates);
    +        int newDimension= pointDimension;
    +        int newIsMalloc= False;
    +        if(qh_qh->HALFspace){
    +            --newDimension;
    +            initializeFeasiblePoint(newDimension);
    +            newPoints= qh_sethalfspace_all(qh_qh, pointDimension, pointCount, newPoints, qh_qh->feasible_point);
    +            newIsMalloc= True;
    +        }
    +        qh_init_B(qh_qh, newPoints, pointCount, newDimension, newIsMalloc);
    +        qh_qhull(qh_qh);
    +        qh_check_output(qh_qh);
    +        qh_prepare_output(qh_qh);
    +        if(qh_qh->VERIFYoutput && !qh_qh->STOPpoint && !qh_qh->STOPcone){
    +            qh_check_points(qh_qh);
    +        }
    +    }
    +    qh_qh->NOerrexit= true;
    +    for(int k= qh_qh->hull_dim; k--; ){  // Do not move into QH_TRY block.  It may throw an error
    +        origin_point << 0.0;
    +    }
    +    qh_qh->maybeThrowQhullMessage(QH_TRY_status);
    +}//runQhull
    +
    +#//!\name Helpers -- be careful of allocating C++ objects due to setjmp/longjmp() error handling by qh_... routines
    +
    +//! initialize qh.feasible_point for half-space intersection
    +//! Sets from qh.feasible_string if available, otherwise from Qhull::feasible_point
    +//! called only once from runQhull(), otherwise it leaks memory (the same as qh_setFeasible)
    +void Qhull::
    +initializeFeasiblePoint(int hulldim)
    +{
    +    if(qh_qh->feasible_string){
    +        qh_setfeasible(qh_qh, hulldim);
    +    }else{
    +        if(feasible_point.isEmpty()){
    +            qh_fprintf(qh_qh, qh_qh->ferr, 6209, "qhull error: missing feasible point for halfspace intersection.  Use option 'Hn,n' or Qhull::setFeasiblePoint before runQhull()\n");
    +            qh_errexit(qh_qh, qh_ERRmem, NULL, NULL);
    +        }
    +        if(feasible_point.size()!=(size_t)hulldim){
    +            qh_fprintf(qh_qh, qh_qh->ferr, 6210, "qhull error: dimension of feasiblePoint should be %d.  It is %u", hulldim, feasible_point.size());
    +            qh_errexit(qh_qh, qh_ERRmem, NULL, NULL);
    +        }
    +        if (!(qh_qh->feasible_point= (coordT*)qh_malloc(hulldim * sizeof(coordT)))) {
    +            qh_fprintf(qh_qh, qh_qh->ferr, 6202, "qhull error: insufficient memory for feasible point\n");
    +            qh_errexit(qh_qh, qh_ERRmem, NULL, NULL);
    +        }
    +        coordT *t= qh_qh->feasible_point;
    +        // No qh_... routines after here -- longjmp() ignores destructor
    +        for(Coordinates::ConstIterator p=feasible_point.begin(); p.  It could be rewritten for another vector class such as QList
    +   #define QHULL_USES_QT
    +      Supply conversions to QT
    +      qhulltest requires QT.  It is defined in RoadTest.h
    +
    +  #define QHULL_ASSERT
    +      Defined by QhullError.h
    +      It invokes assert()
    +*/
    +
    +#//!\name Used here
    +    class QhullFacetList;
    +    class QhullPoints;
    +    class QhullQh;
    +    class RboxPoints;
    +
    +#//!\name Defined here
    +    class Qhull;
    +
    +//! Interface to Qhull from C++
    +class Qhull {
    +
    +private:
    +#//!\name Members and friends
    +    QhullQh *           qh_qh;          //! qhT for this instance
    +    Coordinates         origin_point;   //! origin for qh_qh->hull_dim.  Set by runQhull()
    +    bool                run_called;     //! True at start of runQhull.  Errors if call again.
    +    Coordinates         feasible_point;  //! feasible point for half-space intersection (alternative to qh.feasible_string for qh.feasible_point)
    +
    +public:
    +#//!\name Constructors
    +                        Qhull();      //!< call runQhull() next
    +                        Qhull(const RboxPoints &rboxPoints, const char *qhullCommand2);
    +                        Qhull(const char *inputComment2, int pointDimension, int pointCount, const realT *pointCoordinates, const char *qhullCommand2);
    +                        ~Qhull() throw();
    +private:                //! Disable copy constructor and assignment.  Qhull owns QhullQh.
    +                        Qhull(const Qhull &);
    +    Qhull &             operator=(const Qhull &);
    +
    +private:
    +    void                allocateQhullQh();
    +
    +public:
    +
    +#//!\name GetSet
    +    void                checkIfQhullInitialized();
    +    int                 dimension() const { return qh_qh->input_dim; } //!< Dimension of input and result
    +    void                disableOutputStream() { qh_qh->disableOutputStream(); }
    +    void                enableOutputStream() { qh_qh->enableOutputStream(); }
    +    countT              facetCount() const { return qh_qh->num_facets; }
    +    Coordinates         feasiblePoint() const; 
    +    int                 hullDimension() const { return qh_qh->hull_dim; } //!< Dimension of the computed hull
    +    bool                hasOutputStream() const { return qh_qh->hasOutputStream(); }
    +    bool                initialized() const { return (qh_qh->hull_dim>0); }
    +    const char *        inputComment() const { return qh_qh->rbox_command; }
    +    QhullPoint          inputOrigin();
    +                        //! non-const due to QhullPoint
    +    QhullPoint          origin() { QHULL_ASSERT(initialized()); return QhullPoint(qh_qh, origin_point.data()); }
    +    QhullQh *           qh() const { return qh_qh; };
    +    const char *        qhullCommand() const { return qh_qh->qhull_command; }
    +    const char *        rboxCommand() const { return qh_qh->rbox_command; }
    +    int                 rotateRandom() const { return qh_qh->ROTATErandom; } //!< Return QRn for repeating QR0 runs
    +    void                setFeasiblePoint(const Coordinates &c) { feasible_point= c; } //!< Sets qh.feasible_point via initializeFeasiblePoint
    +    countT              vertexCount() const { return qh_qh->num_vertices; }
    +
    +#//!\name Delegated to QhullQh
    +    double              angleEpsilon() const { return qh_qh->angleEpsilon(); } //!< Epsilon for hyperplane angle equality
    +    void                appendQhullMessage(const std::string &s) { qh_qh->appendQhullMessage(s); }
    +    void                clearQhullMessage() { qh_qh->clearQhullMessage(); }
    +    double              distanceEpsilon() const { return qh_qh->distanceEpsilon(); } //!< Epsilon for distance to hyperplane
    +    double              factorEpsilon() const { return qh_qh->factorEpsilon(); }  //!< Factor for angleEpsilon and distanceEpsilon
    +    std::string         qhullMessage() const { return qh_qh->qhullMessage(); }
    +    bool                hasQhullMessage() const { return qh_qh->hasQhullMessage(); }
    +    int                 qhullStatus() const { return qh_qh->qhullStatus(); }
    +    void                setErrorStream(std::ostream *os) { qh_qh->setErrorStream(os); }
    +    void                setFactorEpsilon(double a) { qh_qh->setFactorEpsilon(a); }
    +    void                setOutputStream(std::ostream *os) { qh_qh->setOutputStream(os); }
    +
    +#//!\name ForEach
    +    QhullFacet          beginFacet() const { return QhullFacet(qh_qh, qh_qh->facet_list); }
    +    QhullVertex         beginVertex() const { return QhullVertex(qh_qh, qh_qh->vertex_list); }
    +    void                defineVertexNeighborFacets(); //!< Automatically called if merging facets or Voronoi diagram
    +    QhullFacet          endFacet() const { return QhullFacet(qh_qh, qh_qh->facet_tail); }
    +    QhullVertex         endVertex() const { return QhullVertex(qh_qh, qh_qh->vertex_tail); }
    +    QhullFacetList      facetList() const;
    +    QhullFacet          firstFacet() const { return beginFacet(); }
    +    QhullVertex         firstVertex() const { return beginVertex(); }
    +    QhullPoints         points() const;
    +    QhullPointSet       otherPoints() const;
    +                        //! Same as points().coordinates()
    +    coordT *            pointCoordinateBegin() const { return qh_qh->first_point; }
    +    coordT *            pointCoordinateEnd() const { return qh_qh->first_point + qh_qh->num_points*qh_qh->hull_dim; }
    +    QhullVertexList     vertexList() const;
    +
    +#//!\name Methods
    +    double              area();
    +    void                outputQhull();
    +    void                outputQhull(const char * outputflags);
    +    void                runQhull(const RboxPoints &rboxPoints, const char *qhullCommand2);
    +    void                runQhull(const char *inputComment2, int pointDimension, int pointCount, const realT *pointCoordinates, const char *qhullCommand2);
    +    double              volume();
    +
    +#//!\name Helpers
    +private:
    +    void                initializeFeasiblePoint(int hulldim);
    +};//Qhull
    +
    +}//namespace orgQhull
    +
    +#endif // QHULLCPP_H
    diff --git a/xs/src/qhull/src/libqhullcpp/QhullError.h b/xs/src/qhull/src/libqhullcpp/QhullError.h
    new file mode 100644
    index 000000000..08d50aa0f
    --- /dev/null
    +++ b/xs/src/qhull/src/libqhullcpp/QhullError.h
    @@ -0,0 +1,62 @@
    +/****************************************************************************
    +**
    +** Copyright (c) 2008-2015 C.B. Barber. All rights reserved.
    +** $Id: //main/2015/qhull/src/libqhullcpp/QhullError.h#2 $$Change: 2066 $
    +** $DateTime: 2016/01/18 19:29:17 $$Author: bbarber $
    +**
    +****************************************************************************/
    +
    +#ifndef QHULLERROR_H
    +#define QHULLERROR_H
    +
    +#include "libqhullcpp/RoadError.h"
    +// No dependencies on libqhull
    +
    +#ifndef QHULL_ASSERT
    +#define QHULL_ASSERT assert
    +#include 
    +#endif
    +
    +namespace orgQhull {
    +
    +#//!\name Defined here
    +    //! QhullError -- std::exception class for Qhull
    +    class QhullError;
    +
    +class QhullError : public RoadError {
    +
    +public:
    +#//!\name Constants
    +    enum {
    +        QHULLfirstError= 10000, //MSG_QHULL_ERROR in Qhull's user.h
    +        QHULLlastError= 10078,
    +        NOthrow= 1 //! For flag to indexOf()
    +    };
    +
    +#//!\name Constructors
    +    // default constructors
    +    QhullError() : RoadError() {};
    +    QhullError(const QhullError &other) : RoadError(other) {}
    +    QhullError(int code, const std::string &message) : RoadError(code, message) {};
    +    QhullError(int code, const char *fmt) : RoadError(code, fmt) {};
    +    QhullError(int code, const char *fmt, int d) : RoadError(code, fmt, d) {};
    +    QhullError(int code, const char *fmt, int d, int d2) : RoadError(code, fmt, d, d2) {};
    +    QhullError(int code, const char *fmt, int d, int d2, float f) : RoadError(code, fmt, d, d2, f) {};
    +    QhullError(int code, const char *fmt, int d, int d2, float f, const char *s) : RoadError(code, fmt, d, d2, f, s) {};
    +    QhullError(int code, const char *fmt, int d, int d2, float f, const void *x) : RoadError(code, fmt, d, d2, f, x) {};
    +    QhullError(int code, const char *fmt, int d, int d2, float f, int i) : RoadError(code, fmt, d, d2, f, i) {};
    +    QhullError(int code, const char *fmt, int d, int d2, float f, long long i) : RoadError(code, fmt, d, d2, f, i) {};
    +    QhullError(int code, const char *fmt, int d, int d2, float f, double e) : RoadError(code, fmt, d, d2, f, e) {};
    +    QhullError &operator=(const QhullError &other) { this->RoadError::operator=(other); return *this; }
    +    ~QhullError() throw() {}
    +
    +};//class QhullError
    +
    +
    +}//namespace orgQhull
    +
    +#//!\name Global
    +
    +inline std::ostream &operator<<(std::ostream &os, const orgQhull::QhullError &e) { return os << e.what(); }
    +
    +#endif // QHULLERROR_H
    diff --git a/xs/src/qhull/src/libqhullcpp/QhullFacet.cpp b/xs/src/qhull/src/libqhullcpp/QhullFacet.cpp
    new file mode 100644
    index 000000000..40d3828a4
    --- /dev/null
    +++ b/xs/src/qhull/src/libqhullcpp/QhullFacet.cpp
    @@ -0,0 +1,519 @@
    +/****************************************************************************
    +**
    +** Copyright (c) 2008-2015 C.B. Barber. All rights reserved.
    +** $Id: //main/2015/qhull/src/libqhullcpp/QhullFacet.cpp#3 $$Change: 2066 $
    +** $DateTime: 2016/01/18 19:29:17 $$Author: bbarber $
    +**
    +****************************************************************************/
    +
    +#//! QhullFacet -- Qhull's facet structure, facetT, as a C++ class
    +
    +#include "libqhullcpp/QhullFacet.h"
    +
    +#include "libqhullcpp/QhullError.h"
    +#include "libqhullcpp/Qhull.h"
    +#include "libqhullcpp/QhullSet.h"
    +#include "libqhullcpp/QhullPoint.h"
    +#include "libqhullcpp/QhullPointSet.h"
    +#include "libqhullcpp/QhullRidge.h"
    +#include "libqhullcpp/QhullFacetSet.h"
    +#include "libqhullcpp/QhullVertex.h"
    +
    +#include 
    +
    +using std::endl;
    +using std::ostream;
    +
    +#ifdef _MSC_VER  // Microsoft Visual C++ -- warning level 4
    +#pragma warning( disable : 4611)  // interaction between '_setjmp' and C++ object destruction is non-portable
    +#pragma warning( disable : 4996)  // function was declared deprecated(strcpy, localtime, etc.)
    +#endif
    +
    +namespace orgQhull {
    +
    +#//!\name Class objects
    +facetT QhullFacet::
    +s_empty_facet= {0,0,0,0,{0},
    +        0,0,0,0,0,
    +        0,0,0,0,0,
    +        0,0,0,0,0,
    +        0,0,0,0,0,
    +        0,0,0,0,0,
    +        0,0,0,0,0,
    +        0,0,0,0};
    +
    +#//!\name Constructors
    +
    +QhullFacet::
    +QhullFacet(const Qhull &q) 
    +: qh_facet(&s_empty_facet)
    +, qh_qh(q.qh())
    +{
    +}
    +
    +QhullFacet::
    +QhullFacet(const Qhull &q, facetT *f) 
    +: qh_facet(f ? f : &s_empty_facet)
    +, qh_qh(q.qh())
    +{
    +}
    +
    +#//!\name GetSet
    +
    +//! Return voronoi center or facet centrum.  Derived from qh_printcenter [io_r.c]
    +//! if printFormat=qh_PRINTtriangles and qh.DELAUNAY, returns centrum of a Delaunay facet
    +//! Sets center if needed
    +//! Code duplicated for PrintCenter and getCenter
    +//! Returns QhullPoint() if none or qh_INFINITE
    +QhullPoint QhullFacet::
    +getCenter(qh_PRINT printFormat)
    +{
    +    if(!qh_qh){
    +        // returns QhullPoint()
    +    }else if(qh_qh->CENTERtype==qh_ASvoronoi){
    +        if(!qh_facet->normal || !qh_facet->upperdelaunay || !qh_qh->ATinfinity){
    +            if(!qh_facet->center){
    +                QH_TRY_(qh_qh){ // no object creation -- destructors skipped on longjmp()
    +                    qh_facet->center= qh_facetcenter(qh_qh, qh_facet->vertices);
    +                }
    +                qh_qh->NOerrexit= true;
    +                qh_qh->maybeThrowQhullMessage(QH_TRY_status);
    +            }
    +            return QhullPoint(qh_qh, qh_qh->hull_dim-1, qh_facet->center);
    +        }
    +    }else if(qh_qh->CENTERtype==qh_AScentrum){
    +        volatile int numCoords= qh_qh->hull_dim;
    +        if(printFormat==qh_PRINTtriangles && qh_qh->DELAUNAY){
    +            numCoords--;
    +        }
    +        if(!qh_facet->center){
    +            QH_TRY_(qh_qh){ // no object creation -- destructors skipped on longjmp()
    +                qh_facet->center= qh_getcentrum(qh_qh, getFacetT());
    +            }
    +            qh_qh->NOerrexit= true;
    +            qh_qh->maybeThrowQhullMessage(QH_TRY_status);
    +        }
    +        return QhullPoint(qh_qh, numCoords, qh_facet->center);
    +    }
    +    return QhullPoint();
    + }//getCenter
    +
    +//! Return innerplane clearly below the vertices
    +//! from io_r.c[qh_PRINTinner]
    +QhullHyperplane QhullFacet::
    +innerplane() const{
    +    QhullHyperplane h;
    +    if(qh_qh){
    +        realT inner;
    +        // Does not error, TRY_QHULL_ not needed
    +        qh_outerinner(qh_qh, const_cast(getFacetT()), NULL, &inner);
    +        h= hyperplane();
    +        h.setOffset(h.offset()-inner); //inner is negative
    +    }
    +    return h;
    +}//innerplane
    +
    +//! Return outerplane clearly above all points
    +//! from io_r.c[qh_PRINTouter]
    +QhullHyperplane QhullFacet::
    +outerplane() const{
    +    QhullHyperplane h;
    +    if(qh_qh){
    +        realT outer;
    +        // Does not error, TRY_QHULL_ not needed
    +        qh_outerinner(qh_qh, const_cast(getFacetT()), &outer, NULL);
    +        h= hyperplane();
    +        h.setOffset(h.offset()-outer); //outer is positive
    +    }
    +    return h;
    +}//outerplane
    +
    +//! Set by qh_triangulate for option 'Qt'.
    +//! Errors if tricoplanar and facetArea() or qh_getarea() called first.
    +QhullFacet QhullFacet::
    +tricoplanarOwner() const
    +{
    +    if(qh_facet->tricoplanar){
    +        if(qh_facet->isarea){
    +            throw QhullError(10018, "Qhull error: facetArea() or qh_getarea() previously called.  triCoplanarOwner() is not available.");
    +        }
    +        return QhullFacet(qh_qh, qh_facet->f.triowner);
    +    }
    +    return QhullFacet(qh_qh); 
    +}//tricoplanarOwner
    +
    +QhullPoint QhullFacet::
    +voronoiVertex()
    +{
    +    if(qh_qh && qh_qh->CENTERtype!=qh_ASvoronoi){
    +          throw QhullError(10052, "Error: QhullFacet.voronoiVertex() requires option 'v' (qh_ASvoronoi)");
    +    }
    +    return getCenter();
    +}//voronoiVertex
    +
    +#//!\name Value
    +
    +//! Disables tricoplanarOwner()
    +double QhullFacet::
    +facetArea()
    +{
    +    if(qh_qh && !qh_facet->isarea){
    +        QH_TRY_(qh_qh){ // no object creation -- destructors skipped on longjmp()
    +            qh_facet->f.area= qh_facetarea(qh_qh, qh_facet);
    +            qh_facet->isarea= True;
    +        }
    +        qh_qh->NOerrexit= true;
    +        qh_qh->maybeThrowQhullMessage(QH_TRY_status);
    +    }
    +    return qh_facet->f.area;
    +}//facetArea
    +
    +#//!\name Foreach
    +
    +QhullPointSet QhullFacet::
    +coplanarPoints() const
    +{
    +    return QhullPointSet(qh_qh, qh_facet->coplanarset);
    +}//coplanarPoints
    +
    +QhullFacetSet QhullFacet::
    +neighborFacets() const
    +{
    +    return QhullFacetSet(qh_qh, qh_facet->neighbors);
    +}//neighborFacets
    +
    +QhullPointSet QhullFacet::
    +outsidePoints() const
    +{
    +    return QhullPointSet(qh_qh, qh_facet->outsideset);
    +}//outsidePoints
    +
    +QhullRidgeSet QhullFacet::
    +ridges() const
    +{
    +    return QhullRidgeSet(qh_qh, qh_facet->ridges);
    +}//ridges
    +
    +QhullVertexSet QhullFacet::
    +vertices() const
    +{
    +    return QhullVertexSet(qh_qh, qh_facet->vertices);
    +}//vertices
    +
    +}//namespace orgQhull
    +
    +#//!\name operator<<
    +
    +using std::ostream;
    +
    +using orgQhull::QhullFacet;
    +using orgQhull::QhullFacetSet;
    +using orgQhull::QhullPoint;
    +using orgQhull::QhullPointSet;
    +using orgQhull::QhullRidge;
    +using orgQhull::QhullRidgeSet;
    +using orgQhull::QhullSetBase;
    +using orgQhull::QhullVertexSet;
    +
    +ostream &
    +operator<<(ostream &os, const QhullFacet::PrintFacet &pr)
    +{
    +    os << pr.message;
    +    QhullFacet f= *pr.facet;
    +    if(f.getFacetT()==0){ // Special values from set iterator
    +        os << " NULLfacet" << endl;
    +        return os;
    +    }
    +    if(f.getFacetT()==qh_MERGEridge){
    +        os << " MERGEridge" << endl;
    +        return os;
    +    }
    +    if(f.getFacetT()==qh_DUPLICATEridge){
    +        os << " DUPLICATEridge" << endl;
    +        return os;
    +    }
    +    os << f.printHeader();
    +    if(!f.ridges().isEmpty()){
    +        os << f.printRidges();
    +    }
    +    return os;
    +}//operator<< PrintFacet
    +
    +//! Print Voronoi center or facet centrum to stream.  Same as qh_printcenter [_r.]
    +//! Code duplicated for PrintCenter and getCenter
    +//! Sets center if needed
    +ostream &
    +operator<<(ostream &os, const QhullFacet::PrintCenter &pr)
    +{
    +    facetT *f= pr.facet->getFacetT();
    +    if(pr.facet->qh()->CENTERtype!=qh_ASvoronoi && pr.facet->qh()->CENTERtype!=qh_AScentrum){
    +        return os;
    +    }
    +    if (pr.message){
    +        os << pr.message;
    +    }
    +    int numCoords;
    +    if(pr.facet->qh()->CENTERtype==qh_ASvoronoi){
    +        numCoords= pr.facet->qh()->hull_dim-1;
    +        if(!f->normal || !f->upperdelaunay || !pr.facet->qh()->ATinfinity){
    +            if(!f->center){
    +                f->center= qh_facetcenter(pr.facet->qh(), f->vertices);
    +            }
    +            for(int k=0; kcenter[k] << " "; // FIXUP QH11010 qh_REAL_1
    +            }
    +        }else{
    +            for(int k=0; kqh()->hull_dim;
    +        if(pr.print_format==qh_PRINTtriangles && pr.facet->qh()->DELAUNAY){
    +            numCoords--;
    +        }
    +        if(!f->center){
    +            f->center= qh_getcentrum(pr.facet->qh(), f);
    +        }
    +        for(int k=0; kcenter[k] << " "; // FIXUP QH11010 qh_REAL_1
    +        }
    +    }
    +    if(pr.print_format==qh_PRINTgeom && numCoords==2){
    +        os << " 0";
    +    }
    +    os << endl;
    +    return os;
    +}//operator<< PrintCenter
    +
    +//! Print flags for facet to stream.  Space prefix.  From qh_printfacetheader [io_r.c]
    +ostream &
    +operator<<(ostream &os, const QhullFacet::PrintFlags &p)
    +{
    +    const facetT *f= p.facet->getFacetT();
    +    if(p.message){
    +        os << p.message;
    +    }
    +
    +    os << (p.facet->isTopOrient() ? " top" : " bottom");
    +    if(p.facet->isSimplicial()){
    +        os << " simplicial";
    +    }
    +    if(p.facet->isTriCoplanar()){
    +        os << " tricoplanar";
    +    }
    +    if(p.facet->isUpperDelaunay()){
    +        os << " upperDelaunay";
    +    }
    +    if(f->visible){
    +        os << " visible";
    +    }
    +    if(f->newfacet){
    +        os << " new";
    +    }
    +    if(f->tested){
    +        os << " tested";
    +    }
    +    if(!f->good){
    +        os << " notG";
    +    }
    +    if(f->seen){
    +        os << " seen";
    +    }
    +    if(f->coplanar){
    +        os << " coplanar";
    +    }
    +    if(f->mergehorizon){
    +        os << " mergehorizon";
    +    }
    +    if(f->keepcentrum){
    +        os << " keepcentrum";
    +    }
    +    if(f->dupridge){
    +        os << " dupridge";
    +    }
    +    if(f->mergeridge && !f->mergeridge2){
    +        os << " mergeridge1";
    +    }
    +    if(f->mergeridge2){
    +        os << " mergeridge2";
    +    }
    +    if(f->newmerge){
    +        os << " newmerge";
    +    }
    +    if(f->flipped){
    +        os << " flipped";
    +    }
    +    if(f->notfurthest){
    +        os << " notfurthest";
    +    }
    +    if(f->degenerate){
    +        os << " degenerate";
    +    }
    +    if(f->redundant){
    +        os << " redundant";
    +    }
    +    os << endl;
    +    return os;
    +}//operator<< PrintFlags
    +
    +//! Print header for facet to stream. Space prefix.  From qh_printfacetheader [io_r.c]
    +ostream &
    +operator<<(ostream &os, const QhullFacet::PrintHeader &pr)
    +{
    +    QhullFacet facet= *pr.facet;
    +    facetT *f= facet.getFacetT();
    +    os << "- f" << facet.id() << endl;
    +    os << facet.printFlags("    - flags:");
    +    if(f->isarea){
    +        os << "    - area: " << f->f.area << endl; //FIXUP QH11010 2.2g
    +    }else if(pr.facet->qh()->NEWfacets && f->visible && f->f.replace){
    +        os << "    - replacement: f" << f->f.replace->id << endl;
    +    }else if(f->newfacet){
    +        if(f->f.samecycle && f->f.samecycle != f){
    +            os << "    - shares same visible/horizon as f" << f->f.samecycle->id << endl;
    +        }
    +    }else if(f->tricoplanar /* !isarea */){
    +        if(f->f.triowner){
    +            os << "    - owner of normal & centrum is facet f" << f->f.triowner->id << endl;
    +        }
    +    }else if(f->f.newcycle){
    +        os << "    - was horizon to f" << f->f.newcycle->id << endl;
    +    }
    +    if(f->nummerge){
    +        os << "    - merges: " << f->nummerge << endl;
    +    }
    +    os << facet.hyperplane().print("    - normal: ", "\n    - offset: "); // FIXUP QH11010 %10.7g
    +    if(pr.facet->qh()->CENTERtype==qh_ASvoronoi || f->center){
    +        os << facet.printCenter(qh_PRINTfacets, "    - center: ");
    +    }
    +#if qh_MAXoutside
    +    if(f->maxoutside > pr.facet->qh()->DISTround){
    +        os << "    - maxoutside: " << f->maxoutside << endl; //FIXUP QH11010 %10.7g
    +    }
    +#endif
    +    QhullPointSet ps= facet.outsidePoints();
    +    if(!ps.isEmpty()){
    +        QhullPoint furthest= ps.last();
    +        if (ps.size() < 6) {
    +            os << "    - outside set(furthest p" << furthest.id() << "):" << endl;
    +            for(QhullPointSet::iterator i=ps.begin(); i!=ps.end(); ++i){
    +                QhullPoint p= *i;
    +                os << p.print("     ");
    +            }
    +        }else if(ps.size()<21){
    +            os << ps.print("    - outside set:");
    +        }else{
    +            os << "    - outside set:  " << ps.size() << " points.";
    +            os << furthest.print("  Furthest");
    +        }
    +#if !qh_COMPUTEfurthest
    +        os << "    - furthest distance= " << f->furthestdist << endl; //FIXUP QH11010 %2.2g
    +#endif
    +    }
    +    QhullPointSet cs= facet.coplanarPoints();
    +    if(!cs.isEmpty()){
    +        QhullPoint furthest= cs.last();
    +        if (cs.size() < 6) {
    +            os << "    - coplanar set(furthest p" << furthest.id() << "):" << endl;
    +            for(QhullPointSet::iterator i=cs.begin(); i!=cs.end(); ++i){
    +                QhullPoint p= *i;
    +                os << p.print("     ");
    +            }
    +        }else if(cs.size()<21){
    +            os << cs.print("    - coplanar set:");
    +        }else{
    +            os << "    - coplanar set:  " << cs.size() << " points.";
    +            os << furthest.print("  Furthest");
    +        }
    +        // FIXUP QH11027 Can/should zinc_(Zdistio) be called from C++ interface
    +        double d= facet.distance(furthest);
    +        os << "      furthest distance= " << d << endl; //FIXUP QH11010 %2.2g
    +    }
    +    QhullVertexSet vs= facet.vertices();
    +    if(!vs.isEmpty()){
    +        os << vs.print("    - vertices:");
    +    }
    +    QhullFacetSet fs= facet.neighborFacets();
    +    fs.selectAll();
    +    if(!fs.isEmpty()){
    +        os << fs.printIdentifiers("    - neighboring facets:");
    +    }
    +    return os;
    +}//operator<< PrintHeader
    +
    +
    +//! Print ridges of facet to stream.  Same as qh_printfacetridges [io_r.c]
    +ostream &
    +operator<<(ostream &os, const QhullFacet::PrintRidges &pr)
    +{
    +    const QhullFacet facet= *pr.facet;
    +    facetT *f= facet.getFacetT();
    +    QhullRidgeSet rs= facet.ridges();
    +    if(!rs.isEmpty()){
    +        if(f->visible && pr.facet->qh()->NEWfacets){
    +            os << "    - ridges(ids may be garbage):";
    +            for(QhullRidgeSet::iterator i=rs.begin(); i!=rs.end(); ++i){
    +                QhullRidge r= *i;
    +                os << " r" << r.id();
    +            }
    +            os << endl;
    +        }else{
    +            os << "    - ridges:" << endl;
    +        }
    +
    +        // Keep track of printed ridges
    +        for(QhullRidgeSet::iterator i=rs.begin(); i!=rs.end(); ++i){
    +            QhullRidge r= *i;
    +            r.getRidgeT()->seen= false;
    +        }
    +        int ridgeCount= 0;
    +        if(facet.dimension()==3){
    +            for(QhullRidge r= rs.first(); !r.getRidgeT()->seen; r= r.nextRidge3d(facet)){
    +                r.getRidgeT()->seen= true;
    +                os << r.print("");
    +                ++ridgeCount;
    +                if(!r.hasNextRidge3d(facet)){
    +                    break;
    +                }
    +            }
    +        }else {
    +            QhullFacetSet ns(facet.neighborFacets());
    +            for(QhullFacetSet::iterator i=ns.begin(); i!=ns.end(); ++i){
    +                QhullFacet neighbor= *i;
    +                QhullRidgeSet nrs(neighbor.ridges());
    +                for(QhullRidgeSet::iterator j=nrs.begin(); j!=nrs.end(); ++j){
    +                    QhullRidge r= *j;
    +                    if(r.otherFacet(neighbor)==facet){
    +                        r.getRidgeT()->seen= true;
    +                        os << r.print("");
    +                        ridgeCount++;
    +                    }
    +                }
    +            }
    +        }
    +        if(ridgeCount!=rs.count()){
    +            os << "     - all ridges:";
    +            for(QhullRidgeSet::iterator i=rs.begin(); i!=rs.end(); ++i){
    +                QhullRidge r= *i;
    +                os << " r" << r.id();
    +            }
    +            os << endl;
    +        }
    +        for(QhullRidgeSet::iterator i=rs.begin(); i!=rs.end(); ++i){
    +            QhullRidge r= *i;
    +            if(!r.getRidgeT()->seen){
    +                os << r.print("");
    +            }
    +        }
    +    }
    +    return os;
    +}//operator<< PrintRidges
    +
    +// "No conversion" error if defined inline
    +ostream &
    +operator<<(ostream &os, QhullFacet &f)
    +{
    +    os << f.print("");
    +    return os;
    +}//<< QhullFacet
    diff --git a/xs/src/qhull/src/libqhullcpp/QhullFacet.h b/xs/src/qhull/src/libqhullcpp/QhullFacet.h
    new file mode 100644
    index 000000000..ae4f008fd
    --- /dev/null
    +++ b/xs/src/qhull/src/libqhullcpp/QhullFacet.h
    @@ -0,0 +1,151 @@
    +/****************************************************************************
    +**
    +** Copyright (c) 2008-2015 C.B. Barber. All rights reserved.
    +** $Id: //main/2015/qhull/src/libqhullcpp/QhullFacet.h#4 $$Change: 2079 $
    +** $DateTime: 2016/02/07 17:43:34 $$Author: bbarber $
    +**
    +****************************************************************************/
    +
    +#ifndef QHULLFACET_H
    +#define QHULLFACET_H
    +
    +#include "libqhull_r/qhull_ra.h"
    +#include "libqhullcpp/QhullHyperplane.h"
    +#include "libqhullcpp/QhullPoint.h"
    +#include "libqhullcpp/QhullSet.h"
    +#include "libqhullcpp/QhullPointSet.h"
    +
    +#include 
    +
    +namespace orgQhull {
    +
    +#//!\name Used here
    +    class Coordinates;
    +    class Qhull;
    +    class QhullFacetSet;
    +    class QhullRidge;
    +    class QhullVertex;
    +    class QhullVertexSet;
    +
    +#//!\name Defined here
    +    class QhullFacet;
    +    typedef QhullSet  QhullRidgeSet;
    +
    +//! A QhullFacet is the C++ equivalent to Qhull's facetT*
    +class QhullFacet {
    +
    +#//!\name Defined here
    +public:
    +    typedef facetT *   base_type;  // for QhullVertexSet
    +
    +private:
    +#//!\name Fields -- no additions (QhullFacetSet of facetT*)
    +    facetT *            qh_facet;  //!< Corresponding facetT, may be 0 for corner cases (e.g., *facetSet.end()==0) and tricoplanarOwner()
    +    QhullQh *           qh_qh;     //!< QhullQh/qhT for facetT, may be 0
    +
    +#//!\name Class objects
    +    static facetT       s_empty_facet; // needed for shallow copy
    +
    +public:
    +#//!\name Constructors
    +                        QhullFacet() : qh_facet(&s_empty_facet), qh_qh(0) {}
    +    explicit            QhullFacet(const Qhull &q);
    +                        QhullFacet(const Qhull &q, facetT *f);
    +    explicit            QhullFacet(QhullQh *qqh) : qh_facet(&s_empty_facet), qh_qh(qqh) {}
    +                        QhullFacet(QhullQh *qqh, facetT *f) : qh_facet(f ? f : &s_empty_facet), qh_qh(qqh) {}
    +                        // Creates an alias.  Does not copy QhullFacet.  Needed for return by value and parameter passing
    +                        QhullFacet(const QhullFacet &other) : qh_facet(other.qh_facet ? other.qh_facet : &s_empty_facet), qh_qh(other.qh_qh) {}
    +                        // Creates an alias.  Does not copy QhullFacet.  Needed for vector
    +    QhullFacet &        operator=(const QhullFacet &other) { qh_facet= other.qh_facet ? other.qh_facet : &s_empty_facet; qh_qh= other.qh_qh; return *this; }
    +                        ~QhullFacet() {}
    +
    +
    +#//!\name GetSet
    +    int                 dimension() const { return (qh_qh ? qh_qh->hull_dim : 0); }
    +    QhullPoint          getCenter() { return getCenter(qh_PRINTpoints); }
    +    QhullPoint          getCenter(qh_PRINT printFormat);
    +    facetT *            getBaseT() const { return getFacetT(); } //!< For QhullSet
    +                        // Do not define facetT().  It conflicts with return type facetT*
    +    facetT *            getFacetT() const { return qh_facet; }
    +    QhullHyperplane     hyperplane() const { return QhullHyperplane(qh_qh, dimension(), qh_facet->normal, qh_facet->offset); }
    +    countT              id() const { return (qh_facet ? qh_facet->id : (int)qh_IDunknown); }
    +    QhullHyperplane     innerplane() const;
    +    bool                isValid() const { return qh_qh && qh_facet && qh_facet != &s_empty_facet; }
    +    bool                isGood() const { return qh_facet && qh_facet->good; }
    +    bool                isSimplicial() const { return qh_facet && qh_facet->simplicial; }
    +    bool                isTopOrient() const { return qh_facet && qh_facet->toporient; }
    +    bool                isTriCoplanar() const { return qh_facet && qh_facet->tricoplanar; }
    +    bool                isUpperDelaunay() const { return qh_facet && qh_facet->upperdelaunay; }
    +    QhullFacet          next() const { return QhullFacet(qh_qh, qh_facet->next); }
    +    bool                operator==(const QhullFacet &other) const { return qh_facet==other.qh_facet; }
    +    bool                operator!=(const QhullFacet &other) const { return !operator==(other); }
    +    QhullHyperplane     outerplane() const;
    +    QhullFacet          previous() const { return QhullFacet(qh_qh, qh_facet->previous); }
    +    QhullQh *           qh() const { return qh_qh; }
    +    QhullFacet          tricoplanarOwner() const;
    +    QhullPoint          voronoiVertex();
    +
    +#//!\name value
    +    //! Undefined if c.size() != dimension()
    +    double              distance(const Coordinates &c) const { return distance(c.data()); }
    +    double              distance(const pointT *p) const { return distance(QhullPoint(qh_qh, const_cast(p))); }
    +    double              distance(const QhullPoint &p) const { return hyperplane().distance(p); }
    +    double              facetArea();
    +
    +#//!\name foreach
    +    // Can not inline.  Otherwise circular reference
    +    QhullPointSet       coplanarPoints() const;
    +    QhullFacetSet       neighborFacets() const;
    +    QhullPointSet       outsidePoints() const;
    +    QhullRidgeSet       ridges() const;
    +    QhullVertexSet      vertices() const;
    +
    +#//!\name IO
    +    struct PrintCenter{
    +        QhullFacet *    facet;  // non-const due to facet.center()
    +        const char *    message;
    +        qh_PRINT        print_format;
    +                        PrintCenter(QhullFacet &f, qh_PRINT printFormat, const char * s) : facet(&f), message(s), print_format(printFormat){}
    +    };//PrintCenter
    +    PrintCenter         printCenter(qh_PRINT printFormat, const char *message) { return PrintCenter(*this, printFormat, message); }
    +
    +    struct PrintFacet{
    +        QhullFacet *    facet;  // non-const due to f->center()
    +        const char *    message;
    +        explicit        PrintFacet(QhullFacet &f, const char * s) : facet(&f), message(s) {}
    +    };//PrintFacet
    +    PrintFacet          print(const char *message) { return PrintFacet(*this, message); }
    +
    +    struct PrintFlags{
    +        const QhullFacet *facet;
    +        const char *    message;
    +                        PrintFlags(const QhullFacet &f, const char *s) : facet(&f), message(s) {}
    +    };//PrintFlags
    +    PrintFlags          printFlags(const char *message) const { return PrintFlags(*this, message); }
    +
    +    struct PrintHeader{
    +        QhullFacet *    facet;  // non-const due to f->center()
    +                        PrintHeader(QhullFacet &f) : facet(&f) {}
    +    };//PrintHeader
    +    PrintHeader         printHeader() { return PrintHeader(*this); }
    +
    +    struct PrintRidges{
    +        const QhullFacet *facet;
    +                        PrintRidges(QhullFacet &f) : facet(&f) {}
    +    };//PrintRidges
    +    PrintRidges         printRidges() { return PrintRidges(*this); }
    +
    +};//class QhullFacet
    +
    +}//namespace orgQhull
    +
    +#//!\name Global
    +
    +std::ostream &operator<<(std::ostream &os, const orgQhull::QhullFacet::PrintFacet &pr);
    +std::ostream &operator<<(std::ostream &os, const orgQhull::QhullFacet::PrintCenter &pr);
    +std::ostream &operator<<(std::ostream &os, const orgQhull::QhullFacet::PrintFlags &pr);
    +std::ostream &operator<<(std::ostream &os, const orgQhull::QhullFacet::PrintHeader &pr);
    +std::ostream &operator<<(std::ostream &os, const orgQhull::QhullFacet::PrintRidges &pr);
    +std::ostream &operator<<(std::ostream &os, orgQhull::QhullFacet &f); // non-const due to qh_getcenter()
    +
    +#endif // QHULLFACET_H
    diff --git a/xs/src/qhull/src/libqhullcpp/QhullFacetList.cpp b/xs/src/qhull/src/libqhullcpp/QhullFacetList.cpp
    new file mode 100644
    index 000000000..9e6ddfe9e
    --- /dev/null
    +++ b/xs/src/qhull/src/libqhullcpp/QhullFacetList.cpp
    @@ -0,0 +1,174 @@
    +/****************************************************************************
    +**
    +** Copyright (c) 2008-2015 C.B. Barber. All rights reserved.
    +** $Id: //main/2015/qhull/src/libqhullcpp/QhullFacetList.cpp#3 $$Change: 2066 $
    +** $DateTime: 2016/01/18 19:29:17 $$Author: bbarber $
    +**
    +****************************************************************************/
    +
    +#//! QhullFacetList -- Qhull's linked facets, as a C++ class
    +
    +#include "libqhullcpp/QhullFacetList.h"
    +
    +#include "libqhullcpp/QhullFacet.h"
    +#include "libqhullcpp/QhullPoint.h"
    +#include "libqhullcpp/QhullRidge.h"
    +#include "libqhullcpp/QhullVertex.h"
    +
    +using std::string;
    +using std::vector;
    +
    +#ifdef _MSC_VER  // Microsoft Visual C++ -- warning level 4
    +#pragma warning( disable : 4611)  // interaction between '_setjmp' and C++ object destruction is non-portable
    +#pragma warning( disable : 4996)  // function was declared deprecated(strcpy, localtime, etc.)
    +#endif
    +
    +namespace orgQhull {
    +
    +#//!\name Constructors
    +
    +QhullFacetList::
    +QhullFacetList(const Qhull &q, facetT *b, facetT *e ) 
    +: QhullLinkedList(QhullFacet(q, b), QhullFacet(q, e))
    +, select_all(false)
    +{
    +}
    +
    +#//!\name Conversions
    +
    +// See qt_qhull.cpp for QList conversions
    +
    +#ifndef QHULL_NO_STL
    +std::vector QhullFacetList::
    +toStdVector() const
    +{
    +    QhullLinkedListIterator i(*this);
    +    std::vector vs;
    +    while(i.hasNext()){
    +        QhullFacet f= i.next();
    +        if(isSelectAll() || f.isGood()){
    +            vs.push_back(f);
    +        }
    +    }
    +    return vs;
    +}//toStdVector
    +#endif //QHULL_NO_STL
    +
    +#ifndef QHULL_NO_STL
    +//! Same as PrintVertices
    +std::vector QhullFacetList::
    +vertices_toStdVector() const
    +{
    +    std::vector vs;
    +    QhullVertexSet qvs(qh(), first().getFacetT(), 0, isSelectAll());
    +
    +    for(QhullVertexSet::iterator i=qvs.begin(); i!=qvs.end(); ++i){
    +        vs.push_back(*i);
    +    }
    +    return vs;
    +}//vertices_toStdVector
    +#endif //QHULL_NO_STL
    +
    +#//!\name GetSet
    +
    +bool QhullFacetList::
    +contains(const QhullFacet &facet) const
    +{
    +    if(isSelectAll()){
    +        return QhullLinkedList::contains(facet);
    +    }
    +    for(QhullFacetList::const_iterator i=begin(); i != end(); ++i){
    +        QhullFacet f= *i;
    +        if(f==facet && f.isGood()){
    +            return true;
    +        }
    +    }
    +    return false;
    +}//contains
    +
    +int QhullFacetList::
    +count() const
    +{
    +    if(isSelectAll()){
    +        return QhullLinkedList::count();
    +    }
    +    int counter= 0;
    +    for(QhullFacetList::const_iterator i=begin(); i != end(); ++i){
    +        if((*i).isGood()){
    +            counter++;
    +        }
    +    }
    +    return counter;
    +}//count
    +
    +int QhullFacetList::
    +count(const QhullFacet &facet) const
    +{
    +    if(isSelectAll()){
    +        return QhullLinkedList::count(facet);
    +    }
    +    int counter= 0;
    +    for(QhullFacetList::const_iterator i=begin(); i != end(); ++i){
    +        QhullFacet f= *i;
    +        if(f==facet && f.isGood()){
    +            counter++;
    +        }
    +    }
    +    return counter;
    +}//count
    +
    +}//namespace orgQhull
    +
    +#//!\name Global functions
    +
    +using std::endl;
    +using std::ostream;
    +using orgQhull::QhullFacet;
    +using orgQhull::QhullFacetList;
    +using orgQhull::QhullVertex;
    +using orgQhull::QhullVertexSet;
    +
    +ostream &
    +operator<<(ostream &os, const QhullFacetList::PrintFacetList &pr)
    +{
    +    os << pr.print_message;
    +    QhullFacetList fs= *pr.facet_list;
    +    os << "Vertices for " << fs.count() << " facets" << endl;
    +    os << fs.printVertices();
    +    os << fs.printFacets();
    +    return os;
    +}//operator<<
    +
    +//! Print facet list to stream.  From qh_printafacet [io_r.c]
    +ostream &
    +operator<<(ostream &os, const QhullFacetList::PrintFacets &pr)
    +{
    +    for(QhullFacetList::const_iterator i= pr.facet_list->begin(); i != pr.facet_list->end(); ++i){
    +        QhullFacet f= *i;
    +        if(pr.facet_list->isSelectAll() || f.isGood()){
    +            os << f.print("");
    +        }
    +    }
    +    return os;
    +}//printFacets
    +
    +//! Print vertices of good faces in facet list to stream.  From qh_printvertexlist [io_r.c]
    +//! Same as vertices_toStdVector
    +ostream &
    +operator<<(ostream &os, const QhullFacetList::PrintVertices &pr)
    +{
    +    QhullVertexSet vs(pr.facet_list->qh(), pr.facet_list->first().getFacetT(), NULL, pr.facet_list->isSelectAll());
    +    for(QhullVertexSet::iterator i=vs.begin(); i!=vs.end(); ++i){
    +        QhullVertex v= *i;
    +        os << v.print("");
    +    }
    +    return os;
    +}//printVertices
    +
    +std::ostream &
    +operator<<(ostream &os, const QhullFacetList &fs)
    +{
    +    os << fs.printFacets();
    +    return os;
    +}//QhullFacetList
    +
    diff --git a/xs/src/qhull/src/libqhullcpp/QhullFacetList.h b/xs/src/qhull/src/libqhullcpp/QhullFacetList.h
    new file mode 100644
    index 000000000..e61e56840
    --- /dev/null
    +++ b/xs/src/qhull/src/libqhullcpp/QhullFacetList.h
    @@ -0,0 +1,106 @@
    +/****************************************************************************
    +**
    +** Copyright (c) 2008-2015 C.B. Barber. All rights reserved.
    +** $Id: //main/2015/qhull/src/libqhullcpp/QhullFacetList.h#2 $$Change: 2066 $
    +** $DateTime: 2016/01/18 19:29:17 $$Author: bbarber $
    +**
    +****************************************************************************/
    +
    +#ifndef QHULLFACETLIST_H
    +#define QHULLFACETLIST_H
    +
    +#include "libqhullcpp/QhullLinkedList.h"
    +#include "libqhullcpp/QhullFacet.h"
    +
    +#include 
    +
    +#ifndef QHULL_NO_STL
    +#include 
    +#endif
    +
    +namespace orgQhull {
    +
    +#//!\name Used here
    +    class Qhull;
    +    class QhullFacet;
    +    class QhullQh;
    +
    +#//!\name Defined here
    +    //! QhullFacetList -- List of QhullFacet/facetT, as a C++ class.  
    +    //!\see QhullFacetSet.h
    +    class QhullFacetList;
    +    //! QhullFacetListIterator -- if(f.isGood()){ ... }
    +    typedef QhullLinkedListIterator QhullFacetListIterator;
    +
    +class QhullFacetList : public QhullLinkedList {
    +
    +#//!\name  Fields
    +private:
    +    bool                select_all;   //! True if include bad facets.  Default is false.
    +
    +#//!\name Constructors
    +public:
    +                        QhullFacetList(const Qhull &q, facetT *b, facetT *e);
    +                        QhullFacetList(QhullQh *qqh, facetT *b, facetT *e);
    +                        QhullFacetList(QhullFacet b, QhullFacet e) : QhullLinkedList(b, e), select_all(false) {}
    +                        //Copy constructor copies pointer but not contents.  Needed for return by value and parameter passing.
    +                        QhullFacetList(const QhullFacetList &other) : QhullLinkedList(*other.begin(), *other.end()), select_all(other.select_all) {}
    +    QhullFacetList &    operator=(const QhullFacetList &other) { QhullLinkedList::operator =(other); select_all= other.select_all; return *this; }
    +                        ~QhullFacetList() {}
    +
    +private:                //!Disable default constructor.  See QhullLinkedList
    +                    QhullFacetList();
    +public:
    +
    +#//!\name Conversion
    +#ifndef QHULL_NO_STL
    +    std::vector toStdVector() const;
    +    std::vector vertices_toStdVector() const;
    +#endif //QHULL_NO_STL
    +#ifdef QHULL_USES_QT
    +    QList   toQList() const;
    +    QList  vertices_toQList() const;
    +#endif //QHULL_USES_QT
    +
    +#//!\name GetSet
    +                        //! Filtered by facet.isGood().  May be 0 when !isEmpty().
    +    countT              count() const;
    +    bool                contains(const QhullFacet &f) const;
    +    countT              count(const QhullFacet &f) const;
    +    bool                isSelectAll() const { return select_all; }
    +    QhullQh *           qh() const { return first().qh(); }
    +    void                selectAll() { select_all= true; }
    +    void                selectGood() { select_all= false; }
    +                        //!< operator==() does not depend on isGood()
    +
    +#//!\name IO
    +    struct PrintFacetList{
    +        const QhullFacetList *facet_list;
    +        const char *    print_message;   //!< non-null message
    +                        PrintFacetList(const QhullFacetList &fl, const char *message) : facet_list(&fl), print_message(message) {}
    +    };//PrintFacetList
    +    PrintFacetList      print(const char *message) const  { return PrintFacetList(*this, message); }
    +
    +    struct PrintFacets{
    +        const QhullFacetList *facet_list;
    +                        PrintFacets(const QhullFacetList &fl) : facet_list(&fl) {}
    +    };//PrintFacets
    +    PrintFacets         printFacets() const { return PrintFacets(*this); }
    +
    +    struct PrintVertices{
    +        const QhullFacetList *facet_list;
    +                        PrintVertices(const QhullFacetList &fl) : facet_list(&fl) {}
    +    };//PrintVertices
    +    PrintVertices       printVertices() const { return PrintVertices(*this); }
    +};//class QhullFacetList
    +
    +}//namespace orgQhull
    +
    +#//!\name == Global namespace =========================================
    +
    +std::ostream &operator<<(std::ostream &os, const orgQhull::QhullFacetList::PrintFacetList &p);
    +std::ostream &operator<<(std::ostream &os, const orgQhull::QhullFacetList::PrintFacets &p);
    +std::ostream &operator<<(std::ostream &os, const orgQhull::QhullFacetList::PrintVertices &p);
    +std::ostream &operator<<(std::ostream &os, const orgQhull::QhullFacetList &fs);
    +
    +#endif // QHULLFACETLIST_H
    diff --git a/xs/src/qhull/src/libqhullcpp/QhullFacetSet.cpp b/xs/src/qhull/src/libqhullcpp/QhullFacetSet.cpp
    new file mode 100644
    index 000000000..d30c21e26
    --- /dev/null
    +++ b/xs/src/qhull/src/libqhullcpp/QhullFacetSet.cpp
    @@ -0,0 +1,147 @@
    +/****************************************************************************
    +**
    +** Copyright (c) 2008-2015 C.B. Barber. All rights reserved.
    +** $Id: //main/2015/qhull/src/libqhullcpp/QhullFacetSet.cpp#2 $$Change: 2066 $
    +** $DateTime: 2016/01/18 19:29:17 $$Author: bbarber $
    +**
    +****************************************************************************/
    +
    +#//! QhullFacetSet -- Qhull's linked facets, as a C++ class
    +
    +#include "libqhullcpp/QhullFacetSet.h"
    +
    +#include "libqhullcpp/QhullFacet.h"
    +#include "libqhullcpp/QhullPoint.h"
    +#include "libqhullcpp/QhullRidge.h"
    +#include "libqhullcpp/QhullVertex.h"
    +
    +#ifndef QHULL_NO_STL
    +using std::vector;
    +#endif
    +
    +#ifdef _MSC_VER  // Microsoft Visual C++ -- warning level 4
    +#pragma warning( disable : 4611)  // interaction between '_setjmp' and C++ object destruction is non-portable
    +#pragma warning( disable : 4996)  // function was declared deprecated(strcpy, localtime, etc.)
    +#endif
    +
    +namespace orgQhull {
    +
    +#//!\name Conversions
    +
    +// See qt-qhull.cpp for QList conversions
    +
    +#ifndef QHULL_NO_STL
    +std::vector QhullFacetSet::
    +toStdVector() const
    +{
    +    QhullSetIterator i(*this);
    +    std::vector vs;
    +    while(i.hasNext()){
    +        QhullFacet f= i.next();
    +        if(isSelectAll() || f.isGood()){
    +            vs.push_back(f);
    +        }
    +    }
    +    return vs;
    +}//toStdVector
    +#endif //QHULL_NO_STL
    +
    +#//!\name GetSet
    +
    +bool QhullFacetSet::
    +contains(const QhullFacet &facet) const
    +{
    +    if(isSelectAll()){
    +        return QhullSet::contains(facet);
    +    }
    +    for(QhullFacetSet::const_iterator i=begin(); i != end(); ++i){
    +        QhullFacet f= *i;
    +        if(f==facet && f.isGood()){
    +            return true;
    +        }
    +    }
    +    return false;
    +}//contains
    +
    +int QhullFacetSet::
    +count() const
    +{
    +    if(isSelectAll()){
    +        return QhullSet::count();
    +    }
    +    int counter= 0;
    +    for(QhullFacetSet::const_iterator i=begin(); i != end(); ++i){
    +        QhullFacet f= *i;
    +        if(f.isGood()){
    +            counter++;
    +        }
    +    }
    +    return counter;
    +}//count
    +
    +int QhullFacetSet::
    +count(const QhullFacet &facet) const
    +{
    +    if(isSelectAll()){
    +        return QhullSet::count(facet);
    +    }
    +    int counter= 0;
    +    for(QhullFacetSet::const_iterator i=begin(); i != end(); ++i){
    +        QhullFacet f= *i;
    +        if(f==facet && f.isGood()){
    +            counter++;
    +        }
    +    }
    +    return counter;
    +}//count
    +
    +}//namespace orgQhull
    +
    +#//!\name Global functions
    +
    +using std::endl;
    +using std::ostream;
    +using orgQhull::QhullFacet;
    +using orgQhull::QhullFacetSet;
    +
    +ostream &
    +operator<<(ostream &os, const QhullFacetSet &fs)
    +{
    +    os << fs.print("");
    +    return os;
    +}//<begin(); i!=p.facet_set->end(); ++i){
    +        const QhullFacet f= *i;
    +        if(f.getFacetT()==qh_MERGEridge){
    +            os << " MERGE";
    +        }else if(f.getFacetT()==qh_DUPLICATEridge){
    +            os << " DUP";
    +        }else if(p.facet_set->isSelectAll() || f.isGood()){
    +            os << " f" << f.id();
    +        }
    +    }
    +    os << endl;
    +    return os;
    +}//<
    +
    +namespace orgQhull {
    +
    +#//!\name Used here
    +    class Qhull;
    +
    +#//!\name Defined here
    +    //! QhullFacetSet -- a set of Qhull facets, as a C++ class.  See QhullFacetList.h
    +    class QhullFacetSet;
    +    typedef QhullSetIterator QhullFacetSetIterator;
    +
    +class QhullFacetSet : public QhullSet {
    +
    +#//!\name Defined here
    +public:
    +    typedef facetT *   base_type;  // for QhullVertexSet
    +
    +private:
    +#//!\name Fields
    +    bool                select_all;   //! True if include bad facets.  Default is false.
    +
    +public:
    +#//!\name Constructor
    +                        //Conversion from setT* is not type-safe.  Implicit conversion for void* to T
    +                        QhullFacetSet(const Qhull &q, setT *s) : QhullSet(q, s), select_all(false) {}
    +                        QhullFacetSet(QhullQh *qqh, setT *s) : QhullSet(qqh, s), select_all(false) {}
    +                        //!Copy constructor copies pointers but not contents.  Needed for return by value and parameter passing.
    +                        QhullFacetSet(const QhullFacetSet &other) : QhullSet(other), select_all(other.select_all) {}
    +                        //!Assignment copies pointers but not contents.
    +    QhullFacetSet &     operator=(const QhullFacetSet &other) { QhullSet::operator=(other); select_all= other.select_all; return *this; }
    +
    +private:
    +                        //!Disable default constructor.  See QhullSetBase
    +                        QhullFacetSet();
    +public:
    +
    +#//!\name Conversion
    +#ifndef QHULL_NO_STL
    +    std::vector toStdVector() const;
    +#endif //QHULL_NO_STL
    +#ifdef QHULL_USES_QT
    +    QList   toQList() const;
    +#endif //QHULL_USES_QT
    +
    +#//!\name GetSet
    +                        //! Filtered by facet.isGood().  May be 0 when !isEmpty().
    +    countT              count() const;
    +    bool                contains(const QhullFacet &f) const;
    +    countT              count(const QhullFacet &f) const;
    +    bool                isSelectAll() const { return select_all; }
    +                        //! operator==() does not depend on isGood()
    +    void                selectAll() { select_all= true; }
    +    void                selectGood() { select_all= false; }
    +
    +#//!\name IO
    +    // Not same as QhullFacetList#IO.  A QhullFacetSet is a component of a QhullFacetList.
    +
    +    struct PrintFacetSet{
    +        const QhullFacetSet *facet_set;
    +        const char *    print_message;  //!< non-null message
    +                        PrintFacetSet(const char *message, const QhullFacetSet *s) : facet_set(s), print_message(message) {}
    +    };//PrintFacetSet
    +    const PrintFacetSet print(const char *message) const { return PrintFacetSet(message, this); }
    +
    +    struct PrintIdentifiers{
    +        const QhullFacetSet *facet_set;
    +        const char *    print_message;  //!< non-null message
    +                        PrintIdentifiers(const char *message, const QhullFacetSet *s) : facet_set(s), print_message(message) {}
    +    };//PrintIdentifiers
    +    PrintIdentifiers    printIdentifiers(const char *message) const { return PrintIdentifiers(message, this); }
    +
    +};//class QhullFacetSet
    +
    +}//namespace orgQhull
    +
    +#//!\name == Global namespace =========================================
    +
    +std::ostream &operator<<(std::ostream &os, const orgQhull::QhullFacetSet &fs);
    +std::ostream &operator<<(std::ostream &os, const orgQhull::QhullFacetSet::PrintFacetSet &pr);
    +std::ostream &operator<<(std::ostream &os, const orgQhull::QhullFacetSet::PrintIdentifiers &p);
    +
    +#endif // QHULLFACETSET_H
    diff --git a/xs/src/qhull/src/libqhullcpp/QhullHyperplane.cpp b/xs/src/qhull/src/libqhullcpp/QhullHyperplane.cpp
    new file mode 100644
    index 000000000..ed5cc4bae
    --- /dev/null
    +++ b/xs/src/qhull/src/libqhullcpp/QhullHyperplane.cpp
    @@ -0,0 +1,187 @@
    +/****************************************************************************
    +**
    +** Copyright (c) 2009-2015 C.B. Barber. All rights reserved.
    +** $Id: //main/2015/qhull/src/libqhullcpp/QhullHyperplane.cpp#3 $$Change: 2066 $
    +** $DateTime: 2016/01/18 19:29:17 $$Author: bbarber $
    +**
    +****************************************************************************/
    +
    +#include "libqhullcpp/QhullHyperplane.h"
    +
    +#include "libqhullcpp/Qhull.h"
    +#include "libqhullcpp/QhullPoint.h"
    +
    +#include 
    +
    +
    +#ifdef _MSC_VER  // Microsoft Visual C++ -- warning level 4
    +#endif
    +
    +namespace orgQhull {
    +
    +#//!\name Constructors
    +
    +QhullHyperplane::
    +QhullHyperplane(const Qhull &q) 
    +: hyperplane_coordinates(0)
    +, qh_qh(q.qh())
    +, hyperplane_offset(0.0)
    +, hyperplane_dimension(0)
    +{
    +}
    +
    +QhullHyperplane::
    +QhullHyperplane(const Qhull &q, int hyperplaneDimension, coordT *c, coordT hyperplaneOffset) 
    +: hyperplane_coordinates(c)
    +, qh_qh(q.qh())
    +, hyperplane_offset(hyperplaneOffset)
    +, hyperplane_dimension(hyperplaneDimension)
    +{
    +}
    +
    +#//!\name Conversions
    +
    +// See qt-qhull.cpp for QList conversions
    +
    +#ifndef QHULL_NO_STL
    +std::vector QhullHyperplane::
    +toStdVector() const
    +{
    +    QhullHyperplaneIterator i(*this);
    +    std::vector fs;
    +    while(i.hasNext()){
    +        fs.push_back(i.next());
    +    }
    +    fs.push_back(hyperplane_offset);
    +    return fs;
    +}//toStdVector
    +#endif //QHULL_NO_STL
    +
    +#//!\name GetSet
    +
    +//! Return true if equal
    +//! If qh_qh defined, tests qh.distanceEpsilon and qh.angleEpsilon
    +//! otherwise, tests equal coordinates and offset
    +bool QhullHyperplane::
    +operator==(const QhullHyperplane &other) const
    +{
    +    if(hyperplane_dimension!=other.hyperplane_dimension || !hyperplane_coordinates || !other.hyperplane_coordinates){
    +        return false;
    +    }
    +    double d= fabs(hyperplane_offset-other.hyperplane_offset);
    +    if(d > (qh_qh ? qh_qh->distanceEpsilon() : 0.0)){
    +        return false;
    +    }
    +    double angle= hyperplaneAngle(other);
    +
    +    double a= fabs(angle-1.0);
    +    if(a > (qh_qh ? qh_qh->angleEpsilon() : 0.0)){
    +        return false;
    +    }
    +    return true;
    +}//operator==
    +
    +#//!\name Methods
    +
    +//! Return distance from point to hyperplane.
    +//!   If greater than zero, the point is above the facet (i.e., outside).
    +// qh_distplane [geom_r.c], QhullFacet::distance, and QhullHyperplane::distance are copies
    +//    Does not support RANDOMdist or logging
    +double QhullHyperplane::
    +distance(const QhullPoint &p) const
    +{
    +    const coordT *point= p.coordinates();
    +    int dim= p.dimension();
    +    QHULL_ASSERT(dim==dimension());
    +    const coordT *normal= coordinates();
    +    double dist;
    +
    +    switch (dim){
    +  case 2:
    +      dist= offset() + point[0] * normal[0] + point[1] * normal[1];
    +      break;
    +  case 3:
    +      dist= offset() + point[0] * normal[0] + point[1] * normal[1] + point[2] * normal[2];
    +      break;
    +  case 4:
    +      dist= offset()+point[0]*normal[0]+point[1]*normal[1]+point[2]*normal[2]+point[3]*normal[3];
    +      break;
    +  case 5:
    +      dist= 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= 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= 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= 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= offset();
    +      for (int k=dim; k--; )
    +          dist += *point++ * *normal++;
    +      break;
    +    }
    +    return dist;
    +}//distance
    +
    +double QhullHyperplane::
    +hyperplaneAngle(const QhullHyperplane &other) const
    +{
    +    volatile realT result= 0.0;
    +    QH_TRY_(qh_qh){ // no object creation -- destructors skipped on longjmp()
    +        result= qh_getangle(qh_qh, hyperplane_coordinates, other.hyperplane_coordinates);
    +    }
    +    qh_qh->NOerrexit= true;
    +    qh_qh->maybeThrowQhullMessage(QH_TRY_status);
    +    return result;
    +}//hyperplaneAngle
    +
    +double QhullHyperplane::
    +norm() const {
    +    double d= 0.0;
    +    const coordT *c= coordinates();
    +    for (int k=dimension(); k--; ){
    +        d += *c * *c;
    +        ++c;
    +    }
    +    return sqrt(d);
    +}//norm
    +
    +}//namespace orgQhull
    +
    +#//!\name Global functions
    +
    +using std::ostream;
    +using orgQhull::QhullHyperplane;
    +
    +#//!\name GetSet<<
    +
    +ostream &
    +operator<<(ostream &os, const QhullHyperplane &p)
    +{
    +    os << p.print("");
    +    return os;
    +}
    +
    +ostream &
    +operator<<(ostream &os, const QhullHyperplane::PrintHyperplane &pr)
    +{
    +    os << pr.print_message;
    +    QhullHyperplane p= *pr.hyperplane;
    +    const realT *c= p.coordinates();
    +    for(int k=p.dimension(); k--; ){
    +        realT r= *c++;
    +        if(pr.print_message){
    +            os << " " << r; // FIXUP QH11010 %8.4g
    +        }else{
    +            os << " " << r; // FIXUP QH11010 qh_REAL_1
    +        }
    +    }
    +    os << pr.hyperplane_offset_message << " " << p.offset();
    +    os << std::endl;
    +    return os;
    +}//PrintHyperplane
    +
    diff --git a/xs/src/qhull/src/libqhullcpp/QhullHyperplane.h b/xs/src/qhull/src/libqhullcpp/QhullHyperplane.h
    new file mode 100644
    index 000000000..2868ce5c9
    --- /dev/null
    +++ b/xs/src/qhull/src/libqhullcpp/QhullHyperplane.h
    @@ -0,0 +1,123 @@
    +/****************************************************************************
    +**
    +** Copyright (c) 2009-2015 C.B. Barber. All rights reserved.
    +** $Id: //main/2015/qhull/src/libqhullcpp/QhullHyperplane.h#4 $$Change: 2079 $
    +** $DateTime: 2016/02/07 17:43:34 $$Author: bbarber $
    +**
    +****************************************************************************/
    +
    +#ifndef QHHYPERPLANE_H
    +#define QHHYPERPLANE_H
    +
    +#include "libqhull_r/qhull_ra.h"
    +#include "libqhullcpp/QhullError.h"
    +#include "libqhullcpp/QhullIterator.h"
    +#include "libqhullcpp/QhullQh.h"
    +
    +#include 
    +
    +namespace orgQhull {
    +
    +#//!\name Used here
    +    class Qhull;
    +    class QhullPoint;
    +
    +#//!\name Defined here
    +    //! QhullHyperplane as an offset, dimension, and pointer to coordinates
    +    class QhullHyperplane;
    +    //! Java-style iterator for QhullHyperplane coordinates
    +    class QhullHyperplaneIterator;
    +
    +class QhullHyperplane { // Similar to QhullPoint
    +public:
    +#//!\name Subtypes
    +    typedef const coordT *                  iterator;
    +    typedef const coordT *                  const_iterator;
    +    typedef QhullHyperplane::iterator       Iterator;
    +    typedef QhullHyperplane::const_iterator ConstIterator;
    +
    +private:
    +#//!\name Fields
    +    coordT *            hyperplane_coordinates;  //!< Normal to hyperplane.   facetT.normal is normalized to 1.0
    +    QhullQh *           qh_qh;                  //!< qhT for distanceEpsilon() in operator==
    +    coordT              hyperplane_offset;      //!< Distance from hyperplane to origin
    +    int                 hyperplane_dimension;   //!< Dimension of hyperplane
    +
    +#//!\name Construct
    +public:
    +                        QhullHyperplane() : hyperplane_coordinates(0), qh_qh(0), hyperplane_offset(0.0), hyperplane_dimension(0) {}
    +    explicit            QhullHyperplane(const Qhull &q);
    +                        QhullHyperplane(const Qhull &q, int hyperplaneDimension, coordT *c, coordT hyperplaneOffset);
    +    explicit            QhullHyperplane(QhullQh *qqh) : hyperplane_coordinates(0), qh_qh(qqh), hyperplane_offset(0.0), hyperplane_dimension(0) {}
    +                        QhullHyperplane(QhullQh *qqh, int hyperplaneDimension, coordT *c, coordT hyperplaneOffset) : hyperplane_coordinates(c), qh_qh(qqh), hyperplane_offset(hyperplaneOffset), hyperplane_dimension(hyperplaneDimension) {}
    +                        // Creates an alias.  Does not copy the hyperplane's coordinates.  Needed for return by value and parameter passing.
    +                        QhullHyperplane(const QhullHyperplane &other)  : hyperplane_coordinates(other.hyperplane_coordinates), qh_qh(other.qh_qh), hyperplane_offset(other.hyperplane_offset), hyperplane_dimension(other.hyperplane_dimension) {}
    +                        // Creates an alias.  Does not copy the hyperplane's coordinates.  Needed for vector
    +    QhullHyperplane &   operator=(const QhullHyperplane &other) { hyperplane_coordinates= other.hyperplane_coordinates; qh_qh= other.qh_qh; hyperplane_offset= other.hyperplane_offset; hyperplane_dimension= other.hyperplane_dimension; return *this; }
    +                        ~QhullHyperplane() {}
    +
    +#//!\name Conversions --
    +//! Includes offset at end
    +#ifndef QHULL_NO_STL
    +    std::vector toStdVector() const;
    +#endif //QHULL_NO_STL
    +#ifdef QHULL_USES_QT
    +    QList       toQList() const;
    +#endif //QHULL_USES_QT
    +
    +#//!\name GetSet
    +public:
    +    const coordT *      coordinates() const { return hyperplane_coordinates; }
    +    coordT *            coordinates() { return hyperplane_coordinates; }
    +    void                defineAs(int hyperplaneDimension, coordT *c, coordT hyperplaneOffset) { QHULL_ASSERT(hyperplaneDimension>=0); hyperplane_coordinates= c; hyperplane_dimension= hyperplaneDimension; hyperplane_offset= hyperplaneOffset; }
    +    //! Creates an alias to other using the same qh_qh
    +    void                defineAs(QhullHyperplane &other) { hyperplane_coordinates= other.coordinates(); hyperplane_dimension= other.dimension();  hyperplane_offset= other.offset(); }
    +    int                 dimension() const { return hyperplane_dimension; }
    +    bool                isValid() const { return hyperplane_coordinates!=0 && hyperplane_dimension>0; }
    +    coordT              offset() const { return hyperplane_offset; }
    +    bool                operator==(const QhullHyperplane &other) const;
    +    bool                operator!=(const QhullHyperplane &other) const { return !operator==(other); }
    +    const coordT &      operator[](int idx) const { QHULL_ASSERT(idx>=0 && idx=0 && idx
    +#include 
    +#include 
    +#include 
    +
    +namespace orgQhull {
    +
    +#//!\name Defined here
    +    //! Only QHULL_DECLARE_SEQUENTIAL_ITERATOR is used in libqhullcpp.  The others need further development
    +    //! QHULL_DECLARE_SEQUENTIAL_ITERATOR(C) -- Declare a Java-style iterator
    +    //! QHULL_DECLARE_MUTABLE_SEQUENTIAL_ITERATOR(C) -- Declare a mutable Java-style iterator
    +    //! QHULL_DECLARE_SET_ITERATOR(C) -- Declare a set iterator
    +    //! QHULL_DECLARE_MUTABLE_SET_ITERATOR(C) -- Declare a mutable set iterator
    +    //! Derived from Qt/core/tools/qiterator.h and qset_r.h/FOREACHsetelement_()
    +
    +// Stores C* as done in Mutable...  Assumes the container is not deleted.
    +// C::const_iterator is an STL-style iterator that returns T&
    +#define QHULL_DECLARE_SEQUENTIAL_ITERATOR(C, T) \
    +    \
    +    class C##Iterator \
    +    { \
    +        typedef C::const_iterator const_iterator; \
    +        const C *c; \
    +        const_iterator i; \
    +        public: \
    +        inline C##Iterator(const C &container) \
    +        : c(&container), i(c->constBegin()) {} \
    +        inline C##Iterator &operator=(const C &container) \
    +        { c = &container; i = c->constBegin(); return *this; } \
    +        inline void toFront() { i = c->constBegin(); } \
    +        inline void toBack() { i = c->constEnd(); } \
    +        inline bool hasNext() const { return i != c->constEnd(); } \
    +        inline const T &next() { return *i++; } \
    +        inline const T &peekNext() const { return *i; } \
    +        inline bool hasPrevious() const { return i != c->constBegin(); } \
    +        inline const T &previous() { return *--i; } \
    +        inline const T &peekPrevious() const { const_iterator p = i; return *--p; } \
    +        inline bool findNext(const T &t) \
    +        { while (i != c->constEnd()) if (*i++ == t) return true; return false; } \
    +        inline bool findPrevious(const T &t) \
    +        { while (i != c->constBegin()) if (*(--i) == t) return true; \
    +        return false;  } \
    +    };//C##Iterator
    +
    +// Remove setShareable() from Q_DECLARE_MUTABLE_SEQUENTIAL_ITERATOR
    +// Uses QHULL_ASSERT (assert.h)
    +// Duplicated in MutablePointIterator without insert or remove
    +// Not used in libqhullcpp.  See Coordinates.h
    +#define QHULL_DECLARE_MUTABLE_SEQUENTIAL_ITERATOR(C, T) \
    +    class Mutable##C##Iterator \
    +    { \
    +        typedef C::iterator iterator; \
    +        typedef C::const_iterator const_iterator; \
    +        C *c; \
    +        iterator i, n; \
    +        inline bool item_exists() const { return const_iterator(n) != c->constEnd(); } \
    +        public: \
    +        inline Mutable##C##Iterator(C &container) \
    +        : c(&container) \
    +        { i = c->begin(); n = c->end(); } \
    +        inline ~Mutable##C##Iterator() \
    +        {} \
    +        inline Mutable##C##Iterator &operator=(C &container) \
    +        { c = &container; \
    +        i = c->begin(); n = c->end(); return *this; } \
    +        inline void toFront() { i = c->begin(); n = c->end(); } \
    +        inline void toBack() { i = c->end(); n = i; } \
    +        inline bool hasNext() const { return c->constEnd() != const_iterator(i); } \
    +        inline T &next() { n = i++; return *n; } \
    +        inline T &peekNext() const { return *i; } \
    +        inline bool hasPrevious() const { return c->constBegin() != const_iterator(i); } \
    +        inline T &previous() { n = --i; return *n; } \
    +        inline T &peekPrevious() const { iterator p = i; return *--p; } \
    +        inline void remove() \
    +        { if (c->constEnd() != const_iterator(n)) { i = c->erase(n); n = c->end(); } } \
    +        inline void setValue(const T &t) const { if (c->constEnd() != const_iterator(n)) *n = t; } \
    +        inline T &value() { QHULL_ASSERT(item_exists()); return *n; } \
    +        inline const T &value() const { QHULL_ASSERT(item_exists()); return *n; } \
    +        inline void insert(const T &t) { n = i = c->insert(i, t); ++i; } \
    +        inline bool findNext(const T &t) \
    +        { while (c->constEnd() != const_iterator(n = i)) if (*i++ == t) return true; return false; } \
    +        inline bool findPrevious(const T &t) \
    +        { while (c->constBegin() != const_iterator(i)) if (*(n = --i) == t) return true; \
    +        n = c->end(); return false;  } \
    +    };//Mutable##C##Iterator
    +
    +// Not used in libqhullcpp.
    +#define QHULL_DECLARE_SET_ITERATOR(C) \
    +\
    +    template  \
    +    class Qhull##C##Iterator \
    +    { \
    +        typedef typename Qhull##C::const_iterator const_iterator; \
    +        Qhull##C c; \
    +        const_iterator i; \
    +    public: \
    +        inline Qhull##C##Iterator(const Qhull##C &container) \
    +        : c(container), i(c.constBegin()) {} \
    +        inline Qhull##C##Iterator &operator=(const Qhull##C &container) \
    +        { c = container; i = c.constBegin(); return *this; } \
    +        inline void toFront() { i = c.constBegin(); } \
    +        inline void toBack() { i = c.constEnd(); } \
    +        inline bool hasNext() const { return i != c.constEnd(); } \
    +        inline const T &next() { return *i++; } \
    +        inline const T &peekNext() const { return *i; } \
    +        inline bool hasPrevious() const { return i != c.constBegin(); } \
    +        inline const T &previous() { return *--i; } \
    +        inline const T &peekPrevious() const { const_iterator p = i; return *--p; } \
    +        inline bool findNext(const T &t) \
    +        { while (i != c.constEnd()) if (*i++ == t) return true; return false; } \
    +        inline bool findPrevious(const T &t) \
    +        { while (i != c.constBegin()) if (*(--i) == t) return true; \
    +        return false;  } \
    +    };//Qhull##C##Iterator
    +
    +// Not used in libqhullcpp.
    +#define QHULL_DECLARE_MUTABLE_SET_ITERATOR(C) \
    +\
    +template  \
    +class QhullMutable##C##Iterator \
    +{ \
    +    typedef typename Qhull##C::iterator iterator; \
    +    typedef typename Qhull##C::const_iterator const_iterator; \
    +    Qhull##C *c; \
    +    iterator i, n; \
    +    inline bool item_exists() const { return const_iterator(n) != c->constEnd(); } \
    +public: \
    +    inline Mutable##C##Iterator(Qhull##C &container) \
    +        : c(&container) \
    +    { c->setSharable(false); i = c->begin(); n = c->end(); } \
    +    inline ~Mutable##C##Iterator() \
    +    { c->setSharable(true); } \
    +    inline Mutable##C##Iterator &operator=(Qhull##C &container) \
    +    { c->setSharable(true); c = &container; c->setSharable(false); \
    +      i = c->begin(); n = c->end(); return *this; } \
    +    inline void toFront() { i = c->begin(); n = c->end(); } \
    +    inline void toBack() { i = c->end(); n = i; } \
    +    inline bool hasNext() const { return c->constEnd() != const_iterator(i); } \
    +    inline T &next() { n = i++; return *n; } \
    +    inline T &peekNext() const { return *i; } \
    +    inline bool hasPrevious() const { return c->constBegin() != const_iterator(i); } \
    +    inline T &previous() { n = --i; return *n; } \
    +    inline T &peekPrevious() const { iterator p = i; return *--p; } \
    +    inline void remove() \
    +    { if (c->constEnd() != const_iterator(n)) { i = c->erase(n); n = c->end(); } } \
    +    inline void setValue(const T &t) const { if (c->constEnd() != const_iterator(n)) *n = t; } \
    +    inline T &value() { Q_ASSERT(item_exists()); return *n; } \
    +    inline const T &value() const { Q_ASSERT(item_exists()); return *n; } \
    +    inline void insert(const T &t) { n = i = c->insert(i, t); ++i; } \
    +    inline bool findNext(const T &t) \
    +    { while (c->constEnd() != const_iterator(n = i)) if (*i++ == t) return true; return false; } \
    +    inline bool findPrevious(const T &t) \
    +    { while (c->constBegin() != const_iterator(i)) if (*(n = --i) == t) return true; \
    +      n = c->end(); return false;  } \
    +};//QhullMutable##C##Iterator
    +
    +}//namespace orgQhull
    +
    +#endif // QHULLITERATOR_H
    +
    diff --git a/xs/src/qhull/src/libqhullcpp/QhullLinkedList.h b/xs/src/qhull/src/libqhullcpp/QhullLinkedList.h
    new file mode 100644
    index 000000000..d4caf52c1
    --- /dev/null
    +++ b/xs/src/qhull/src/libqhullcpp/QhullLinkedList.h
    @@ -0,0 +1,388 @@
    +/****************************************************************************
    +**
    +** Copyright (c) 2008-2015 C.B. Barber. All rights reserved.
    +** $Id: //main/2015/qhull/src/libqhullcpp/QhullLinkedList.h#7 $$Change: 2079 $
    +** $DateTime: 2016/02/07 17:43:34 $$Author: bbarber $
    +**
    +****************************************************************************/
    +
    +#ifndef QHULLLINKEDLIST_H
    +#define QHULLLINKEDLIST_H
    +
    +#include "libqhull_r/qhull_ra.h"
    +#include "libqhullcpp/QhullError.h"
    +
    +#include   // ptrdiff_t, size_t
    +
    +#ifdef QHULL_USES_QT
    +#include 
    +#endif
    +
    +#ifndef QHULL_NO_STL
    +#include 
    +#include 
    +#include 
    +#endif
    +
    +namespace orgQhull {
    +
    +#//!\name Defined here
    +    //! QhullLinkedList -- A linked list modeled on QLinkedList.
    +    //!   T is an opaque type with T(B *b), b=t.getBaseT(), t=t.next(), and t=t.prev().  The end node is a sentinel.
    +    //!   QhullQh/qhT owns the contents.
    +    //!   QhullLinkedList does not define erase(), clear(), removeFirst(), removeLast(), pop_back(), pop_front(), fromStdList()
    +    //!   Derived from Qt/core/tools/qlinkedlist.h and libqhull_r.h/FORALLfacets_()
    +    //! QhullLinkedList::const_iterator -- STL-style iterator
    +    //! QhullLinkedList::iterator -- STL-style iterator
    +    //! QhullLinkedListIterator -- Java-style iterator
    +    //!   Derived from Qt/core/tools/qiterator.h
    +    //!   Works with Qt's foreach keyword [Qt/src/corelib/global/qglobal.h]
    +
    +template 
    +class QhullLinkedList
    +{
    +#//!\name Defined here
    +public:
    +    class const_iterator;
    +    class iterator;
    +    typedef const_iterator  ConstIterator;
    +    typedef iterator    Iterator;
    +    typedef ptrdiff_t   difference_type;
    +    typedef countT      size_type;
    +    typedef T           value_type;
    +    typedef const value_type *const_pointer;
    +    typedef const value_type &const_reference;
    +    typedef value_type *pointer;
    +    typedef value_type &reference;
    +
    +#//!\name Fields
    +private:
    +    T                   begin_node;
    +    T                   end_node;     //! Sentinel node at end of list
    +
    +#//!\name Constructors
    +public:
    +                        QhullLinkedList(T b, T e) : begin_node(b), end_node(e) {}
    +                        //! Copy constructor copies begin_node and end_node, but not the list elements.  Needed for return by value and parameter passing.
    +                        QhullLinkedList(const QhullLinkedList &other) : begin_node(other.begin_node), end_node(other.end_node) {}
    +                        //! Copy assignment copies begin_node and end_node, but not the list elements.
    +                        QhullLinkedList & operator=(const QhullLinkedList &other) { begin_node= other.begin_node; end_node= other.end_node; return *this; }
    +                        ~QhullLinkedList() {}
    +
    +private:
    +                        //!disabled since a sentinel must be allocated as the private type
    +                        QhullLinkedList() {}
    +
    +public:
    +
    +#//!\name Conversions
    +#ifndef QHULL_NO_STL
    +    std::vector      toStdVector() const;
    +#endif
    +#ifdef QHULL_USES_QT
    +    QList            toQList() const;
    +#endif
    +
    +#//!\name GetSet
    +    countT              count() const;
    +                        //count(t) under #//!\name Search
    +    bool                isEmpty() const { return (begin_node==end_node); }
    +    bool                operator==(const QhullLinkedList &o) const;
    +    bool                operator!=(const QhullLinkedList &o) const { return !operator==(o); }
    +    size_t              size() const { return count(); }
    +
    +#//!\name Element access
    +    //! For back() and last(), return T instead of T& (T is computed)
    +    const T             back() const { return last(); }
    +    T                   back() { return last(); }
    +    const T &           first() const { QHULL_ASSERT(!isEmpty()); return begin_node; }
    +    T &                 first() { QHULL_ASSERT(!isEmpty()); return begin_node; }
    +    const T &           front() const { return first(); }
    +    T &                 front() { return first(); }
    +    const T             last() const { QHULL_ASSERT(!isEmpty()); return *--end(); }
    +    T                   last() { QHULL_ASSERT(!isEmpty()); return *--end(); }
    +
    +#//!\name Modify -- Allocation of opaque types not implemented.
    +
    +#//!\name Search
    +    bool                contains(const T &t) const;
    +    countT              count(const T &t) const;
    +
    +#//!\name Iterator
    +    iterator            begin() { return begin_node; }
    +    const_iterator      begin() const { return begin_node; }
    +    const_iterator      constBegin() const { return begin_node; }
    +    const_iterator      constEnd() const { return end_node; }
    +    iterator            end() { return end_node; }
    +    const_iterator      end() const { return end_node; }
    +
    +    class iterator {
    +
    +    private:
    +        T               i;
    +        friend class const_iterator;
    +
    +    public:
    +        typedef std::bidirectional_iterator_tag  iterator_category;
    +        typedef T           value_type;
    +        typedef value_type *pointer;
    +        typedef value_type &reference;
    +        typedef ptrdiff_t   difference_type;
    +
    +                        iterator() : i() {}
    +                        iterator(const T &t) : i(t) {}  //!< Automatic conversion to iterator
    +                        iterator(const iterator &o) : i(o.i) {}
    +        iterator &      operator=(const iterator &o) { i= o.i; return *this; }
    +
    +        const T &       operator*() const { return i; }
    +        T &             operator*() { return i; }
    +        // Do not define operator[]
    +        const T *       operator->() const { return &i; }
    +        T *             operator->() { return &i; }
    +        bool            operator==(const iterator &o) const { return i == o.i; }
    +        bool            operator!=(const iterator &o) const { return !operator==(o); }
    +        bool            operator==(const const_iterator &o) const { return i==reinterpret_cast(o).i; }
    +        bool            operator!=(const const_iterator &o) const { return !operator==(o); }
    +        iterator &      operator++() { i= i.next(); return *this; }
    +        iterator        operator++(int) { iterator o= i; i= i.next(); return o; }
    +        iterator &      operator--() { i= i.previous(); return *this; }
    +        iterator        operator--(int) { iterator o= i; i= i.previous(); return o; }
    +        iterator        operator+(int j) const;
    +        iterator        operator-(int j) const { return operator+(-j); }
    +        iterator &      operator+=(int j) { return (*this= *this + j); }
    +        iterator &      operator-=(int j) { return (*this= *this - j); }
    +    };//QhullLinkedList::iterator
    +
    +    class const_iterator {
    +
    +    private:
    +        T               i;
    +
    +    public:
    +        typedef std::bidirectional_iterator_tag  iterator_category;
    +        typedef T                 value_type;
    +        typedef const value_type *pointer;
    +        typedef const value_type &reference;
    +        typedef ptrdiff_t         difference_type;
    +
    +                        const_iterator() : i() {}
    +                        const_iterator(const T &t) : i(t) {}  //!< Automatic conversion to const_iterator
    +                        const_iterator(const iterator &o) : i(o.i) {}
    +                        const_iterator(const const_iterator &o) : i(o.i) {}
    +        const_iterator &operator=(const const_iterator &o) { i= o.i; return *this; }
    +
    +        const T &       operator*() const { return i; }
    +        const T *       operator->() const { return i; }
    +        bool            operator==(const const_iterator &o) const { return i == o.i; }
    +        bool            operator!=(const const_iterator &o) const { return !operator==(o); }
    +                        // No comparisons or iterator diff
    +        const_iterator &operator++() { i= i.next(); return *this; }
    +        const_iterator  operator++(int) { const_iterator o= i; i= i.next(); return o; }
    +        const_iterator &operator--() { i= i.previous(); return *this; }
    +        const_iterator  operator--(int) { const_iterator o= i; i= i.previous(); return o; }
    +        const_iterator  operator+(int j) const;
    +        const_iterator  operator-(int j) const { return operator+(-j); }
    +        const_iterator &operator+=(int j) { return (*this= *this + j); }
    +        const_iterator &operator-=(int j) { return (*this= *this - j); }
    +    };//QhullLinkedList::const_iterator
    +
    +};//QhullLinkedList
    +
    +template 
    +class QhullLinkedListIterator // FIXUP QH11016 define QhullMutableLinkedListIterator
    +{
    +    typedef typename QhullLinkedList::const_iterator const_iterator;
    +    const QhullLinkedList *c;
    +    const_iterator      i;
    +
    +public:
    +                        QhullLinkedListIterator(const QhullLinkedList &container) : c(&container), i(c->constBegin()) {}
    +    QhullLinkedListIterator & operator=(const QhullLinkedList &container) { c= &container; i= c->constBegin(); return *this; }
    +    bool                findNext(const T &t);
    +    bool                findPrevious(const T &t);
    +    bool                hasNext() const { return i != c->constEnd(); }
    +    bool                hasPrevious() const { return i != c->constBegin(); }
    +    T                   next() { return *i++; }
    +    T                   peekNext() const { return *i; }
    +    T                   peekPrevious() const { const_iterator p= i; return *--p; }
    +    T                   previous() { return *--i; }
    +    void                toFront() { i= c->constBegin(); }
    +    void                toBack() { i= c->constEnd(); }
    +};//QhullLinkedListIterator
    +
    +#//!\name == Definitions =========================================
    +
    +#//!\name Conversion
    +
    +#ifndef QHULL_NO_STL
    +template 
    +std::vector QhullLinkedList::
    +toStdVector() const
    +{
    +    std::vector tmp;
    +    std::copy(constBegin(), constEnd(), std::back_inserter(tmp));
    +    return tmp;
    +}//toStdVector
    +#endif
    +
    +#ifdef QHULL_USES_QT
    +template 
    +QList  QhullLinkedList::
    +toQList() const
    +{
    +    QhullLinkedListIterator i(*this);
    +    QList ls;
    +    while(i.hasNext()){
    +        ls.append(i.next());
    +    }
    +    return ls;
    +}//toQList
    +#endif
    +
    +#//!\name GetSet
    +
    +template 
    +countT QhullLinkedList::
    +count() const
    +{
    +    const_iterator i= begin_node;
    +    countT c= 0;
    +    while(i != end_node){
    +        c++;
    +        i++;
    +    }
    +    return c;
    +}//count
    +
    +#//!\name Search
    +
    +template 
    +bool QhullLinkedList::
    +contains(const T &t) const
    +{
    +    const_iterator i= begin_node;
    +    while(i != end_node){
    +        if(i==t){
    +            return true;
    +        }
    +        i++;
    +    }
    +    return false;
    +}//contains
    +
    +template 
    +countT QhullLinkedList::
    +count(const T &t) const
    +{
    +    const_iterator i= begin_node;
    +    countT c= 0;
    +    while(i != end_node){
    +        if(i==t){
    +            c++;
    +        }
    +        i++;
    +    }
    +    return c;
    +}//count
    +
    +template 
    +bool QhullLinkedList::
    +operator==(const QhullLinkedList &l) const
    +{
    +    if(begin_node==l.begin_node){
    +        return (end_node==l.end_node);
    +    }
    +    T i= begin_node;
    +    T il= l.begin_node;
    +    while(i != end_node){
    +        if(i != il){
    +            return false;
    +        }
    +        i= static_cast(i.next());
    +        il= static_cast(il.next());
    +    }
    +    if(il != l.end_node){
    +        return false;
    +    }
    +    return true;
    +}//operator==
    +
    +#//!\name Iterator
    +
    +template 
    +typename QhullLinkedList::iterator  QhullLinkedList::iterator::
    +operator+(int j) const
    +{
    +    T n= i;
    +    if(j>0){
    +        while(j--){
    +            n= n.next();
    +        }
    +    }else{
    +        while(j++){
    +            n= n.previous();
    +        }
    +    }
    +    return iterator(n);
    +}//operator+
    +
    +template 
    +typename QhullLinkedList::const_iterator  QhullLinkedList::const_iterator::
    +operator+(int j) const
    +{
    +    T n= i;
    +    if(j>0){
    +        while(j--){
    +            n= n.next();
    +        }
    +    }else{
    +        while(j++){
    +            n= n.previous();
    +        }
    +    }
    +    return const_iterator(n);
    +}//operator+
    +
    +#//!\name QhullLinkedListIterator
    +
    +template 
    +bool QhullLinkedListIterator::
    +findNext(const T &t)
    +{
    +    while(i != c->constEnd()){
    +        if (*i++ == t){
    +            return true;
    +        }
    +    }
    +    return false;
    +}//findNext
    +
    +template 
    +bool QhullLinkedListIterator::
    +findPrevious(const T &t)
    +{
    +    while(i!=c->constBegin()){
    +        if(*(--i)==t){
    +            return true;
    +        }
    +    }
    +    return false;
    +}//findNext
    +
    +}//namespace orgQhull
    +
    +#//!\name Global
    +
    +template 
    +std::ostream &
    +operator<<(std::ostream &os, const orgQhull::QhullLinkedList &qs)
    +{
    +    typename orgQhull::QhullLinkedList::const_iterator i;
    +    for(i= qs.begin(); i != qs.end(); ++i){
    +        os << *i;
    +    }
    +    return os;
    +}//operator<<
    +
    +#endif // QHULLLINKEDLIST_H
    +
    diff --git a/xs/src/qhull/src/libqhullcpp/QhullPoint.cpp b/xs/src/qhull/src/libqhullcpp/QhullPoint.cpp
    new file mode 100644
    index 000000000..f5e912460
    --- /dev/null
    +++ b/xs/src/qhull/src/libqhullcpp/QhullPoint.cpp
    @@ -0,0 +1,203 @@
    +/****************************************************************************
    +**
    +** Copyright (c) 2009-2015 C.B. Barber. All rights reserved.
    +** $Id: //main/2015/qhull/src/libqhullcpp/QhullPoint.cpp#3 $$Change: 2066 $
    +** $DateTime: 2016/01/18 19:29:17 $$Author: bbarber $
    +**
    +****************************************************************************/
    +
    +#include "libqhullcpp/QhullPoint.h"
    +
    +#include "libqhullcpp/QhullError.h"
    +#include "libqhullcpp/Qhull.h"
    +
    +#include 
    +#include 
    +
    +#ifdef _MSC_VER  // Microsoft Visual C++ -- warning level 4
    +#endif
    +
    +namespace orgQhull {
    +
    +#//!\name Constructors
    +
    +
    +QhullPoint::
    +QhullPoint(const Qhull &q) 
    +: point_coordinates(0)
    +, qh_qh(q.qh())
    +, point_dimension(q.hullDimension())
    +{
    +}//QhullPoint
    +
    +QhullPoint::
    +QhullPoint(const Qhull &q, coordT *c) 
    +: point_coordinates(c)
    +, qh_qh(q.qh())
    +, point_dimension(q.hullDimension())
    +{
    +    QHULL_ASSERT(q.hullDimension()>0);
    +}//QhullPoint dim, coordT
    +
    +QhullPoint::
    +QhullPoint(const Qhull &q, int pointDimension, coordT *c) 
    +: point_coordinates(c)
    +, qh_qh(q.qh())
    +, point_dimension(pointDimension)
    +{
    +}//QhullPoint dim, coordT
    +
    +//! QhullPoint of Coordinates with point_dimension==c.count()
    +QhullPoint::
    +QhullPoint(const Qhull &q, Coordinates &c) 
    +: point_coordinates(c.data())
    +, qh_qh(q.qh())
    +, point_dimension(c.count())
    +{
    +}//QhullPoint Coordinates
    +
    +#//!\name Conversions
    +
    +// See qt-qhull.cpp for QList conversion
    +
    +#ifndef QHULL_NO_STL
    +std::vector QhullPoint::
    +toStdVector() const
    +{
    +    QhullPointIterator i(*this);
    +    std::vector vs;
    +    while(i.hasNext()){
    +        vs.push_back(i.next());
    +    }
    +    return vs;
    +}//toStdVector
    +#endif //QHULL_NO_STL
    +
    +#//!\name GetSet
    +
    +//! QhullPoint is equal if it has the same address and dimension
    +//! If !qh_qh, returns true if dimension and coordinates are equal
    +//! If qh_qh, returns true if the distance between points is less than qh_qh->distanceEpsilon()
    +//!\todo Compares distance with distance-to-hyperplane (distanceEpsilon).   Is that correct?
    +bool QhullPoint::
    +operator==(const QhullPoint &other) const
    +{
    +    if(point_dimension!=other.point_dimension){
    +        return false;
    +    }
    +    const coordT *c= point_coordinates;
    +    const coordT *c2= other.point_coordinates;
    +    if(c==c2){
    +        return true;
    +    }
    +    if(!c || !c2){
    +        return false;
    +    }
    +    if(!qh_qh || qh_qh->hull_dim==0){
    +        for(int k= point_dimension; k--; ){
    +            if(*c++ != *c2++){
    +                return false;
    +            }
    +        }
    +        return true;
    +    }
    +    double dist2= 0.0;
    +    for(int k= point_dimension; k--; ){
    +        double diff= *c++ - *c2++;
    +        dist2 += diff*diff;
    +    }
    +    dist2= sqrt(dist2);
    +    return (dist2 < qh_qh->distanceEpsilon());
    +}//operator==
    +
    +#//!\name Methods
    +
    +//! Return distance between two points.
    +double QhullPoint::
    +distance(const QhullPoint &p) const
    +{
    +    const coordT *c= point_coordinates;
    +    const coordT *c2= p.point_coordinates;
    +    int dim= point_dimension;
    +    if(dim!=p.point_dimension){
    +        throw QhullError(10075, "QhullPoint error: Expecting dimension %d for distance().  Got %d", dim, p.point_dimension);
    +    }
    +    if(!c || !c2){
    +        throw QhullError(10076, "QhullPoint error: Cannot compute distance() for undefined point");
    +    }
    +    double dist;
    +
    +    switch(dim){
    +  case 2:
    +      dist= (c[0]-c2[0])*(c[0]-c2[0]) + (c[1]-c2[1])*(c[1]-c2[1]);
    +      break;
    +  case 3:
    +      dist= (c[0]-c2[0])*(c[0]-c2[0]) + (c[1]-c2[1])*(c[1]-c2[1]) + (c[2]-c2[2])*(c[2]-c2[2]);
    +      break;
    +  case 4:
    +      dist= (c[0]-c2[0])*(c[0]-c2[0]) + (c[1]-c2[1])*(c[1]-c2[1]) + (c[2]-c2[2])*(c[2]-c2[2]) + (c[3]-c2[3])*(c[3]-c2[3]);
    +      break;
    +  case 5:
    +      dist= (c[0]-c2[0])*(c[0]-c2[0]) + (c[1]-c2[1])*(c[1]-c2[1]) + (c[2]-c2[2])*(c[2]-c2[2]) + (c[3]-c2[3])*(c[3]-c2[3]) + (c[4]-c2[4])*(c[4]-c2[4]);
    +      break;
    +  case 6:
    +      dist= (c[0]-c2[0])*(c[0]-c2[0]) + (c[1]-c2[1])*(c[1]-c2[1]) + (c[2]-c2[2])*(c[2]-c2[2]) + (c[3]-c2[3])*(c[3]-c2[3]) + (c[4]-c2[4])*(c[4]-c2[4]) + (c[5]-c2[5])*(c[5]-c2[5]);
    +      break;
    +  case 7:
    +      dist= (c[0]-c2[0])*(c[0]-c2[0]) + (c[1]-c2[1])*(c[1]-c2[1]) + (c[2]-c2[2])*(c[2]-c2[2]) + (c[3]-c2[3])*(c[3]-c2[3]) + (c[4]-c2[4])*(c[4]-c2[4]) + (c[5]-c2[5])*(c[5]-c2[5]) + (c[6]-c2[6])*(c[6]-c2[6]);
    +      break;
    +  case 8:
    +      dist= (c[0]-c2[0])*(c[0]-c2[0]) + (c[1]-c2[1])*(c[1]-c2[1]) + (c[2]-c2[2])*(c[2]-c2[2]) + (c[3]-c2[3])*(c[3]-c2[3]) + (c[4]-c2[4])*(c[4]-c2[4]) + (c[5]-c2[5])*(c[5]-c2[5]) + (c[6]-c2[6])*(c[6]-c2[6]) + (c[7]-c2[7])*(c[7]-c2[7]);
    +      break;
    +  default:
    +      dist= 0.0;
    +      for(int k=dim; k--; ){
    +          dist += (*c - *c2) * (*c - *c2);
    +          ++c;
    +          ++c2;
    +      }
    +      break;
    +    }
    +    return sqrt(dist);
    +}//distance
    +
    +}//namespace orgQhull
    +
    +#//!\name Global functions
    +
    +using std::ostream;
    +using orgQhull::QhullPoint;
    +
    +//! Same as qh_printpointid [io.c]
    +ostream &
    +operator<<(ostream &os, const QhullPoint::PrintPoint &pr)
    +{
    +    QhullPoint p= *pr.point; 
    +    countT i= p.id();
    +    if(pr.point_message){
    +        if(*pr.point_message){
    +            os << pr.point_message << " ";
    +        }
    +        if(pr.with_identifier && (i!=qh_IDunknown) && (i!=qh_IDnone)){
    +            os << "p" << i << ": ";
    +        }
    +    }
    +    const realT *c= p.coordinates();
    +    for(int k=p.dimension(); k--; ){
    +        realT r= *c++;
    +        if(pr.point_message){
    +            os << " " << r; // FIXUP QH11010 %8.4g
    +        }else{
    +            os << " " << r; // FIXUP QH11010 qh_REAL_1
    +        }
    +    }
    +    os << std::endl;
    +    return os;
    +}//printPoint
    +
    +ostream & 
    +operator<<(ostream &os, const QhullPoint &p)
    +{
    +    os << p.print(""); 
    +    return os;
    +}//operator<<
    diff --git a/xs/src/qhull/src/libqhullcpp/QhullPoint.h b/xs/src/qhull/src/libqhullcpp/QhullPoint.h
    new file mode 100644
    index 000000000..17f94ab36
    --- /dev/null
    +++ b/xs/src/qhull/src/libqhullcpp/QhullPoint.h
    @@ -0,0 +1,136 @@
    +/****************************************************************************
    +**
    +** Copyright (c) 2009-2015 C.B. Barber. All rights reserved.
    +** $Id: //main/2015/qhull/src/libqhullcpp/QhullPoint.h#4 $$Change: 2079 $
    +** $DateTime: 2016/02/07 17:43:34 $$Author: bbarber $
    +**
    +****************************************************************************/
    +
    +#ifndef QHPOINT_H
    +#define QHPOINT_H
    +
    +#include "libqhull_r/qhull_ra.h"
    +#include "libqhullcpp/QhullError.h"
    +#include "libqhullcpp/QhullIterator.h"
    +#include "libqhullcpp/QhullQh.h"
    +#include "libqhullcpp/Coordinates.h"
    +
    +#include 
    +
    +namespace orgQhull {
    +
    +#//!\name Defined here
    +    class QhullPoint;  //!<  QhullPoint as a pointer and dimension to shared memory
    +    class QhullPointIterator; //!< Java-style iterator for QhullPoint coordinates
    +
    +#//!\name Used here
    +    class Qhull;
    +
    +//! A QhullPoint is a dimension and an array of coordinates.
    +//! With Qhull/QhullQh, a QhullPoint has an identifier.  Point equality is relative to qh.distanceEpsilon
    +class QhullPoint {
    +
    +#//!\name Iterators
    +public:
    +    typedef coordT *                    base_type;  // for QhullPointSet
    +    typedef const coordT *              iterator;
    +    typedef const coordT *              const_iterator;
    +    typedef QhullPoint::iterator        Iterator;
    +    typedef QhullPoint::const_iterator  ConstIterator;
    +
    +#//!\name Fields
    +protected: // For QhullPoints::iterator, QhullPoints::const_iterator
    +    coordT *            point_coordinates;  //!< Pointer to first coordinate,   0 if undefined
    +    QhullQh *           qh_qh;              //!< qhT for this instance of Qhull.  0 if undefined.
    +                                            //!< operator==() returns true if points within sqrt(qh_qh->distanceEpsilon())
    +                                            //!< If !qh_qh, id() is -3, and operator==() requires equal coordinates
    +    int                 point_dimension;    //!< Default dimension is qh_qh->hull_dim
    +public:
    +
    +#//!\name Constructors
    +    //! QhullPoint, PointCoordinates, and QhullPoints have similar constructors
    +    //! If Qhull/QhullQh is not initialized, then QhullPoint.dimension() is zero unless explicitly set
    +    //! Cannot define QhullPoints(int pointDimension) since it is ambiguous with QhullPoints(QhullQh *qqh)
    +                        QhullPoint() : point_coordinates(0), qh_qh(0), point_dimension(0) {}
    +                        QhullPoint(int pointDimension, coordT *c) : point_coordinates(c), qh_qh(0), point_dimension(pointDimension) { QHULL_ASSERT(pointDimension>0); }
    +    explicit            QhullPoint(const Qhull &q);
    +                        QhullPoint(const Qhull &q, coordT *c);
    +                        QhullPoint(const Qhull &q, Coordinates &c);
    +                        QhullPoint(const Qhull &q, int pointDimension, coordT *c);
    +    explicit            QhullPoint(QhullQh *qqh) : point_coordinates(0), qh_qh(qqh), point_dimension(qqh->hull_dim) {}
    +                        QhullPoint(QhullQh *qqh, coordT *c) : point_coordinates(c), qh_qh(qqh), point_dimension(qqh->hull_dim) { QHULL_ASSERT(qqh->hull_dim>0); }
    +                        QhullPoint(QhullQh *qqh, Coordinates &c) : point_coordinates(c.data()), qh_qh(qqh), point_dimension(c.count()) {}
    +                        QhullPoint(QhullQh *qqh, int pointDimension, coordT *c) : point_coordinates(c), qh_qh(qqh), point_dimension(pointDimension) {}
    +                        //! Creates an alias.  Does not make a deep copy of the point.  Needed for return by value and parameter passing.
    +                        QhullPoint(const QhullPoint &other) : point_coordinates(other.point_coordinates), qh_qh(other.qh_qh), point_dimension(other.point_dimension) {}
    +                        //! Creates an alias.  Does not make a deep copy of the point.  Needed for vector
    +    QhullPoint &        operator=(const QhullPoint &other) { point_coordinates= other.point_coordinates; qh_qh= other.qh_qh; point_dimension= other.point_dimension; return *this; }
    +                        ~QhullPoint() {}
    +
    +
    +#//!\name Conversions
    +
    +#ifndef QHULL_NO_STL
    +    std::vector toStdVector() const;
    +#endif //QHULL_NO_STL
    +#ifdef QHULL_USES_QT
    +    QList       toQList() const;
    +#endif //QHULL_USES_QT
    +
    +#//!\name GetSet
    +public:
    +    const coordT *      coordinates() const { return point_coordinates; }  //!< 0 if undefined
    +    coordT *            coordinates() { return point_coordinates; }        //!< 0 if undefined
    +    void                defineAs(coordT *c) { QHULL_ASSERT(point_dimension>0); point_coordinates= c; }
    +    void                defineAs(int pointDimension, coordT *c) { QHULL_ASSERT(pointDimension>=0); point_coordinates= c; point_dimension= pointDimension; }
    +    void                defineAs(QhullPoint &other) { point_coordinates= other.point_coordinates; qh_qh= other.qh_qh; point_dimension= other.point_dimension; }
    +    int                 dimension() const { return point_dimension; }
    +    coordT *            getBaseT() const { return point_coordinates; } // for QhullPointSet
    +    countT              id() const { return qh_pointid(qh_qh, point_coordinates); } // NOerrors
    +    bool                isValid() const { return (point_coordinates!=0 && point_dimension>0); };
    +    bool                operator==(const QhullPoint &other) const;
    +    bool                operator!=(const QhullPoint &other) const { return ! operator==(other); }
    +    const coordT &      operator[](int idx) const { QHULL_ASSERT(point_coordinates!=0 && idx>=0 && idx=0 && idx
    +#include 
    +
    +#ifdef _MSC_VER  // Microsoft Visual C++ -- warning level 4
    +#endif
    +
    +namespace orgQhull {
    +
    +// Implemented via QhullSet.h
    +
    +}//namespace orgQhull
    +
    +#//!\name Global functions
    +
    +using std::endl;
    +using std::ostream;
    +using orgQhull::QhullPoint;
    +using orgQhull::QhullPointSet;
    +using orgQhull::QhullPointSetIterator;
    +
    +ostream &
    +operator<<(ostream &os, const QhullPointSet::PrintIdentifiers &pr)
    +{
    +    os << pr.print_message;
    +    const QhullPointSet s= *pr.point_set;
    +    QhullPointSetIterator i(s);
    +    while(i.hasNext()){
    +        if(i.hasPrevious()){
    +            os << " ";
    +        }
    +        const QhullPoint point= i.next();
    +        countT id= point.id();
    +        os << "p" << id;
    +
    +    }
    +    os << endl;
    +    return os;
    +}//PrintIdentifiers
    +
    +ostream &
    +operator<<(ostream &os, const QhullPointSet::PrintPointSet &pr)
    +{
    +    os << pr.print_message;
    +    const QhullPointSet s= *pr.point_set;
    +    for(QhullPointSet::const_iterator i=s.begin(); i != s.end(); ++i){
    +        const QhullPoint point= *i;
    +        os << point;
    +    }
    +    return os;
    +}//printPointSet
    +
    +
    diff --git a/xs/src/qhull/src/libqhullcpp/QhullPointSet.h b/xs/src/qhull/src/libqhullcpp/QhullPointSet.h
    new file mode 100644
    index 000000000..8562e170e
    --- /dev/null
    +++ b/xs/src/qhull/src/libqhullcpp/QhullPointSet.h
    @@ -0,0 +1,77 @@
    +/****************************************************************************
    +**
    +** Copyright (c) 2009-2015 C.B. Barber. All rights reserved.
    +** $Id: //main/2015/qhull/src/libqhullcpp/QhullPointSet.h#4 $$Change: 2079 $
    +** $DateTime: 2016/02/07 17:43:34 $$Author: bbarber $
    +**
    +****************************************************************************/
    +
    +#ifndef QHULLPOINTSET_H
    +#define QHULLPOINTSET_H
    +
    +#include "libqhull_r/qhull_ra.h"
    +#include "libqhullcpp/QhullSet.h"
    +#include "libqhullcpp/QhullPoint.h"
    +
    +#include 
    +
    +namespace orgQhull {
    +
    +#//!\name Used here
    +    class Qhull;
    +    class QhullPoint;
    +
    +#//!\name Defined here
    +    //! QhullPointSet -- a set of coordinate pointers with input dimension
    +    // with const_iterator and iterator
    +    class QhullPointSet;
    +
    +class QhullPointSet : public QhullSet {
    +
    +private:
    +#//!\name Fields
    +    // no fields
    +public:
    +
    +#//!\name Construct
    +                        QhullPointSet(const Qhull &q, setT *s) : QhullSet(q, s) {}
    +                        //Conversion from setT* is not type-safe.  Implicit conversion for void* to T
    +                        QhullPointSet(QhullQh *qqh, setT *s) : QhullSet(qqh, s) {}
    +                        //Copy constructor copies pointer but not contents.  Needed for return by value and parameter passing.
    +                        QhullPointSet(const QhullPointSet &other) : QhullSet(other) {}
    +                        //!Assignment copies pointers but not contents.
    +    QhullPointSet &     operator=(const QhullPointSet &other) { QhullSet::operator=(other); return *this; }
    +                        ~QhullPointSet() {}
    +
    +                        //!Default constructor disabled.
    +private:
    +                        QhullPointSet();
    +public:
    +
    +#//!\name IO
    +    struct PrintIdentifiers{
    +        const QhullPointSet *point_set;
    +        const char *    print_message; //!< non-null message
    +        PrintIdentifiers(const char *message, const QhullPointSet *s) : point_set(s), print_message(message) {}
    +    };//PrintIdentifiers
    +    PrintIdentifiers printIdentifiers(const char *message) const { return PrintIdentifiers(message, this); }
    +
    +    struct PrintPointSet{
    +        const QhullPointSet *point_set;
    +        const char *    print_message;  //!< non-null message
    +        PrintPointSet(const char *message, const QhullPointSet &s) : point_set(&s), print_message(message) {}
    +    };//PrintPointSet
    +    PrintPointSet       print(const char *message) const { return PrintPointSet(message, *this); }
    +
    +};//QhullPointSet
    +
    +typedef QhullSetIterator  QhullPointSetIterator;
    +
    +}//namespace orgQhull
    +
    +#//!\name Global
    +
    +std::ostream &operator<<(std::ostream &os, const orgQhull::QhullPointSet::PrintIdentifiers &pr);
    +std::ostream &operator<<(std::ostream &os, const orgQhull::QhullPointSet::PrintPointSet &pr);
    +
    +#endif // QHULLPOINTSET_H
    diff --git a/xs/src/qhull/src/libqhullcpp/QhullPoints.cpp b/xs/src/qhull/src/libqhullcpp/QhullPoints.cpp
    new file mode 100644
    index 000000000..2320b5007
    --- /dev/null
    +++ b/xs/src/qhull/src/libqhullcpp/QhullPoints.cpp
    @@ -0,0 +1,320 @@
    +/****************************************************************************
    +**
    +** Copyright (c) 2009-2015 C.B. Barber. All rights reserved.
    +** $Id: //main/2015/qhull/src/libqhullcpp/QhullPoints.cpp#3 $$Change: 2066 $
    +** $DateTime: 2016/01/18 19:29:17 $$Author: bbarber $
    +**
    +****************************************************************************/
    +
    +#include "libqhullcpp/QhullPoints.h"
    +
    +#include "libqhullcpp/Qhull.h"
    +
    +#include 
    +
    +#ifndef QHULL_NO_STL
    +#include 
    +#endif
    +
    +#ifdef _MSC_VER  // Microsoft Visual C++ -- warning level 4
    +#endif
    +
    +namespace orgQhull {
    +
    +#//!\name Constructors
    +
    +QhullPoints::
    +QhullPoints(const Qhull &q)
    +: point_first(0)
    +, point_end(0)
    +, qh_qh(q.qh())
    +, point_dimension(q.hullDimension())
    +{
    +}//QhullPoints Qhull
    +
    +QhullPoints::
    +QhullPoints(const Qhull &q, countT coordinateCount2, coordT *c)
    +: point_first(c)
    +, point_end(c+coordinateCount2)
    +, qh_qh(q.qh())
    +, point_dimension(q.hullDimension())
    +{
    +    QHULL_ASSERT(q.hullDimension());
    +    QHULL_ASSERT(coordinateCount2>=0);
    +}//QhullPoints Qhull dim
    +
    +QhullPoints::
    +QhullPoints(const Qhull &q, int pointDimension, countT coordinateCount2, coordT *c)
    +: point_first(c)
    +, point_end(c+coordinateCount2)
    +, qh_qh(q.qh())
    +, point_dimension(pointDimension)
    +{
    +    QHULL_ASSERT(pointDimension>=0);
    +    QHULL_ASSERT(coordinateCount2>=0);
    +}//QhullPoints Qhull dim coordT
    +
    +QhullPoints::
    +QhullPoints(QhullQh *qqh, int pointDimension, countT coordinateCount2, coordT *c)
    +: point_first(c)
    +, point_end(c+coordinateCount2)
    +, qh_qh(qqh)
    +, point_dimension(pointDimension)
    +{
    +    QHULL_ASSERT(pointDimension>=0);
    +    QHULL_ASSERT(coordinateCount2>=0);
    +}//QhullPoints QhullQh dim coordT
    +
    +#//!\name Conversions
    +// See qt-qhull.cpp for QList conversion
    +
    +#ifndef QHULL_NO_STL
    +std::vector QhullPoints::
    +toStdVector() const
    +{
    +    QhullPointsIterator i(*this);
    +    std::vector vs;
    +    while(i.hasNext()){
    +        vs.push_back(i.next());
    +    }
    +    return vs;
    +}//toStdVector
    +#endif //QHULL_NO_STL
    +
    +#//!\name GetSet
    +
    +countT QhullPoints::
    +extraCoordinatesCount() const
    +{
    +    if(point_dimension>0){
    +        return (countT)((point_end-point_first)%(size_t)point_dimension);
    +    }
    +    return 0;
    +}//extraCoordinatesCount
    +
    +//! QhullPoints is equal if the same address, or if the coordinates are identical
    +//! Use QhullPoint.operator==() for DISTround equality
    +bool QhullPoints::
    +operator==(const QhullPoints &other) const
    +{
    +    if((point_end-point_first) != (other.point_end-other.point_first)){
    +        return false;
    +    }
    +    if(point_dimension!=other.point_dimension){
    +        return false;
    +    }
    +    if(point_first==other.point_first){
    +        return true;
    +    }
    +    if(!qh_qh || qh_qh->hull_dim==0){
    +        const coordT *c= point_first;
    +        const coordT *c2= other.point_first;
    +        while(chull_dim : 0);
    +    point_first= 0;
    +    point_end= 0;
    +}//resetQhullQh
    +
    +QhullPoint QhullPoints::
    +value(countT idx) const
    +{
    +    QhullPoint p(qh_qh);
    +    if(idx>=0 && idx=0 && idx=n){
    +        n= 0;
    +    }else if(length<0 || idx+length>=n){
    +        n -= idx;
    +    }else{
    +        n -= idx+length;
    +    }
    +    return QhullPoints(qh_qh, point_dimension, n*point_dimension, point_first+idx*point_dimension);
    +}//mid
    +
    +#//!\name QhullPointsIterator
    +
    +bool QhullPointsIterator::
    +findNext(const QhullPoint &p)
    +{
    +    while(i!=ps->constEnd()){
    +        if(*i++ == p){
    +            return true;
    +        }
    +    }
    +    return false;
    +}//findNext
    +
    +bool QhullPointsIterator::
    +findPrevious(const QhullPoint &p)
    +{
    +    while(i!=ps->constBegin()){
    +        if(*--i == p){
    +            return true;
    +        }
    +    }
    +    return false;
    +}//findPrevious
    +
    +}//namespace orgQhull
    +
    +#//!\name Global functions
    +
    +using std::ostream;
    +using orgQhull::QhullPoint;
    +using orgQhull::QhullPoints;
    +using orgQhull::QhullPointsIterator;
    +
    +ostream &
    +operator<<(ostream &os, const QhullPoints &p)
    +{
    +    QhullPointsIterator i(p);
    +    while(i.hasNext()){
    +        os << i.next();
    +    }
    +    return os;
    +}//operator<  // ptrdiff_t, size_t
    +#include 
    +
    +namespace orgQhull {
    +
    +#//!\name Defined here
    +    class QhullPoints;          //!< One or more points Coordinate pointers with dimension and iterators
    +    class QhullPointsIterator;  //!< Java-style iterator
    +
    +//! QhullPoints are an array of QhullPoint as pointers into an array of coordinates.
    +//! For Qhull/QhullQh, QhullPoints must use hull_dim.  Can change QhullPoint to input_dim if needed for Delaunay input site
    +class QhullPoints {
    +
    +private:
    +#//!\name Fields
    +    coordT *            point_first; //!< First coordinate of an array of points of point_dimension
    +    coordT *            point_end;   //!< End of point coordinates (end>=first).  Trailing coordinates ignored
    +    QhullQh *           qh_qh;       //!< Maybe initialized NULL to allow ownership by RboxPoints
    +                                     //!< qh_qh used for QhullPoint() and qh_qh->hull_dim in constructor
    +    int                 point_dimension;  //!< Dimension, >=0
    +
    +public:
    +#//!\name Subtypes
    +    class const_iterator;
    +    class iterator;
    +    typedef QhullPoints::const_iterator ConstIterator;
    +    typedef QhullPoints::iterator       Iterator;
    +
    +#//!\name Construct
    +    //! QhullPoint, PointCoordinates, and QhullPoints have similar constructors
    +    //! If Qhull/QhullQh is not initialized, then QhullPoints.dimension() is zero unless explicitly set
    +    //! Cannot define QhullPoints(int pointDimension) since it is ambiguous with QhullPoints(QhullQh *qqh)
    +                        QhullPoints() : point_first(0), point_end(0), qh_qh(0), point_dimension(0) { }
    +                        QhullPoints(int pointDimension, countT coordinateCount2, coordT *c) : point_first(c), point_end(c+coordinateCount2), qh_qh(0), point_dimension(pointDimension) { QHULL_ASSERT(pointDimension>=0); }
    +    explicit            QhullPoints(const Qhull &q);
    +                        QhullPoints(const Qhull &q, countT coordinateCount2, coordT *c);
    +                        QhullPoints(const Qhull &q, int pointDimension, countT coordinateCount2, coordT *c);
    +    explicit            QhullPoints(QhullQh *qqh) : point_first(0), point_end(0), qh_qh(qqh), point_dimension(qqh ? qqh->hull_dim : 0) { }
    +                        QhullPoints(QhullQh *qqh, countT coordinateCount2, coordT *c) : point_first(c), point_end(c+coordinateCount2), qh_qh(qqh), point_dimension(qqh ? qqh->hull_dim : 0) { QHULL_ASSERT(qqh && qqh->hull_dim>0); }
    +                        QhullPoints(QhullQh *qqh, int pointDimension, countT coordinateCount2, coordT *c);
    +                        //! Copy constructor copies pointers but not contents.  Needed for return by value and parameter passing.
    +                        QhullPoints(const QhullPoints &other)  : point_first(other.point_first), point_end(other.point_end), qh_qh(other.qh_qh), point_dimension(other.point_dimension) {}
    +    QhullPoints &       operator=(const QhullPoints &other) { point_first= other.point_first; point_end= other.point_end; qh_qh= other.qh_qh; point_dimension= other.point_dimension; return *this; }
    +                        ~QhullPoints() {}
    +
    +public:
    +
    +#//!\name Conversion
    +
    +#ifndef QHULL_NO_STL
    +    std::vector toStdVector() const;
    +#endif //QHULL_NO_STL
    +#ifdef QHULL_USES_QT
    +    QList   toQList() const;
    +#endif //QHULL_USES_QT
    +
    +#//!\name GetSet
    +    // Constructs QhullPoint.  Cannot return reference.
    +    const QhullPoint    at(countT idx) const { /* point_first==0 caught by point_end assert */ coordT *p= point_first+idx*point_dimension; QHULL_ASSERT(p=0 && coordinatesCount>=0 && c!=0); point_first= c; point_end= c+coordinatesCount; point_dimension= pointDimension; }
    +    void                defineAs(countT coordinatesCount, coordT *c) { QHULL_ASSERT((point_dimension>0 && coordinatesCount>=0 && c!=0) || (c==0 && coordinatesCount==0)); point_first= c; point_end= c+coordinatesCount; }
    +    void                defineAs(const QhullPoints &other) { point_first= other.point_first; point_end= other.point_end; qh_qh= other.qh_qh; point_dimension= other.point_dimension; }
    +    int                 dimension() const { return point_dimension; }
    +    ConstIterator       end() const { return ConstIterator(qh_qh, point_dimension, point_end); }
    +    Iterator            end() { return Iterator(qh_qh, point_dimension, point_end); }
    +    coordT *            extraCoordinates() const { return extraCoordinatesCount() ? (point_end-extraCoordinatesCount()) : 0; }
    +    countT              extraCoordinatesCount() const;  // WARN64
    +    // Constructs QhullPoint.  Cannot return reference.
    +    const QhullPoint    first() const { return QhullPoint(qh_qh, point_dimension, point_first); }
    +    QhullPoint          first() { return QhullPoint(qh_qh, point_dimension, point_first); }
    +    // Constructs QhullPoint.  Cannot return reference.
    +    const QhullPoint    front() const { return first(); }
    +    QhullPoint          front() { return first(); }
    +    bool                includesCoordinates(const coordT *c) const { return c>=point_first && c(other)); return *this; }
    +
    +        // Need 'const QhullPoint' to maintain const
    +        const QhullPoint & operator*() const { return *this; }
    +        QhullPoint &    operator*() { return *this; }
    +        const QhullPoint * operator->() const { return this; }
    +        QhullPoint *    operator->() { return this; }
    +        // value instead of reference since advancePoint() modifies self
    +        QhullPoint      operator[](countT idx) const { QhullPoint result= *this; result.advancePoint(idx); return result; }
    +        bool            operator==(const iterator &o) const { QHULL_ASSERT(qh_qh==o.qh_qh); return (point_coordinates==o.point_coordinates && point_dimension==o.point_dimension); }
    +        bool            operator!=(const iterator &o) const { return !operator==(o); }
    +        bool            operator<(const iterator &o) const  { QHULL_ASSERT(qh_qh==o.qh_qh); return point_coordinates < o.point_coordinates; }
    +        bool            operator<=(const iterator &o) const { QHULL_ASSERT(qh_qh==o.qh_qh); return point_coordinates <= o.point_coordinates; }
    +        bool            operator>(const iterator &o) const  { QHULL_ASSERT(qh_qh==o.qh_qh); return point_coordinates > o.point_coordinates; }
    +        bool            operator>=(const iterator &o) const { QHULL_ASSERT(qh_qh==o.qh_qh); return point_coordinates >= o.point_coordinates; }
    +        // reinterpret_cast to break circular dependency
    +        bool            operator==(const QhullPoints::const_iterator &o) const { QHULL_ASSERT(qh_qh==reinterpret_cast(o).qh_qh); return (point_coordinates==reinterpret_cast(o).point_coordinates && point_dimension==reinterpret_cast(o).point_dimension); }
    +        bool            operator!=(const QhullPoints::const_iterator &o) const { return !operator==(reinterpret_cast(o)); }
    +        bool            operator<(const QhullPoints::const_iterator &o) const  { QHULL_ASSERT(qh_qh==reinterpret_cast(o).qh_qh); return point_coordinates < reinterpret_cast(o).point_coordinates; }
    +        bool            operator<=(const QhullPoints::const_iterator &o) const { QHULL_ASSERT(qh_qh==reinterpret_cast(o).qh_qh); return point_coordinates <= reinterpret_cast(o).point_coordinates; }
    +        bool            operator>(const QhullPoints::const_iterator &o) const  { QHULL_ASSERT(qh_qh==reinterpret_cast(o).qh_qh); return point_coordinates > reinterpret_cast(o).point_coordinates; }
    +        bool            operator>=(const QhullPoints::const_iterator &o) const { QHULL_ASSERT(qh_qh==reinterpret_cast(o).qh_qh); return point_coordinates >= reinterpret_cast(o).point_coordinates; }
    +        iterator &      operator++() { advancePoint(1); return *this; }
    +        iterator        operator++(int) { iterator n= *this; operator++(); return iterator(n); }
    +        iterator &      operator--() { advancePoint(-1); return *this; }
    +        iterator        operator--(int) { iterator n= *this; operator--(); return iterator(n); }
    +        iterator &      operator+=(countT idx) { advancePoint(idx); return *this; }
    +        iterator &      operator-=(countT idx) { advancePoint(-idx); return *this; }
    +        iterator        operator+(countT idx) const { iterator n= *this; n.advancePoint(idx); return n; }
    +        iterator        operator-(countT idx) const { iterator n= *this; n.advancePoint(-idx); return n; }
    +        difference_type operator-(iterator o) const { QHULL_ASSERT(qh_qh==o.qh_qh && point_dimension==o.point_dimension); return (point_dimension ? (point_coordinates-o.point_coordinates)/point_dimension : 0); }
    +    };//QhullPoints::iterator
    +
    +#//!\name QhullPoints::const_iterator
    +    //!\todo FIXUP QH11018 const_iterator same as iterator.  SHould have a common definition
    +    class const_iterator : public QhullPoint {
    +
    +    public:
    +        typedef std::random_access_iterator_tag  iterator_category;
    +        typedef QhullPoint          value_type;
    +        typedef const value_type *  pointer;
    +        typedef const value_type &  reference;
    +        typedef ptrdiff_t           difference_type;
    +
    +                        const_iterator(const QhullPoints::iterator &o) : QhullPoint(*o) {}
    +        explicit        const_iterator(const QhullPoints &ps) : QhullPoint(ps.qh(), ps.dimension(), ps.coordinates()) {}
    +                        const_iterator(const int pointDimension, coordT *c): QhullPoint(pointDimension, c) {}
    +                        const_iterator(const Qhull &q, coordT *c): QhullPoint(q, c) {}
    +                        const_iterator(const Qhull &q, int pointDimension, coordT *c): QhullPoint(q, pointDimension, c) {}
    +                        const_iterator(QhullQh *qqh, coordT *c): QhullPoint(qqh, c) {}
    +                        const_iterator(QhullQh *qqh, int pointDimension, coordT *c): QhullPoint(qqh, pointDimension, c) {}
    +                        const_iterator(const const_iterator &o) : QhullPoint(*o) {}
    +        const_iterator &operator=(const const_iterator &o) { defineAs(const_cast(o)); return *this; }
    +
    +        // value/non-const since advancePoint(1), etc. modifies self
    +        const QhullPoint & operator*() const { return *this; }
    +        const QhullPoint * operator->() const { return this; }
    +        // value instead of reference since advancePoint() modifies self
    +        const QhullPoint operator[](countT idx) const { QhullPoint n= *this; n.advancePoint(idx); return n; }
    +        bool            operator==(const const_iterator &o) const { QHULL_ASSERT(qh_qh==o.qh_qh); return (point_coordinates==o.point_coordinates && point_dimension==o.point_dimension); }
    +        bool            operator!=(const const_iterator &o) const { return ! operator==(o); }
    +        bool            operator<(const const_iterator &o) const  { QHULL_ASSERT(qh_qh==o.qh_qh); return point_coordinates < o.point_coordinates; }
    +        bool            operator<=(const const_iterator &o) const { QHULL_ASSERT(qh_qh==o.qh_qh); return point_coordinates <= o.point_coordinates; }
    +        bool            operator>(const const_iterator &o) const  { QHULL_ASSERT(qh_qh==o.qh_qh); return point_coordinates > o.point_coordinates; }
    +        bool            operator>=(const const_iterator &o) const { QHULL_ASSERT(qh_qh==o.qh_qh); return point_coordinates >= o.point_coordinates; }
    +        const_iterator &operator++() { advancePoint(1); return *this; }
    +        const_iterator  operator++(int) { const_iterator n= *this; operator++(); return const_iterator(n); }
    +        const_iterator &operator--() { advancePoint(-1); return *this; }
    +        const_iterator  operator--(int) { const_iterator n= *this; operator--(); return const_iterator(n); }
    +        const_iterator &operator+=(countT idx) { advancePoint(idx); return *this; }
    +        const_iterator &operator-=(countT idx) { advancePoint(-idx); return *this; }
    +        const_iterator  operator+(countT idx) const { const_iterator n= *this; n.advancePoint(idx); return n; }
    +        const_iterator  operator-(countT idx) const { const_iterator n= *this; n.advancePoint(-idx); return n; }
    +        difference_type operator-(const_iterator o) const { QHULL_ASSERT(qh_qh==o.qh_qh && point_dimension==o.point_dimension); return (point_dimension ? (point_coordinates-o.point_coordinates)/point_dimension : 0); }
    +    };//QhullPoints::const_iterator
    +
    +#//!\name IO
    +    struct PrintPoints{
    +        const QhullPoints  *points;
    +        const char *    point_message;
    +        bool            with_identifier;
    +        PrintPoints(const char *message, bool withIdentifier, const QhullPoints &ps) : points(&ps), point_message(message), with_identifier(withIdentifier) {}
    +    };//PrintPoints
    +    PrintPoints          print(const char *message) const { return PrintPoints(message, false, *this); }
    +    PrintPoints          printWithIdentifier(const char *message) const { return PrintPoints(message, true, *this); }
    +};//QhullPoints
    +
    +// Instead of QHULL_DECLARE_SEQUENTIAL_ITERATOR because next(),etc would return a reference to a temporary
    +class QhullPointsIterator
    +{
    +    typedef QhullPoints::const_iterator const_iterator;
    +
    +#//!\name Fields
    +private:
    +    const QhullPoints  *ps;
    +    const_iterator      i;
    +
    +public:
    +                        QhullPointsIterator(const QhullPoints &other) : ps(&other), i(ps->constBegin()) {}
    +    QhullPointsIterator &operator=(const QhullPoints &other) { ps = &other; i = ps->constBegin(); return *this; }
    +
    +    bool                findNext(const QhullPoint &t);
    +    bool                findPrevious(const QhullPoint &t);
    +    bool                hasNext() const { return i != ps->constEnd(); }
    +    bool                hasPrevious() const { return i != ps->constBegin(); }
    +    QhullPoint          next() { return *i++; }
    +    QhullPoint          peekNext() const { return *i; }
    +    QhullPoint          peekPrevious() const { const_iterator p = i; return *--p; }
    +    QhullPoint          previous() { return *--i; }
    +    void                toBack() { i = ps->constEnd(); }
    +    void                toFront() { i = ps->constBegin(); }
    +};//QhullPointsIterator
    +
    +}//namespace orgQhull
    +
    +#//!\name Global
    +
    +std::ostream &          operator<<(std::ostream &os, const orgQhull::QhullPoints &p);
    +std::ostream &          operator<<(std::ostream &os, const orgQhull::QhullPoints::PrintPoints &pr);
    +
    +#endif // QHULLPOINTS_H
    diff --git a/xs/src/qhull/src/libqhullcpp/QhullQh.cpp b/xs/src/qhull/src/libqhullcpp/QhullQh.cpp
    new file mode 100644
    index 000000000..363533700
    --- /dev/null
    +++ b/xs/src/qhull/src/libqhullcpp/QhullQh.cpp
    @@ -0,0 +1,237 @@
    +/****************************************************************************
    +**
    +** Copyright (c) 2008-2015 C.B. Barber. All rights reserved.
    +** $Id: //main/2015/qhull/src/libqhullcpp/QhullQh.cpp#5 $$Change: 2066 $
    +** $DateTime: 2016/01/18 19:29:17 $$Author: bbarber $
    +**
    +****************************************************************************/
    +
    +#//! QhullQh -- Qhull's global data structure, qhT, as a C++ class
    +
    +
    +#include "libqhullcpp/QhullQh.h"
    +
    +#include "libqhullcpp/QhullError.h"
    +#include "libqhullcpp/QhullStat.h"
    +
    +#include 
    +#include 
    +
    +#include 
    +
    +using std::cerr;
    +using std::string;
    +using std::vector;
    +using std::ostream;
    +
    +#ifdef _MSC_VER  // Microsoft Visual C++ -- warning level 4
    +#pragma warning( disable : 4611)  // interaction between '_setjmp' and C++ object destruction is non-portable
    +#pragma warning( disable : 4996)  // function was declared deprecated(strcpy, localtime, etc.)
    +#endif
    +
    +namespace orgQhull {
    +
    +#//!\name Global variables
    +const double QhullQh::
    +default_factor_epsilon= 1.0;
    +
    +#//!\name Constructor, destructor, etc.
    +
    +//! Derived from qh_new_qhull[user.c]
    +QhullQh::
    +QhullQh()
    +: qhull_status(qh_ERRnone)
    +, qhull_message()
    +, error_stream(0)
    +, output_stream(0)
    +, factor_epsilon(QhullQh::default_factor_epsilon)
    +, use_output_stream(false)
    +{
    +    // NOerrors: TRY_QHULL_ not needed since these routines do not call qh_errexit()
    +    qh_meminit(this, NULL);
    +    qh_initstatistics(this);
    +    qh_initqhull_start2(this, NULL, NULL, qh_FILEstderr);  // Initialize qhT
    +    this->ISqhullQh= True;
    +}//QhullQh
    +
    +QhullQh::
    +~QhullQh()
    +{
    +    checkAndFreeQhullMemory();
    +}//~QhullQh
    +
    +#//!\name Methods
    +
    +//! Check memory for internal consistency
    +//! Free global memory used by qh_initbuild and qh_buildhull
    +//! Zero the qhT data structure, except for memory (qhmemT) and statistics (qhstatT)
    +//! Check and free short memory (e.g., facetT)
    +//! Zero the qhmemT data structure
    +void QhullQh::
    +checkAndFreeQhullMemory()
    +{
    +#ifdef qh_NOmem
    +    qh_freeqhull(this, qh_ALL);
    +#else
    +    qh_memcheck(this);
    +    qh_freeqhull(this, !qh_ALL);
    +    countT curlong;
    +    countT totlong;
    +    qh_memfreeshort(this, &curlong, &totlong);
    +    if (curlong || totlong)
    +        throw QhullError(10026, "Qhull error: qhull did not free %d bytes of long memory (%d pieces).", totlong, curlong);
    +#endif
    +}//checkAndFreeQhullMemory
    +
    +#//!\name Messaging
    +
    +void QhullQh::
    +appendQhullMessage(const string &s)
    +{
    +    if(output_stream && use_output_stream && this->USEstdout){ 
    +        *output_stream << s;
    +    }else if(error_stream){
    +        *error_stream << s;
    +    }else{
    +        qhull_message += s;
    +    }
    +}//appendQhullMessage
    +
    +//! clearQhullMessage does not throw errors (~Qhull)
    +void QhullQh::
    +clearQhullMessage()
    +{
    +    qhull_status= qh_ERRnone;
    +    qhull_message.clear();
    +    RoadError::clearGlobalLog();
    +}//clearQhullMessage
    +
    +//! hasQhullMessage does not throw errors (~Qhull)
    +bool QhullQh::
    +hasQhullMessage() const
    +{
    +    return (!qhull_message.empty() || qhull_status!=qh_ERRnone);
    +    //FIXUP QH11006 -- inconsistent usage with Rbox.  hasRboxMessage just tests rbox_status.  No appendRboxMessage()
    +}
    +
    +void QhullQh::
    +maybeThrowQhullMessage(int exitCode)
    +{
    +    if(!NOerrexit){
    +        if(qhull_message.size()>0){
    +            qhull_message.append("\n");
    +        }
    +        if(exitCode || qhull_status==qh_ERRnone){
    +            qhull_status= 10073;
    +        }else{
    +            qhull_message.append("QH10073: ");
    +        }
    +        qhull_message.append("Cannot call maybeThrowQhullMessage() from QH_TRY_().  Or missing 'qh->NOerrexit=true;' after QH_TRY_(){...}.");
    +    }
    +    if(qhull_status==qh_ERRnone){
    +        qhull_status= exitCode;
    +    }
    +    if(qhull_status!=qh_ERRnone){
    +        QhullError e(qhull_status, qhull_message);
    +        clearQhullMessage();
    +        throw e; // FIXUP QH11007: copy constructor is expensive if logging
    +    }
    +}//maybeThrowQhullMessage
    +
    +void QhullQh::
    +maybeThrowQhullMessage(int exitCode, int noThrow)  throw()
    +{
    +    QHULL_UNUSED(noThrow);
    +
    +    if(qhull_status==qh_ERRnone){
    +        qhull_status= exitCode;
    +    }
    +    if(qhull_status!=qh_ERRnone){
    +        QhullError e(qhull_status, qhull_message);
    +        e.logErrorLastResort();
    +    }
    +}//maybeThrowQhullMessage
    +
    +//! qhullMessage does not throw errors (~Qhull)
    +std::string QhullQh::
    +qhullMessage() const
    +{
    +    if(qhull_message.empty() && qhull_status!=qh_ERRnone){
    +        return "qhull: no message for error.  Check cerr or error stream\n";
    +    }else{
    +        return qhull_message;
    +    }
    +}//qhullMessage
    +
    +int QhullQh::
    +qhullStatus() const
    +{
    +    return qhull_status;
    +}//qhullStatus
    +
    +void QhullQh::
    +setErrorStream(ostream *os)
    +{
    +    error_stream= os;
    +}//setErrorStream
    +
    +//! Updates use_output_stream
    +void QhullQh::
    +setOutputStream(ostream *os)
    +{
    +    output_stream= os;
    +    use_output_stream= (os!=0);
    +}//setOutputStream
    +
    +}//namespace orgQhull
    +
    +/*---------------------------------
    +
    +  qh_fprintf(qhT *qh, fp, msgcode, format, list of args )
    +    replaces qh_fprintf() in userprintf_r.c
    +
    +notes:
    +    only called from libqhull
    +    same as fprintf() and RboxPoints.qh_fprintf_rbox()
    +    fgets() is not trapped like fprintf()
    +    Do not throw errors from here.  Use qh_errexit;
    +*/
    +extern "C"
    +void qh_fprintf(qhT *qh, FILE *fp, int msgcode, const char *fmt, ... ) {
    +    va_list args;
    +
    +    using namespace orgQhull;
    +
    +    if(!qh->ISqhullQh){
    +        qh_fprintf_stderr(10025, "Qhull error: qh_fprintf called from a Qhull instance without QhullQh defined\n");
    +        qh_exit(10025);
    +    }
    +    QhullQh *qhullQh= static_cast(qh);
    +    va_start(args, fmt);
    +    if(msgcode=MSG_ERROR && msgcodeqhull_statusqhull_status>=MSG_WARNING){
    +                qhullQh->qhull_status= msgcode;
    +            }
    +        }
    +        char newMessage[MSG_MAXLEN];
    +        // RoadError will add the message tag
    +        vsnprintf(newMessage, sizeof(newMessage), fmt, args);
    +        qhullQh->appendQhullMessage(newMessage);
    +        va_end(args);
    +        return;
    +    }
    +    if(qhullQh->output_stream && qhullQh->use_output_stream){
    +        char newMessage[MSG_MAXLEN];
    +        vsnprintf(newMessage, sizeof(newMessage), fmt, args);
    +        *qhullQh->output_stream << newMessage;
    +        va_end(args);
    +        return;
    +    }
    +    // FIXUP QH11008: how do users trap messages and handle input?  A callback?
    +    char newMessage[MSG_MAXLEN];
    +    vsnprintf(newMessage, sizeof(newMessage), fmt, args);
    +    qhullQh->appendQhullMessage(newMessage);
    +    va_end(args);
    +} /* qh_fprintf */
    diff --git a/xs/src/qhull/src/libqhullcpp/QhullQh.h b/xs/src/qhull/src/libqhullcpp/QhullQh.h
    new file mode 100644
    index 000000000..c3b277ff0
    --- /dev/null
    +++ b/xs/src/qhull/src/libqhullcpp/QhullQh.h
    @@ -0,0 +1,110 @@
    +/****************************************************************************
    +**
    +** Copyright (c) 2008-2015 C.B. Barber. All rights reserved.
    +** $Id: //main/2015/qhull/src/libqhullcpp/QhullQh.h#2 $$Change: 2079 $
    +** $DateTime: 2016/02/07 17:43:34 $$Author: bbarber $
    +**
    +****************************************************************************/
    +
    +#ifndef QHULLQH_H
    +#define QHULLQH_H
    +
    +#include "libqhull_r/qhull_ra.h"
    +
    +#include 
    +
    +#ifdef _MSC_VER  // Microsoft Visual C++ -- warning level 4
    +#pragma warning( disable : 4611)  /* interaction between '_setjmp' and C++ object destruction is non-portable */
    +/* setjmp should not be implemented with 'catch' */
    +#endif
    +
    +//! Use QH_TRY_ or QH_TRY_NOTHROW_ to call a libqhull_r routine that may invoke qh_errexit()
    +//! QH_TRY_(qh){...} qh->NOerrexit=true;
    +//! No object creation -- longjmp() skips object destructors
    +//! To test for error when done -- qh->maybeThrowQhullMessage(QH_TRY_status);
    +//! Use the same compiler for QH_TRY_, libqhullcpp, and libqhull_r.  setjmp() is not portable between compilers.
    +
    +#define QH_TRY_ERROR 10071
    +
    +#define QH_TRY_(qh) \
    +    int QH_TRY_status; \
    +    if(qh->NOerrexit){ \
    +        qh->NOerrexit= False; \
    +        QH_TRY_status= setjmp(qh->errexit); \
    +    }else{ \
    +        throw QhullError(QH_TRY_ERROR, "Cannot invoke QH_TRY_() from inside a QH_TRY_.  Or missing 'qh->NOerrexit=true' after previously called QH_TRY_(qh){...}"); \
    +    } \
    +    if(!QH_TRY_status)
    +
    +#define QH_TRY_NO_THROW_(qh) \
    +    int QH_TRY_status; \
    +    if(qh->NOerrexit){ \
    +        qh->NOerrexit= False; \
    +        QH_TRY_status= setjmp(qh->errexit); \
    +    }else{ \
    +        QH_TRY_status= QH_TRY_ERROR; \
    +    } \
    +    if(!QH_TRY_status)
    +
    +namespace orgQhull {
    +
    +#//!\name Defined here
    +    //! QhullQh -- Qhull's global data structure, qhT, as a C++ class
    +    class QhullQh;
    +
    +//! POD type equivalent to qhT.  No virtual members
    +class QhullQh : public qhT {
    +
    +#//!\name Constants
    +
    +#//!\name Fields
    +private:
    +    int                 qhull_status;   //!< qh_ERRnone if valid
    +    std::string         qhull_message;  //!< Returned messages from libqhull_r
    +    std::ostream *      error_stream;   //!< overrides errorMessage, use appendQhullMessage()
    +    std::ostream *      output_stream;  //!< send output to stream
    +    double              factor_epsilon; //!< Factor to increase ANGLEround and DISTround for hyperplane equality
    +    bool                use_output_stream; //!< True if using output_stream
    +
    +    friend void         ::qh_fprintf(qhT *qh, FILE *fp, int msgcode, const char *fmt, ... );
    +
    +    static const double default_factor_epsilon;  //!< Default factor_epsilon is 1.0, never updated
    +
    +#//!\name Constructors
    +public:
    +                        QhullQh();
    +                        ~QhullQh();
    +private:
    +                        //!disable copy constructor and assignment
    +                        QhullQh(const QhullQh &);
    +    QhullQh &           operator=(const QhullQh &);
    +public:
    +
    +#//!\name GetSet
    +    double              factorEpsilon() const { return factor_epsilon; }
    +    void                setFactorEpsilon(double a) { factor_epsilon= a; }
    +    void                disableOutputStream() { use_output_stream= false; }
    +    void                enableOutputStream() { use_output_stream= true; }
    +
    +#//!\name Messaging
    +    void                appendQhullMessage(const std::string &s);
    +    void                clearQhullMessage();
    +    std::string         qhullMessage() const;
    +    bool                hasOutputStream() const { return use_output_stream; }
    +    bool                hasQhullMessage() const;
    +    void                maybeThrowQhullMessage(int exitCode);
    +    void                maybeThrowQhullMessage(int exitCode, int noThrow) throw();
    +    int                 qhullStatus() const;
    +    void                setErrorStream(std::ostream *os);
    +    void                setOutputStream(std::ostream *os);
    +
    +#//!\name Methods
    +    double              angleEpsilon() const { return this->ANGLEround*factor_epsilon; } //!< Epsilon for hyperplane angle equality
    +    void                checkAndFreeQhullMemory();
    +    double              distanceEpsilon() const { return this->DISTround*factor_epsilon; } //!< Epsilon for distance to hyperplane
    +
    +};//class QhullQh
    +
    +}//namespace orgQhull
    +
    +#endif // QHULLQH_H
    diff --git a/xs/src/qhull/src/libqhullcpp/QhullRidge.cpp b/xs/src/qhull/src/libqhullcpp/QhullRidge.cpp
    new file mode 100644
    index 000000000..7a0181280
    --- /dev/null
    +++ b/xs/src/qhull/src/libqhullcpp/QhullRidge.cpp
    @@ -0,0 +1,124 @@
    +/****************************************************************************
    +**
    +** Copyright (c) 2008-2015 C.B. Barber. All rights reserved.
    +** $Id: //main/2015/qhull/src/libqhullcpp/QhullRidge.cpp#3 $$Change: 2066 $
    +** $DateTime: 2016/01/18 19:29:17 $$Author: bbarber $
    +**
    +****************************************************************************/
    +
    +#//! QhullRidge -- Qhull's ridge structure, ridgeT, as a C++ class
    +
    +#include "libqhullcpp/QhullRidge.h"
    +
    +#include "libqhullcpp/QhullSets.h"
    +#include "libqhullcpp/QhullVertex.h"
    +#include "libqhullcpp/Qhull.h"
    +
    +#ifdef _MSC_VER  // Microsoft Visual C++ -- warning level 4
    +#pragma warning( disable : 4611)  // interaction between '_setjmp' and C++ object destruction is non-portable
    +#pragma warning( disable : 4996)  // function was declared deprecated(strcpy, localtime, etc.)
    +#endif
    +
    +namespace orgQhull {
    +
    +#//!\name Class objects
    +ridgeT QhullRidge::
    +s_empty_ridge= {0,0,0,0,0,
    +                0,0};
    +
    +#//!\name Constructors
    +
    +QhullRidge::QhullRidge(const Qhull &q)
    +: qh_ridge(&s_empty_ridge)
    +, qh_qh(q.qh())
    +{
    +}//Default
    +
    +QhullRidge::QhullRidge(const Qhull &q, ridgeT *r)
    +: qh_ridge(r ? r : &s_empty_ridge)
    +, qh_qh(q.qh())
    +{
    +}//ridgeT
    +
    +#//!\name foreach
    +
    +//! Return True if nextRidge3d
    +//! Simplicial facets may have incomplete ridgeSets
    +//! Does not use qh_errexit()
    +bool QhullRidge::
    +hasNextRidge3d(const QhullFacet &f) const
    +{
    +    if(!qh_qh){
    +        return false;
    +    }
    +    vertexT *v= 0;
    +    // Does not call qh_errexit(), TRY_QHULL_ not needed
    +    ridgeT *ridge= qh_nextridge3d(getRidgeT(), f.getFacetT(), &v);
    +    return (ridge!=0);
    +}//hasNextRidge3d
    +
    +//! Return next ridge and optional vertex for a 3d facet and ridge
    +//! Does not use qh_errexit()
    +QhullRidge QhullRidge::
    +nextRidge3d(const QhullFacet &f, QhullVertex *nextVertex) const
    +{
    +    vertexT *v= 0;
    +    ridgeT *ridge= 0;
    +    if(qh_qh){
    +        // Does not call qh_errexit(), TRY_QHULL_ not needed
    +        ridge= qh_nextridge3d(getRidgeT(), f.getFacetT(), &v);
    +        if(!ridge){
    +            throw QhullError(10030, "Qhull error nextRidge3d:  missing next ridge for facet %d ridge %d.  Does facet contain ridge?", f.id(), id());
    +        }
    +    }
    +    if(nextVertex!=0){
    +        *nextVertex= QhullVertex(qh_qh, v);
    +    }
    +    return QhullRidge(qh_qh, ridge);
    +}//nextRidge3d
    +
    +}//namespace orgQhull
    +
    +#//!\name Global functions
    +
    +using std::endl;
    +using std::ostream;
    +using orgQhull::QhullRidge;
    +using orgQhull::QhullVertex;
    +
    +ostream &
    +operator<<(ostream &os, const QhullRidge &r)
    +{
    +    os << r.print("");
    +    return os;
    +}//<< QhullRidge
    +
    +//! Duplicate of qh_printridge [io_r.c]
    +ostream &
    +operator<<(ostream &os, const QhullRidge::PrintRidge &pr)
    +{
    +    if(*pr.print_message){
    +        os << pr.print_message << " ";
    +    }else{
    +        os << "     - ";
    +    }
    +    QhullRidge r= *pr.ridge;
    +    os << "r" << r.id();
    +    if(r.getRidgeT()->tested){
    +        os << " tested";
    +    }
    +    if(r.getRidgeT()->nonconvex){
    +        os << " nonconvex";
    +    }
    +    os << endl;
    +    os << r.vertices().print("           vertices:");
    +    if(r.getRidgeT()->top && r.getRidgeT()->bottom){
    +        os << "           between f" << r.topFacet().id() << " and f" << r.bottomFacet().id() << endl;
    +    }else if(r.getRidgeT()->top){
    +        os << "           top f" << r.topFacet().id() << endl;
    +    }else if(r.getRidgeT()->bottom){
    +        os << "           bottom f" << r.bottomFacet().id() << endl;
    +    }
    +
    +    return os;
    +}//<< PrintRidge
    diff --git a/xs/src/qhull/src/libqhullcpp/QhullRidge.h b/xs/src/qhull/src/libqhullcpp/QhullRidge.h
    new file mode 100644
    index 000000000..924340fb0
    --- /dev/null
    +++ b/xs/src/qhull/src/libqhullcpp/QhullRidge.h
    @@ -0,0 +1,112 @@
    +/****************************************************************************
    +**
    +** Copyright (c) 2008-2015 C.B. Barber. All rights reserved.
    +** $Id: //main/2015/qhull/src/libqhullcpp/QhullRidge.h#4 $$Change: 2079 $
    +** $DateTime: 2016/02/07 17:43:34 $$Author: bbarber $
    +**
    +****************************************************************************/
    +
    +#ifndef QHULLRIDGE_H
    +#define QHULLRIDGE_H
    +
    +#include "libqhull_r/qhull_ra.h"
    +#include "libqhullcpp/QhullSet.h"
    +#include "libqhullcpp/QhullVertex.h"
    +#include "libqhullcpp/QhullVertexSet.h"
    +#include "libqhullcpp/QhullFacet.h"
    +
    +#include 
    +
    +namespace orgQhull {
    +
    +#//!\name Used here
    +    class Qhull;
    +    class QhullVertex;
    +    class QhullVertexSet;
    +    class QhullFacet;
    +
    +#//!\name Defined here
    +    //! QhullRidge -- Qhull's ridge structure, ridgeT [libqhull.h], as a C++ class
    +    class QhullRidge;
    +    typedef QhullSet  QhullRidgeSet;
    +    typedef QhullSetIterator  QhullRidgeSetIterator;
    +    // see QhullSets.h for QhullRidgeSet and QhullRidgeSetIterator -- avoids circular references
    +
    +/************************
    +a ridge is hull_dim-1 simplex between two neighboring facets.  If the
    +facets are non-simplicial, there may be more than one ridge between
    +two facets.  E.G. a 4-d hypercube has two triangles between each pair
    +of neighboring facets.
    +
    +topological information:
    +    vertices            a set of vertices
    +    top,bottom          neighboring facets with orientation
    +
    +geometric information:
    +    tested              True if ridge is clearly convex
    +    nonconvex           True if ridge is non-convex
    +*/
    +
    +class QhullRidge {
    +
    +#//!\name Defined here
    +public:
    +    typedef ridgeT *   base_type;  // for QhullRidgeSet
    +
    +#//!\name Fields
    +private:
    +    ridgeT *            qh_ridge;  //!< Corresponding ridgeT, never 0
    +    QhullQh *           qh_qh;     //!< QhullQh/qhT for ridgeT, may be 0
    +
    +#//!\name Class objects
    +    static ridgeT       s_empty_ridge;
    +
    +public:
    +#//!\name Constants
    +
    +#//!\name Constructors
    +                        QhullRidge() : qh_ridge(&s_empty_ridge), qh_qh(0) {}
    +    explicit            QhullRidge(const Qhull &q);
    +                        QhullRidge(const Qhull &q, ridgeT *r);
    +    explicit            QhullRidge(QhullQh *qqh) : qh_ridge(&s_empty_ridge), qh_qh(qqh) {}
    +                        QhullRidge(QhullQh *qqh, ridgeT *r) : qh_ridge(r ? r : &s_empty_ridge), qh_qh(qqh) {}
    +                        // Creates an alias.  Does not copy QhullRidge.  Needed for return by value and parameter passing
    +                        QhullRidge(const QhullRidge &other) : qh_ridge(other.qh_ridge), qh_qh(other.qh_qh) {}
    +                        // Creates an alias.  Does not copy QhullRidge.  Needed for vector
    +    QhullRidge &        operator=(const QhullRidge &other) { qh_ridge= other.qh_ridge; qh_qh= other.qh_qh; return *this; }
    +                        ~QhullRidge() {}
    +
    +#//!\name GetSet
    +    QhullFacet          bottomFacet() const { return QhullFacet(qh_qh, qh_ridge->bottom); }
    +    int                 dimension() const { return ((qh_qh && qh_qh->hull_dim) ? qh_qh->hull_dim-1 : 0); }
    +    ridgeT *            getBaseT() const { return getRidgeT(); } //!< For QhullSet
    +    ridgeT *            getRidgeT() const { return qh_ridge; }
    +    countT              id() const { return qh_ridge->id; }
    +    bool                isValid() const { return (qh_qh && qh_ridge != &s_empty_ridge); }
    +    bool                operator==(const QhullRidge &other) const { return qh_ridge==other.qh_ridge; }
    +    bool                operator!=(const QhullRidge &other) const { return !operator==(other); }
    +    QhullFacet          otherFacet(const QhullFacet &f) const { return QhullFacet(qh_qh, (qh_ridge->top==f.getFacetT() ? qh_ridge->bottom : qh_ridge->top)); }
    +    QhullFacet          topFacet() const { return QhullFacet(qh_qh, qh_ridge->top); }
    +
    +#//!\name foreach
    +    bool                hasNextRidge3d(const QhullFacet &f) const;
    +    QhullRidge          nextRidge3d(const QhullFacet &f) const { return nextRidge3d(f, 0); }
    +    QhullRidge          nextRidge3d(const QhullFacet &f, QhullVertex *nextVertex) const;
    +    QhullVertexSet      vertices() const { return QhullVertexSet(qh_qh, qh_ridge->vertices); }
    +
    +#//!\name IO
    +
    +    struct PrintRidge{
    +        const QhullRidge *ridge;
    +        const char *    print_message;    //!< non-null message
    +                        PrintRidge(const char *message, const QhullRidge &r) : ridge(&r), print_message(message) {}
    +    };//PrintRidge
    +    PrintRidge          print(const char* message) const { return PrintRidge(message, *this); }
    +};//class QhullRidge
    +
    +}//namespace orgQhull
    +
    +std::ostream &operator<<(std::ostream &os, const orgQhull::QhullRidge &r);
    +std::ostream &operator<<(std::ostream &os, const orgQhull::QhullRidge::PrintRidge &pr);
    +
    +#endif // QHULLRIDGE_H
    diff --git a/xs/src/qhull/src/libqhullcpp/QhullSet.cpp b/xs/src/qhull/src/libqhullcpp/QhullSet.cpp
    new file mode 100644
    index 000000000..dfdc3c51f
    --- /dev/null
    +++ b/xs/src/qhull/src/libqhullcpp/QhullSet.cpp
    @@ -0,0 +1,62 @@
    +/****************************************************************************
    +**
    +** Copyright (c) 2008-2015 C.B. Barber. All rights reserved.
    +** $Id: //main/2015/qhull/src/libqhullcpp/QhullSet.cpp#3 $$Change: 2066 $
    +** $DateTime: 2016/01/18 19:29:17 $$Author: bbarber $
    +**
    +****************************************************************************/
    +
    +#//! QhullSet -- Qhull's set structure, setT, as a C++ class
    +
    +#include "libqhullcpp/QhullSet.h"
    +
    +#include "libqhullcpp/Qhull.h"
    +#include "libqhullcpp/QhullError.h"
    +
    +#ifdef _MSC_VER  // Microsoft Visual C++ -- warning level 4
    +#endif
    +
    +namespace orgQhull {
    +
    +#//!\name Class objects
    +
    +setT QhullSetBase::
    +s_empty_set;
    +
    +#//!\name Constructors
    +
    +QhullSetBase::
    +QhullSetBase(const Qhull &q, setT *s) 
    +: qh_set(s ? s : &s_empty_set)
    +, qh_qh(q.qh())
    +{
    +}
    +
    +#//!\name Class methods
    +
    +// Same code for qh_setsize [qset_r.c] and QhullSetBase::count [static]
    +countT QhullSetBase::
    +count(const setT *set)
    +{
    +    countT size;
    +    const setelemT *sizep;
    +
    +    if (!set){
    +        return(0);
    +    }
    +    sizep= SETsizeaddr_(set);
    +    if ((size= sizep->i)) {
    +        size--;
    +        if (size > set->maxsize) {
    +            // FIXUP QH11022 How to add additional output to a error? -- qh_setprint(qhmem.ferr, "set: ", set);
    +            throw QhullError(10032, "QhullSet internal error: current set size %d is greater than maximum size %d\n",
    +                size, set->maxsize);
    +        }
    +    }else{
    +        size= set->maxsize;
    +    }
    +    return size;
    +}//count
    +
    +}//namespace orgQhull
    +
    diff --git a/xs/src/qhull/src/libqhullcpp/QhullSet.h b/xs/src/qhull/src/libqhullcpp/QhullSet.h
    new file mode 100644
    index 000000000..afb6b51d9
    --- /dev/null
    +++ b/xs/src/qhull/src/libqhullcpp/QhullSet.h
    @@ -0,0 +1,462 @@
    +/****************************************************************************
    +**
    +** Copyright (c) 2008-2015 C.B. Barber. All rights reserved.
    +** $Id: //main/2015/qhull/src/libqhullcpp/QhullSet.h#6 $$Change: 2079 $
    +** $DateTime: 2016/02/07 17:43:34 $$Author: bbarber $
    +**
    +****************************************************************************/
    +
    +#ifndef QhullSet_H
    +#define QhullSet_H
    +
    +#include "libqhull_r/qhull_ra.h"
    +#include "libqhullcpp/QhullError.h"
    +#include "libqhullcpp/QhullQh.h"
    +
    +#include   // ptrdiff_t, size_t
    +
    +#ifndef QHULL_NO_STL
    +#include 
    +#endif
    +
    +#ifdef QHULL_USES_QT
    + #include 
    +#endif
    +
    +namespace orgQhull {
    +
    +#//!\name Used here
    +    class Qhull;
    +
    +#//!\name Defined here
    +    class QhullSetBase;  //! Base class for QhullSet
    +    //! QhullSet defined below
    +    //! QhullSetIterator defined below
    +    //! \see QhullPointSet, QhullLinkedList
    +
    +//! QhullSetBase is a wrapper for Qhull's setT of void* pointers
    +//! \see libqhull_r/qset.h
    +class QhullSetBase {
    +
    +private:
    +#//!\name Fields --
    +    setT *              qh_set;
    +    QhullQh *           qh_qh;             //! Provides access to setT memory allocator
    +
    +#//!\name Class objects
    +    static setT         s_empty_set;  //! Used if setT* is NULL
    +
    +public:
    +#//!\name Constructors
    +                        QhullSetBase(const Qhull &q, setT *s);
    +                        QhullSetBase(QhullQh *qqh, setT *s) : qh_set(s ? s : &s_empty_set), qh_qh(qqh) {}
    +                        //! Copy constructor copies the pointer but not the set.  Needed for return by value and parameter passing.
    +                        QhullSetBase(const QhullSetBase &other) : qh_set(other.qh_set), qh_qh(other.qh_qh) {}
    +    QhullSetBase &      operator=(const QhullSetBase &other) { qh_set= other.qh_set; qh_qh= other.qh_qh; return *this; }
    +                        ~QhullSetBase() {}
    +
    +private:
    +                        //!disabled since memory allocation for QhullSet not defined
    +                        QhullSetBase() {}
    +public:
    +
    +#//!\name GetSet
    +    countT              count() const { return QhullSetBase::count(qh_set); }
    +    void                defineAs(setT *s) { qh_set= s ? s : &s_empty_set; } //!< Not type-safe since setT may contain any type
    +    void                forceEmpty() { qh_set= &s_empty_set; }
    +    setT *              getSetT() const { return qh_set; }
    +    bool                isEmpty() const { return SETempty_(qh_set); }
    +    QhullQh *           qh() const { return qh_qh; }
    +    setT **             referenceSetT() { return &qh_set; }
    +    size_t              size() const { return QhullSetBase::count(qh_set); }
    +
    +#//!\name Element
    +protected:
    +    void **             beginPointer() const { return &qh_set->e[0].p; }
    +    void **             elementPointer(countT idx) const { QHULL_ASSERT(idx>=0 && idxmaxsize); return &SETelem_(qh_set, idx); }
    +                        //! Always points to 0
    +    void **             endPointer() const { return qh_setendpointer(qh_set); }
    +
    +#//!\name Class methods
    +public:
    +    static countT       count(const setT *set);
    +    //s may be null
    +    static bool         isEmpty(const setT *s) { return SETempty_(s); }
    +};//QhullSetBase
    +
    +
    +//! QhullSet -- A read-only wrapper to Qhull's collection class, setT.
    +//!  QhullSet is similar to STL's  and Qt's QVector
    +//!  QhullSet is unrelated to STL and Qt's set and map types (e.g., QSet and QMap)
    +//!  T is a Qhull type that defines 'base_type' and getBaseT() (e.g., QhullFacet with base_type 'facetT *'
    +//!  A QhullSet does not own its contents -- erase(), clear(), removeFirst(), removeLast(), pop_back(), pop_front(), fromStdList() not defined
    +//!  QhullSetIterator is faster than STL-style iterator/const_iterator
    +//!  Qhull's FOREACHelement_() [qset_r.h] maybe more efficient than QhullSet.  It uses a NULL terminator instead of an end pointer.  STL requires an end pointer.
    +//!  Derived from QhullLinkedList.h and Qt/core/tools/qlist.h w/o QT_STRICT_ITERATORS
    +template 
    +class QhullSet : public QhullSetBase {
    +
    +private:
    +#//!\name Fields -- see QhullSetBase
    +
    +#//!\name Class objects
    +    static setT         s_empty_set;  //! Workaround for no setT allocator.  Used if setT* is NULL
    +
    +public:
    +#//!\name Defined here
    +    class iterator;
    +    class const_iterator;
    +    typedef typename QhullSet::iterator Iterator;
    +    typedef typename QhullSet::const_iterator ConstIterator;
    +
    +#//!\name Constructors
    +                        QhullSet(const Qhull &q, setT *s) : QhullSetBase(q, s) { }
    +                        QhullSet(QhullQh *qqh, setT *s) : QhullSetBase(qqh, s) { }
    +                        //Conversion from setT* is not type-safe.  Implicit conversion for void* to T
    +                        //Copy constructor copies pointer but not contents.  Needed for return by value.
    +                        QhullSet(const QhullSet &other) : QhullSetBase(other) {}
    +    QhullSet &       operator=(const QhullSet &other) { QhullSetBase::operator=(other); return *this; }
    +                        ~QhullSet() {}
    +
    +private:
    +                        //!Disable default constructor.  See QhullSetBase
    +                        QhullSet();
    +public:
    +
    +#//!\name Conversion
    +
    +#ifndef QHULL_NO_STL
    +    std::vector toStdVector() const;
    +#endif
    +#ifdef QHULL_USES_QT
    +    QList toQList() const;
    +#endif
    +
    +#//!\name GetSet -- see QhullSetBase for count(), empty(), isEmpty(), size()
    +    using QhullSetBase::count;
    +    using QhullSetBase::isEmpty;
    +    // operator== defined for QhullSets of the same type
    +    bool                operator==(const QhullSet &other) const { return qh_setequal(getSetT(), other.getSetT()); }
    +    bool                operator!=(const QhullSet &other) const { return !operator==(other); }
    +
    +#//!\name Element access
    +    // Constructs T.  Cannot return reference.
    +    const T             at(countT idx) const { return operator[](idx); }
    +    // Constructs T.  Cannot return reference.
    +    const T             back() const { return last(); }
    +    T                   back() { return last(); }
    +    //! end element is NULL
    +    const typename T::base_type * constData() const { return reinterpret_cast(beginPointer()); }
    +    typename T::base_type *     data() { return reinterpret_cast(beginPointer()); }
    +    const typename T::base_type *data() const { return reinterpret_cast(beginPointer()); }
    +    typename T::base_type *     endData() { return reinterpret_cast(endPointer()); }
    +    const typename T::base_type * endData() const { return reinterpret_cast(endPointer()); }
    +    // Constructs T.  Cannot return reference.
    +    const T             first() const { QHULL_ASSERT(!isEmpty()); return T(qh(), *data()); }
    +    T                   first() { QHULL_ASSERT(!isEmpty()); return T(qh(), *data()); }
    +    // Constructs T.  Cannot return reference.
    +    const T             front() const { return first(); }
    +    T                   front() { return first(); }
    +    // Constructs T.  Cannot return reference.
    +    const T             last() const { QHULL_ASSERT(!isEmpty()); return T(qh(), *(endData()-1)); }
    +    T                   last() { QHULL_ASSERT(!isEmpty()); return T(qh(), *(endData()-1)); }
    +    // mid() not available.  No setT constructor
    +    // Constructs T.  Cannot return reference.
    +    const T             operator[](countT idx) const { const typename T::base_type *p= reinterpret_cast(elementPointer(idx)); QHULL_ASSERT(idx>=0 && p < endData()); return T(qh(), *p); }
    +    T                   operator[](countT idx) { typename T::base_type *p= reinterpret_cast(elementPointer(idx)); QHULL_ASSERT(idx>=0 && p < endData()); return T(qh(), *p); }
    +    const T             second() const { return operator[](1); }
    +    T                   second() { return operator[](1); }
    +    T                   value(countT idx) const;
    +    T                   value(countT idx, const T &defaultValue) const;
    +
    +#//!\name Read-write -- Not available, no setT constructor
    +
    +#//!\name iterator
    +    iterator            begin() { return iterator(qh(), reinterpret_cast(beginPointer())); }
    +    const_iterator      begin() const { return const_iterator(qh(), data()); }
    +    const_iterator      constBegin() const { return const_iterator(qh(), data()); }
    +    const_iterator      constEnd() const { return const_iterator(qh(), endData()); }
    +    iterator            end() { return iterator(qh(), endData()); }
    +    const_iterator      end() const { return const_iterator(qh(), endData()); }
    +
    +#//!\name Search
    +    bool                contains(const T &t) const;
    +    countT              count(const T &t) const;
    +    countT              indexOf(const T &t) const { /* no qh_qh */ return qh_setindex(getSetT(), t.getBaseT()); }
    +    countT              lastIndexOf(const T &t) const;
    +
    +    // before const_iterator for conversion with comparison operators
    +    class iterator {
    +        friend class const_iterator;
    +    private:
    +        typename T::base_type *  i;  // e.g., facetT**, first for debugger
    +        QhullQh *       qh_qh;
    +
    +    public:
    +        typedef ptrdiff_t       difference_type;
    +        typedef std::bidirectional_iterator_tag  iterator_category;
    +        typedef T               value_type;
    +
    +                        iterator(QhullQh *qqh, typename T::base_type *p) : i(p), qh_qh(qqh) {}
    +                        iterator(const iterator &o) : i(o.i), qh_qh(o.qh_qh) {}
    +        iterator &      operator=(const iterator &o) { i= o.i; qh_qh= o.qh_qh; return *this; }
    +
    +        // Constructs T.  Cannot return reference.  
    +        T               operator*() const { return T(qh_qh, *i); }
    +        //operator->() n/a, value-type
    +        // Constructs T.  Cannot return reference.  
    +        T               operator[](countT idx) const { return T(qh_qh, *(i+idx)); } //!< No error checking
    +        bool            operator==(const iterator &o) const { return i == o.i; }
    +        bool            operator!=(const iterator &o) const { return !operator==(o); }
    +        bool            operator==(const const_iterator &o) const { return (i==reinterpret_cast(o).i); }
    +        bool            operator!=(const const_iterator &o) const { return !operator==(o); }
    +
    +        //! Assumes same point set
    +        countT          operator-(const iterator &o) const { return (countT)(i-o.i); } //WARN64
    +        bool            operator>(const iterator &o) const { return i>o.i; }
    +        bool            operator<=(const iterator &o) const { return !operator>(o); }
    +        bool            operator<(const iterator &o) const { return i=(const iterator &o) const { return !operator<(o); }
    +        bool            operator>(const const_iterator &o) const { return (i > reinterpret_cast(o).i); }
    +        bool            operator<=(const const_iterator &o) const { return !operator>(o); }
    +        bool            operator<(const const_iterator &o) const { return (i < reinterpret_cast(o).i); }
    +        bool            operator>=(const const_iterator &o) const { return !operator<(o); }
    +
    +        //! No error checking
    +        iterator &      operator++() { ++i; return *this; }
    +        iterator        operator++(int) { iterator o= *this; ++i; return o; }
    +        iterator &      operator--() { --i; return *this; }
    +        iterator        operator--(int) { iterator o= *this; --i; return o; }
    +        iterator        operator+(countT j) const { return iterator(qh_qh, i+j); }
    +        iterator        operator-(countT j) const { return operator+(-j); }
    +        iterator &      operator+=(countT j) { i += j; return *this; }
    +        iterator &      operator-=(countT j) { i -= j; return *this; }
    +    };//QhullPointSet::iterator
    +
    +    class const_iterator {
    +    private:
    +        const typename T::base_type *  i;  // e.g., const facetT**, first for debugger
    +        QhullQh *       qh_qh;
    +
    +    public:
    +        typedef ptrdiff_t       difference_type;
    +        typedef std::random_access_iterator_tag  iterator_category;
    +        typedef T               value_type;
    +
    +                        const_iterator(QhullQh *qqh, const typename T::base_type * p) : i(p), qh_qh(qqh) {}
    +                        const_iterator(const const_iterator &o) : i(o.i), qh_qh(o.qh_qh) {}
    +                        const_iterator(const iterator &o) : i(o.i), qh_qh(o.qh_qh) {}
    +        const_iterator &operator=(const const_iterator &o) { i= o.i; qh_qh= o.qh_qh; return *this; }
    +
    +        // Constructs T.  Cannot return reference.  Retaining 'const T' return type for consistency with QList/QVector
    +        const T         operator*() const { return T(qh_qh, *i); }
    +        const T         operator[](countT idx) const { return T(qh_qh, *(i+idx)); }  //!< No error checking
    +        //operator->() n/a, value-type
    +        bool            operator==(const const_iterator &o) const { return i == o.i; }
    +        bool            operator!=(const const_iterator &o) const { return !operator==(o); }
    +
    +        //! Assumes same point set
    +        countT          operator-(const const_iterator &o) { return (countT)(i-o.i); } //WARN64
    +        bool            operator>(const const_iterator &o) const { return i>o.i; }
    +        bool            operator<=(const const_iterator &o) const { return !operator>(o); }
    +        bool            operator<(const const_iterator &o) const { return i=(const const_iterator &o) const { return !operator<(o); }
    +
    +        //!< No error checking
    +        const_iterator &operator++() { ++i; return *this; }
    +        const_iterator  operator++(int) { const_iterator o= *this; ++i; return o; }
    +        const_iterator &operator--() { --i; return *this; }
    +        const_iterator  operator--(int) { const_iterator o= *this; --i; return o; }
    +        const_iterator  operator+(int j) const { return const_iterator(qh_qh, i+j); }
    +        const_iterator  operator-(int j) const { return operator+(-j); }
    +        const_iterator &operator+=(int j) { i += j; return *this; }
    +        const_iterator &operator-=(int j) { i -= j; return *this; }
    +    };//QhullPointSet::const_iterator
    +
    +};//class QhullSet
    +
    +
    +//! Faster then interator/const_iterator due to T::base_type
    +template 
    +class QhullSetIterator {
    +
    +#//!\name Subtypes
    +    typedef typename QhullSet::const_iterator const_iterator;
    +
    +private:
    +#//!\name Fields
    +    const typename T::base_type *  i;  // e.g., facetT**, first for debugger
    +    const typename T::base_type *  begin_i;  // must be initialized after i
    +    const typename T::base_type *  end_i;
    +    QhullQh *                qh_qh;
    +
    +public:
    +#//!\name Constructors
    +                        QhullSetIterator(const QhullSet &s) : i(s.data()), begin_i(i), end_i(s.endData()), qh_qh(s.qh()) {}
    +                        QhullSetIterator(const QhullSetIterator &o) : i(o.i), begin_i(o.begin_i), end_i(o.end_i), qh_qh(o.qh_qh) {}
    +    QhullSetIterator &operator=(const QhullSetIterator &o) { i= o.i; begin_i= o.begin_i; end_i= o.end_i; qh_qh= o.qh_qh; return *this; }
    +
    +#//!\name ReadOnly
    +    countT              countRemaining() { return (countT)(end_i-i); } // WARN64
    +
    +#//!\name Search
    +    bool                findNext(const T &t);
    +    bool                findPrevious(const T &t);
    +
    +#//!\name Foreach
    +    bool                hasNext() const { return i != end_i; }
    +    bool                hasPrevious() const { return i != begin_i; }
    +    T                   next() { return T(qh_qh, *i++); }
    +    T                   peekNext() const { return T(qh_qh, *i); }
    +    T                   peekPrevious() const { const typename T::base_type *p = i; return T(qh_qh, *--p); }
    +    T                   previous() { return T(qh_qh, *--i); }
    +    void                toBack() { i = end_i; }
    +    void                toFront() { i = begin_i; }
    +};//class QhullSetIterator
    +
    +#//!\name == Definitions =========================================
    +
    +#//!\name Conversions
    +
    +// See qt-qhull.cpp for QList conversion
    +
    +#ifndef QHULL_NO_STL
    +template 
    +std::vector QhullSet::
    +toStdVector() const
    +{
    +	typename QhullSet::const_iterator i = begin();
    +	typename QhullSet::const_iterator e = end();
    +    std::vector vs;
    +    while(i!=e){
    +        vs.push_back(*i++);
    +    }
    +    return vs;
    +}//toStdVector
    +#endif //QHULL_NO_STL
    +
    +#ifdef QHULL_USES_QT
    +template 
    +QList QhullSet::
    +toQList() const
    +{
    +    QhullSet::const_iterator i= begin();
    +    QhullSet::const_iterator e= end();
    +    QList vs;
    +    while(i!=e){
    +        vs.append(*i++);
    +    }
    +    return vs;
    +}//toQList
    +#endif
    +
    +#//!\name Element
    +
    +template 
    +T QhullSet::
    +value(countT idx) const
    +{
    +    // Avoid call to qh_setsize() and assert in elementPointer()
    +    const typename T::base_type *p= reinterpret_cast(&SETelem_(getSetT(), idx));
    +    return (idx>=0 && p
    +T QhullSet::
    +value(countT idx, const T &defaultValue) const
    +{
    +    // Avoid call to qh_setsize() and assert in elementPointer()
    +    const typename T::base_type *p= reinterpret_cast(&SETelem_(getSetT(), idx));
    +    return (idx>=0 && p
    +bool QhullSet::
    +contains(const T &t) const
    +{
    +    setT *s= getSetT();
    +    void *p= t.getBaseT();  // contains() is not inline for better error reporting
    +    int result= qh_setin(s, p);
    +    return result!=0;
    +}//contains
    +
    +template 
    +countT QhullSet::
    +count(const T &t) const
    +{
    +    countT n= 0;
    +    const typename T::base_type *i= data();
    +    const typename T::base_type *e= endData();
    +    typename T::base_type p= t.getBaseT();
    +    while(i
    +countT QhullSet::
    +lastIndexOf(const T &t) const
    +{
    +    const typename T::base_type *b= data();
    +    const typename T::base_type *i= endData();
    +    typename T::base_type p= t.getBaseT();
    +    while(--i>=b){
    +        if(*i==p){
    +            break;
    +        }
    +    }
    +    return (countT)(i-b); // WARN64
    +}//lastIndexOf
    +
    +#//!\name QhullSetIterator
    +
    +template 
    +bool QhullSetIterator::
    +findNext(const T &t)
    +{
    +    typename T::base_type p= t.getBaseT();
    +    while(i!=end_i){
    +        if(*(++i)==p){
    +            return true;
    +        }
    +    }
    +    return false;
    +}//findNext
    +
    +template 
    +bool QhullSetIterator::
    +findPrevious(const T &t)
    +{
    +    typename T::base_type p= t.getBaseT();
    +    while(i!=begin_i){
    +        if(*(--i)==p){
    +            return true;
    +        }
    +    }
    +    return false;
    +}//findPrevious
    +
    +}//namespace orgQhull
    +
    +
    +#//!\name == Global namespace =========================================
    +
    +template 
    +std::ostream &
    +operator<<(std::ostream &os, const orgQhull::QhullSet &qs)
    +{
    +    const typename T::base_type *i= qs.data();
    +    const typename T::base_type *e= qs.endData();
    +    while(i!=e){
    +        os << T(qs.qh(), *i++);
    +    }
    +    return os;
    +}//operator<<
    +
    +#endif // QhullSet_H
    diff --git a/xs/src/qhull/src/libqhullcpp/QhullSets.h b/xs/src/qhull/src/libqhullcpp/QhullSets.h
    new file mode 100644
    index 000000000..d0f200cbc
    --- /dev/null
    +++ b/xs/src/qhull/src/libqhullcpp/QhullSets.h
    @@ -0,0 +1,27 @@
    +/****************************************************************************
    +**
    +** Copyright (c) 2008-2015 C.B. Barber. All rights reserved.
    +** $Id: //main/2015/qhull/src/libqhullcpp/QhullSets.h#2 $$Change: 2066 $
    +** $DateTime: 2016/01/18 19:29:17 $$Author: bbarber $
    +**
    +****************************************************************************/
    +
    +#ifndef QHULLSETS_H
    +#define QHULLSETS_H
    +
    +#include "libqhullcpp/QhullSet.h"
    +
    +namespace orgQhull {
    +
    +    //See: QhullFacetSet.h
    +    //See: QhullPointSet.h
    +    //See: QhullVertexSet.h
    +
    +    // Avoid circular references between QhullFacet, QhullRidge, and QhullVertex
    +    class QhullRidge;
    +    typedef QhullSet  QhullRidgeSet;
    +    typedef QhullSetIterator  QhullRidgeSetIterator;
    +
    +}//namespace orgQhull
    +
    +#endif // QHULLSETS_H
    diff --git a/xs/src/qhull/src/libqhullcpp/QhullStat.cpp b/xs/src/qhull/src/libqhullcpp/QhullStat.cpp
    new file mode 100644
    index 000000000..c4fe6c491
    --- /dev/null
    +++ b/xs/src/qhull/src/libqhullcpp/QhullStat.cpp
    @@ -0,0 +1,42 @@
    +/****************************************************************************
    +**
    +** Copyright (c) 2008-2015 C.B. Barber. All rights reserved.
    +** $Id: //main/2015/qhull/src/libqhullcpp/QhullStat.cpp#3 $$Change: 2066 $
    +** $DateTime: 2016/01/18 19:29:17 $$Author: bbarber $
    +**
    +****************************************************************************/
    +
    +#//! QhullStat -- Qhull's global data structure, statT, as a C++ class
    +
    +#include "libqhullcpp/QhullStat.h"
    +
    +#include "libqhullcpp/QhullError.h"
    +
    +#include 
    +#include 
    +
    +using std::cerr;
    +using std::string;
    +using std::vector;
    +using std::ostream;
    +
    +#ifdef _MSC_VER  // Microsoft Visual C++ -- warning level 4
    +#endif
    +
    +namespace orgQhull {
    +
    +#//!\name Constructor, destructor, etc.
    +
    +//! If qh_QHpointer==0, invoke with placement new on qh_stat;
    +QhullStat::
    +QhullStat()
    +{
    +}//QhullStat
    +
    +QhullStat::
    +~QhullStat()
    +{
    +}//~QhullStat
    +
    +}//namespace orgQhull
    +
    diff --git a/xs/src/qhull/src/libqhullcpp/QhullStat.h b/xs/src/qhull/src/libqhullcpp/QhullStat.h
    new file mode 100644
    index 000000000..54bde8fc7
    --- /dev/null
    +++ b/xs/src/qhull/src/libqhullcpp/QhullStat.h
    @@ -0,0 +1,49 @@
    +/****************************************************************************
    +**
    +** Copyright (c) 2008-2015 C.B. Barber. All rights reserved.
    +** $Id: //main/2015/qhull/src/libqhullcpp/QhullStat.h#2 $$Change: 2079 $
    +** $DateTime: 2016/02/07 17:43:34 $$Author: bbarber $
    +**
    +****************************************************************************/
    +
    +#ifndef QHULLSTAT_H
    +#define QHULLSTAT_H
    +
    +#include "libqhull_r/qhull_ra.h"
    +
    +#include 
    +#include 
    +
    +namespace orgQhull {
    +
    +#//!\name defined here
    +    //! QhullStat -- Qhull's statistics, qhstatT, as a C++ class
    +    //! Statistics defined with zzdef_() control Qhull's behavior, summarize its result, and report precision problems.
    +    class QhullStat;
    +
    +class QhullStat : public qhstatT {
    +
    +private:
    +#//!\name Fields (empty) -- POD type equivalent to qhstatT.  No data or virtual members
    +
    +public:
    +#//!\name Constants
    +
    +#//!\name class methods
    +
    +#//!\name constructor, assignment, destructor, invariant
    +                        QhullStat();
    +                        ~QhullStat();
    +
    +private:
    +    //!disable copy constructor and assignment
    +                        QhullStat(const QhullStat &);
    +    QhullStat &         operator=(const QhullStat &);
    +public:
    +
    +#//!\name Access
    +};//class QhullStat
    +
    +}//namespace orgQhull
    +
    +#endif // QHULLSTAT_H
    diff --git a/xs/src/qhull/src/libqhullcpp/QhullVertex.cpp b/xs/src/qhull/src/libqhullcpp/QhullVertex.cpp
    new file mode 100644
    index 000000000..fd7aef089
    --- /dev/null
    +++ b/xs/src/qhull/src/libqhullcpp/QhullVertex.cpp
    @@ -0,0 +1,112 @@
    +/****************************************************************************
    +**
    +** Copyright (c) 2008-2015 C.B. Barber. All rights reserved.
    +** $Id: //main/2015/qhull/src/libqhullcpp/QhullVertex.cpp#3 $$Change: 2066 $
    +** $DateTime: 2016/01/18 19:29:17 $$Author: bbarber $
    +**
    +****************************************************************************/
    +
    +#//! QhullVertex -- Qhull's vertex structure, vertexT, as a C++ class
    +
    +#include "libqhullcpp/QhullVertex.h"
    +
    +#include "libqhullcpp/Qhull.h"
    +#include "libqhullcpp/QhullPoint.h"
    +#include "libqhullcpp/QhullFacetSet.h"
    +#include "libqhullcpp/QhullFacet.h"
    +
    +#ifdef _MSC_VER  // Microsoft Visual C++ -- warning level 4
    +#pragma warning( disable : 4611)  // interaction between '_setjmp' and C++ object destruction is non-portable
    +#pragma warning( disable : 4996)  // function was declared deprecated(strcpy, localtime, etc.)
    +#endif
    +
    +namespace orgQhull {
    +
    +#//!\name Class objects
    +vertexT QhullVertex::
    +s_empty_vertex= {0,0,0,0,0,
    +                 0,0,0,0,0,
    +                 0};
    +
    +#//!\name Constructors
    +
    +QhullVertex::QhullVertex(const Qhull &q)
    +: qh_vertex(&s_empty_vertex)
    +, qh_qh(q.qh())
    +{
    +}//Default
    +
    +QhullVertex::QhullVertex(const Qhull &q, vertexT *v)
    +: qh_vertex(v ? v : &s_empty_vertex)
    +, qh_qh(q.qh())
    +{
    +}//vertexT
    +
    +#//!\name foreach
    +
    +//! Return neighboring facets for a vertex
    +//! If neither merging nor Voronoi diagram, requires Qhull::defineVertexNeighborFacets() beforehand.
    +QhullFacetSet QhullVertex::
    +neighborFacets() const
    +{
    +    if(!neighborFacetsDefined()){
    +        throw QhullError(10034, "Qhull error: neighboring facets of vertex %d not defined.  Please call Qhull::defineVertexNeighborFacets() beforehand.", id());
    +    }
    +    return QhullFacetSet(qh_qh, qh_vertex->neighbors);
    +}//neighborFacets
    +
    +}//namespace orgQhull
    +
    +#//!\name Global functions
    +
    +using std::endl;
    +using std::ostream;
    +using std::string;
    +using std::vector;
    +using orgQhull::QhullPoint;
    +using orgQhull::QhullFacet;
    +using orgQhull::QhullFacetSet;
    +using orgQhull::QhullFacetSetIterator;
    +using orgQhull::QhullVertex;
    +
    +//! Duplicate of qh_printvertex [io_r.c]
    +ostream &
    +operator<<(ostream &os, const QhullVertex::PrintVertex &pr)
    +{
    +    QhullVertex v= *pr.vertex;
    +    QhullPoint p= v.point();
    +    if(*pr.print_message){
    +        os << pr.print_message << " ";
    +    }else{
    +        os << "- ";
    +    }
    +    os << "p" << p.id() << " (v" << v.id() << "): ";
    +    const realT *c= p.coordinates();
    +    for(int k= p.dimension(); k--; ){
    +        os << " " << *c++; // FIXUP QH11010 %5.2g
    +    }
    +    if(v.getVertexT()->deleted){
    +        os << " deleted";
    +    }
    +    if(v.getVertexT()->delridge){
    +        os << " ridgedeleted";
    +    }
    +    os << endl;
    +    if(v.neighborFacetsDefined()){
    +        QhullFacetSetIterator i= v.neighborFacets();
    +        if(i.hasNext()){
    +            os << " neighborFacets:";
    +            countT count= 0;
    +            while(i.hasNext()){
    +                if(++count % 100 == 0){
    +                    os << endl << "     ";
    +                }
    +                QhullFacet f= i.next();
    +                os << " f" << f.id();
    +            }
    +            os << endl;
    +        }
    +    }
    +    return os;
    +}//<< PrintVertex
    +
    diff --git a/xs/src/qhull/src/libqhullcpp/QhullVertex.h b/xs/src/qhull/src/libqhullcpp/QhullVertex.h
    new file mode 100644
    index 000000000..0137766db
    --- /dev/null
    +++ b/xs/src/qhull/src/libqhullcpp/QhullVertex.h
    @@ -0,0 +1,104 @@
    +/****************************************************************************
    +**
    +** Copyright (c) 2008-2015 C.B. Barber. All rights reserved.
    +** $Id: //main/2015/qhull/src/libqhullcpp/QhullVertex.h#4 $$Change: 2079 $
    +** $DateTime: 2016/02/07 17:43:34 $$Author: bbarber $
    +**
    +****************************************************************************/
    +
    +#ifndef QHULLVERTEX_H
    +#define QHULLVERTEX_H
    +
    +#include "libqhull_r/qhull_ra.h"
    +#include "libqhullcpp/QhullPoint.h"
    +#include "libqhullcpp/QhullLinkedList.h"
    +#include "libqhullcpp/QhullSet.h"
    +
    +#include 
    +
    +namespace orgQhull {
    +
    +#//!\name Used here
    +    class QhullFacetSet;
    +
    +#//!\name Defined here
    +    //! QhullVertex -- Qhull's vertex structure, vertexT [libqhull_r.h], as a C++ class
    +    class QhullVertex;
    +    typedef QhullLinkedList QhullVertexList;
    +    typedef QhullLinkedListIterator QhullVertexListIterator;
    +
    +
    +/*********************
    +  topological information:
    +    next,previous       doubly-linked list of all vertices
    +    neighborFacets           set of adjacent facets (only if qh.VERTEXneighbors)
    +
    +  geometric information:
    +    point               array of DIM coordinates
    +*/
    +
    +class QhullVertex {
    +
    +#//!\name Defined here
    +public:
    +    typedef vertexT *   base_type;  // for QhullVertexSet
    +
    +private:
    +#//!\name Fields
    +    vertexT *           qh_vertex;  //!< Corresponding vertexT, never 0
    +    QhullQh *           qh_qh;      //!< QhullQh/qhT for vertexT, may be 0
    +
    +#//!\name Class objects
    +    static vertexT      s_empty_vertex;  // needed for shallow copy
    +
    +public:
    +#//!\name Constants
    +
    +#//!\name Constructors
    +                        QhullVertex() : qh_vertex(&s_empty_vertex), qh_qh(0) {}
    +    explicit            QhullVertex(const Qhull &q);
    +                        QhullVertex(const Qhull &q, vertexT *v);
    +    explicit            QhullVertex(QhullQh *qqh) : qh_vertex(&s_empty_vertex), qh_qh(qqh) {}
    +                        QhullVertex(QhullQh *qqh, vertexT *v) : qh_vertex(v ? v : &s_empty_vertex), qh_qh(qqh) {}
    +                        // Creates an alias.  Does not copy QhullVertex.  Needed for return by value and parameter passing
    +                        QhullVertex(const QhullVertex &other) : qh_vertex(other.qh_vertex), qh_qh(other.qh_qh) {}
    +                        // Creates an alias.  Does not copy QhullVertex.  Needed for vector
    +    QhullVertex &       operator=(const QhullVertex &other) { qh_vertex= other.qh_vertex; qh_qh= other.qh_qh; return *this; }
    +                        ~QhullVertex() {}
    +
    +#//!\name GetSet
    +    int                 dimension() const { return (qh_qh ? qh_qh->hull_dim : 0); }
    +    vertexT *           getBaseT() const { return getVertexT(); } //!< For QhullSet
    +    vertexT *           getVertexT() const { return qh_vertex; }
    +    countT              id() const { return qh_vertex->id; }
    +    bool                isValid() const { return (qh_qh && qh_vertex != &s_empty_vertex); }
    +                        //! True if defineVertexNeighborFacets() already called.  Auotomatically set for facet merging, Voronoi diagrams
    +    bool                neighborFacetsDefined() const { return qh_vertex->neighbors != 0; }
    +    QhullVertex         next() const { return QhullVertex(qh_qh, qh_vertex->next); }
    +    bool                operator==(const QhullVertex &other) const { return qh_vertex==other.qh_vertex; }
    +    bool                operator!=(const QhullVertex &other) const { return !operator==(other); }
    +    QhullPoint          point() const { return QhullPoint(qh_qh, qh_vertex->point); }
    +    QhullVertex         previous() const { return QhullVertex(qh_qh, qh_vertex->previous); }
    +    QhullQh *           qh() const { return qh_qh; }
    +
    +#//!\name foreach
    +    //See also QhullVertexList
    +    QhullFacetSet       neighborFacets() const;
    +
    +#//!\name IO
    +    struct PrintVertex{
    +        const QhullVertex *vertex;
    +        const char *    print_message;    //!< non-null message
    +                        PrintVertex(const char *message, const QhullVertex &v) : vertex(&v), print_message(message) {}
    +    };//PrintVertex
    +    PrintVertex         print(const char *message) const { return PrintVertex(message, *this); }
    +};//class QhullVertex
    +
    +}//namespace orgQhull
    +
    +#//!\name GLobal
    +
    +std::ostream &operator<<(std::ostream &os, const orgQhull::QhullVertex::PrintVertex &pr);
    +inline std::ostream &operator<<(std::ostream &os, const orgQhull::QhullVertex &v) { os << v.print(""); return os; }
    +
    +#endif // QHULLVERTEX_H
    diff --git a/xs/src/qhull/src/libqhullcpp/QhullVertexSet.cpp b/xs/src/qhull/src/libqhullcpp/QhullVertexSet.cpp
    new file mode 100644
    index 000000000..00ba62d19
    --- /dev/null
    +++ b/xs/src/qhull/src/libqhullcpp/QhullVertexSet.cpp
    @@ -0,0 +1,161 @@
    +/****************************************************************************
    +**
    +** Copyright (c) 2009-2015 C.B. Barber. All rights reserved.
    +** $Id: //main/2015/qhull/src/libqhullcpp/QhullVertexSet.cpp#3 $$Change: 2066 $
    +** $DateTime: 2016/01/18 19:29:17 $$Author: bbarber $
    +**
    +****************************************************************************/
    +
    +#//! QhullVertexSet -- Qhull's linked Vertexs, as a C++ class
    +
    +#include "libqhullcpp/QhullVertexSet.h"
    +
    +#include "libqhullcpp/QhullVertex.h"
    +#include "libqhullcpp/QhullPoint.h"
    +#include "libqhullcpp/QhullRidge.h"
    +#include "libqhullcpp/QhullVertex.h"
    +#include "libqhullcpp/Qhull.h"
    +
    +using std::string;
    +using std::vector;
    +
    +#ifdef _MSC_VER  // Microsoft Visual C++ -- warning level 4
    +#pragma warning( disable : 4611)  /* interaction between '_setjmp' and C++ object destruction is non-portable */
    +                                    /* setjmp should not be implemented with 'catch' */
    +#endif
    +
    +namespace orgQhull {
    +
    +QhullVertexSet::
    +QhullVertexSet(const Qhull &q, facetT *facetlist, setT *facetset, bool allfacets)
    +: QhullSet(q.qh(), 0)
    +, qhsettemp_defined(false)
    +{
    +    QH_TRY_(q.qh()){ // no object creation -- destructors skipped on longjmp()
    +        setT *vertices= qh_facetvertices(q.qh(), facetlist, facetset, allfacets);
    +        defineAs(vertices);
    +        qhsettemp_defined= true;
    +    }
    +    q.qh()->NOerrexit= true;
    +    q.qh()->maybeThrowQhullMessage(QH_TRY_status);
    +}//QhullVertexSet facetlist facetset
    +
    +//! Return tempory QhullVertexSet of vertices for a list and/or a set of facets
    +//! Sets qhsettemp_defined (disallows copy constructor and assignment to prevent double-free)
    +QhullVertexSet::
    +QhullVertexSet(QhullQh *qqh, facetT *facetlist, setT *facetset, bool allfacets)
    +: QhullSet(qqh, 0)
    +, qhsettemp_defined(false)
    +{
    +    QH_TRY_(qh()){ // no object creation -- destructors skipped on longjmp()
    +        setT *vertices= qh_facetvertices(qh(), facetlist, facetset, allfacets);
    +        defineAs(vertices);
    +        qhsettemp_defined= true;
    +    }
    +    qh()->NOerrexit= true;
    +    qh()->maybeThrowQhullMessage(QH_TRY_status);
    +}//QhullVertexSet facetlist facetset
    +
    +//! Copy constructor for argument passing and returning a result
    +//! Only copies a pointer to the set.
    +//! Throws an error if qhsettemp_defined, otherwise have a double-free
    +//!\todo Convert QhullVertexSet to a shared pointer with reference counting
    +QhullVertexSet::
    +QhullVertexSet(const QhullVertexSet &other)
    +: QhullSet(other)
    +, qhsettemp_defined(false)
    +{
    +    if(other.qhsettemp_defined){
    +        throw QhullError(10077, "QhullVertexSet: Cannot use copy constructor since qhsettemp_defined (e.g., QhullVertexSet for a set and/or list of QhFacet).  Contains %d vertices", other.count());
    +    }
    +}//copy constructor
    +
    +//! Copy assignment only copies a pointer to the set.
    +//! Throws an error if qhsettemp_defined, otherwise have a double-free
    +QhullVertexSet & QhullVertexSet::
    +operator=(const QhullVertexSet &other)
    +{
    +    QhullSet::operator=(other);
    +    qhsettemp_defined= false;
    +    if(other.qhsettemp_defined){
    +        throw QhullError(10078, "QhullVertexSet: Cannot use copy constructor since qhsettemp_defined (e.g., QhullVertexSet for a set and/or list of QhFacet).  Contains %d vertices", other.count());
    +    }
    +    return *this;
    +}//assignment
    +
    +void QhullVertexSet::
    +freeQhSetTemp()
    +{
    +    if(qhsettemp_defined){
    +        qhsettemp_defined= false;
    +        QH_TRY_(qh()){ // no object creation -- destructors skipped on longjmp()
    +            qh_settempfree(qh(), referenceSetT()); // errors if not top of tempstack or if qhmem corrupted
    +        }
    +        qh()->NOerrexit= true;
    +        qh()->maybeThrowQhullMessage(QH_TRY_status, QhullError::NOthrow);
    +    }
    +}//freeQhSetTemp
    +
    +QhullVertexSet::
    +~QhullVertexSet()
    +{
    +    freeQhSetTemp();
    +}//~QhullVertexSet
    +
    +//FIXUP -- Move conditional, QhullVertexSet code to QhullVertexSet.cpp
    +#ifndef QHULL_NO_STL
    +std::vector QhullVertexSet::
    +toStdVector() const
    +{
    +    QhullSetIterator i(*this);
    +    std::vector vs;
    +    while(i.hasNext()){
    +        QhullVertex v= i.next();
    +        vs.push_back(v);
    +    }
    +    return vs;
    +}//toStdVector
    +#endif //QHULL_NO_STL
    +
    +}//namespace orgQhull
    +
    +#//!\name Global functions
    +
    +using std::endl;
    +using std::ostream;
    +using orgQhull::QhullPoint;
    +using orgQhull::QhullVertex;
    +using orgQhull::QhullVertexSet;
    +using orgQhull::QhullVertexSetIterator;
    +
    +//! Print Vertex identifiers to stream.  Space prefix.  From qh_printVertexheader [io_r.c]
    +ostream &
    +operator<<(ostream &os, const QhullVertexSet::PrintIdentifiers &pr)
    +{
    +    os << pr.print_message;
    +    for(QhullVertexSet::const_iterator i= pr.vertex_set->begin(); i!=pr.vertex_set->end(); ++i){
    +        const QhullVertex v= *i;
    +        os << " v" << v.id();
    +    }
    +    os << endl;
    +    return os;
    +}//<
    +
    +namespace orgQhull {
    +
    +#//!\name Used here
    +
    +#//!\name Defined here
    +    //! QhullVertexSet -- a set of Qhull Vertices, as a C++ class.
    +    //! See Qhull
    +    class QhullVertexSet;
    +    typedef QhullSetIterator QhullVertexSetIterator;
    +
    +class QhullVertexSet : public QhullSet {
    +
    +private:
    +#//!\name Fields
    +    bool                qhsettemp_defined;  //! Set was allocated with qh_settemp()
    +
    +public:
    +#//!\name Constructor
    +                        QhullVertexSet(const Qhull &q, setT *s) : QhullSet(q, s), qhsettemp_defined(false) {}
    +                        QhullVertexSet(const Qhull &q, facetT *facetlist, setT *facetset, bool allfacets);
    +                        //Conversion from setT* is not type-safe.  Implicit conversion for void* to T
    +                        QhullVertexSet(QhullQh *qqh, setT *s) : QhullSet(qqh, s), qhsettemp_defined(false) {}
    +                        QhullVertexSet(QhullQh *qqh, facetT *facetlist, setT *facetset, bool allfacets);
    +                        //Copy constructor and assignment copies pointer but not contents.  Throws error if qhsettemp_defined.  Needed for return by value.
    +                        QhullVertexSet(const QhullVertexSet &other);
    +    QhullVertexSet &    operator=(const QhullVertexSet &other);
    +                        ~QhullVertexSet();
    +
    +private:                //!Default constructor disabled.  Will implement allocation later
    +                        QhullVertexSet();
    +public:
    +
    +#//!\name Destructor
    +    void                freeQhSetTemp();
    +
    +#//!\name Conversion
    +#ifndef QHULL_NO_STL
    +    std::vector toStdVector() const;
    +#endif //QHULL_NO_STL
    +#ifdef QHULL_USES_QT
    +    QList   toQList() const;
    +#endif //QHULL_USES_QT
    +
    +#//!\name IO
    +    struct PrintVertexSet{
    +        const QhullVertexSet *vertex_set;
    +        const char *    print_message;     //!< non-null message
    +                        
    +                        PrintVertexSet(const char *message, const QhullVertexSet *s) : vertex_set(s), print_message(message) {}
    +    };//PrintVertexSet
    +    const PrintVertexSet print(const char *message) const { return PrintVertexSet(message, this); }
    +
    +    struct PrintIdentifiers{
    +        const QhullVertexSet *vertex_set;
    +        const char *    print_message;    //!< non-null message
    +                        PrintIdentifiers(const char *message, const QhullVertexSet *s) : vertex_set(s), print_message(message) {}
    +    };//PrintIdentifiers
    +    PrintIdentifiers    printIdentifiers(const char *message) const { return PrintIdentifiers(message, this); }
    +
    +};//class QhullVertexSet
    +
    +}//namespace orgQhull
    +
    +#//!\name Global
    +
    +std::ostream &operator<<(std::ostream &os, const orgQhull::QhullVertexSet::PrintVertexSet &pr);
    +std::ostream &operator<<(std::ostream &os, const orgQhull::QhullVertexSet::PrintIdentifiers &p);
    +inline std::ostream &operator<<(std::ostream &os, const orgQhull::QhullVertexSet &vs) { os << vs.print(""); return os; }
    +
    +#endif // QHULLVERTEXSET_H
    diff --git a/xs/src/qhull/src/libqhullcpp/RboxPoints.cpp b/xs/src/qhull/src/libqhullcpp/RboxPoints.cpp
    new file mode 100644
    index 000000000..d7acd9fce
    --- /dev/null
    +++ b/xs/src/qhull/src/libqhullcpp/RboxPoints.cpp
    @@ -0,0 +1,224 @@
    +/****************************************************************************
    +**
    +** Copyright (c) 2008-2015 C.B. Barber. All rights reserved.
    +** $Id: //main/2015/qhull/src/libqhullcpp/RboxPoints.cpp#3 $$Change: 2066 $
    +** $DateTime: 2016/01/18 19:29:17 $$Author: bbarber $
    +**
    +****************************************************************************/
    +
    +#include "libqhullcpp/RboxPoints.h"
    +
    +#include "libqhullcpp/QhullError.h"
    +
    +#include 
    +
    +using std::cerr;
    +using std::endl;
    +using std::istream;
    +using std::ostream;
    +using std::ostringstream;
    +using std::string;
    +using std::vector;
    +using std::ws;
    +
    +#ifdef _MSC_VER  // Microsoft Visual C++ -- warning level 4
    +#pragma warning( disable : 4996)  // function was declared deprecated(strcpy, localtime, etc.)
    +#endif
    +
    +namespace orgQhull {
    +
    +#//! RboxPoints -- generate random PointCoordinates for qhull (rbox)
    +
    +
    +#//!\name Constructors
    +RboxPoints::
    +RboxPoints()
    +: PointCoordinates("rbox")
    +, rbox_new_count(0)
    +, rbox_status(qh_ERRnone)
    +, rbox_message()
    +{
    +    allocateQhullQh();
    +}
    +
    +//! Allocate and generate points according to rboxCommand
    +//! For rbox commands, see http://www.qhull.org/html/rbox.htm or html/rbox.htm
    +//! Same as appendPoints()
    +RboxPoints::
    +RboxPoints(const char *rboxCommand)
    +: PointCoordinates("rbox")
    +, rbox_new_count(0)
    +, rbox_status(qh_ERRnone)
    +, rbox_message()
    +{
    +    allocateQhullQh();
    +    // rbox arguments added to comment() via qh_rboxpoints > qh_fprintf_rbox
    +    appendPoints(rboxCommand);
    +}
    +
    +RboxPoints::
    +~RboxPoints()
    +{
    +    delete qh();
    +    resetQhullQh(0);
    +}
    +
    +// RboxPoints and qh_rboxpoints has several fields in qhT (rbox_errexit..cpp_object)
    +// It shares last_random with qh_rand and qh_srand
    +// The other fields are unused
    +void RboxPoints::
    +allocateQhullQh()
    +{
    +    QHULL_LIB_CHECK /* Check for compatible library */
    +    resetQhullQh(new QhullQh);
    +}//allocateQhullQh
    +
    +#//!\name Messaging
    +
    +void RboxPoints::
    +clearRboxMessage()
    +{
    +    rbox_status= qh_ERRnone;
    +    rbox_message.clear();
    +}//clearRboxMessage
    +
    +std::string RboxPoints::
    +rboxMessage() const
    +{
    +    if(rbox_status!=qh_ERRnone){
    +        return rbox_message;
    +    }
    +    if(isEmpty()){
    +        return "rbox warning: no points generated\n";
    +    }
    +    return "rbox: OK\n";
    +}//rboxMessage
    +
    +int RboxPoints::
    +rboxStatus() const
    +{
    +    return rbox_status;
    +}
    +
    +bool RboxPoints::
    +hasRboxMessage() const
    +{
    +    return (rbox_status!=qh_ERRnone);
    +}
    +
    +#//!\name Methods
    +
    +//! Appends points as defined by rboxCommand
    +//! Appends rboxCommand to comment
    +//! For rbox commands, see http://www.qhull.org/html/rbox.htm or html/rbox.htm
    +void RboxPoints::
    +appendPoints(const char *rboxCommand)
    +{
    +    string s("rbox ");
    +    s += rboxCommand;
    +    char *command= const_cast(s.c_str());
    +    if(qh()->cpp_object){
    +        throw QhullError(10001, "Qhull error: conflicting user of cpp_object for RboxPoints::appendPoints() or corrupted qh_qh");
    +    }
    +    if(extraCoordinatesCount()!=0){
    +        throw QhullError(10067, "Qhull error: Extra coordinates (%d) prior to calling RboxPoints::appendPoints.  Was %s", extraCoordinatesCount(), 0, 0.0, comment().c_str());
    +    }
    +    countT previousCount= count();
    +    qh()->cpp_object= this;           // for qh_fprintf_rbox()
    +    int status= qh_rboxpoints(qh(), command);
    +    qh()->cpp_object= 0;
    +    if(rbox_status==qh_ERRnone){
    +        rbox_status= status;
    +    }
    +    if(rbox_status!=qh_ERRnone){
    +        throw QhullError(rbox_status, rbox_message);
    +    }
    +    if(extraCoordinatesCount()!=0){
    +        throw QhullError(10002, "Qhull error: extra coordinates (%d) for PointCoordinates (%x)", extraCoordinatesCount(), 0, 0.0, coordinates());
    +    }
    +    if(previousCount+newCount()!=count()){
    +        throw QhullError(10068, "Qhull error: rbox specified %d points but got %d points for command '%s'", newCount(), count()-previousCount, 0.0, comment().c_str());
    +    }
    +}//appendPoints
    +
    +}//namespace orgQhull
    +
    +#//!\name Global functions
    +
    +/*---------------------------------
    +
    +  qh_fprintf_rbox(qh, fp, msgcode, format, list of args )
    +    fp is ignored (replaces qh_fprintf_rbox() in userprintf_rbox.c)
    +    cpp_object == RboxPoints
    +
    +notes:
    +    only called from qh_rboxpoints()
    +    same as fprintf() and Qhull.qh_fprintf()
    +    fgets() is not trapped like fprintf()
    +    Do not throw errors from here.  Use qh_errexit_rbox;
    +    A similar technique can be used for qh_fprintf to capture all of its output
    +*/
    +extern "C"
    +void qh_fprintf_rbox(qhT *qh, FILE*, int msgcode, const char *fmt, ... ) {
    +    va_list args;
    +
    +    using namespace orgQhull;
    +
    +    if(!qh->cpp_object){
    +        qh_errexit_rbox(qh, 10072);
    +    }
    +    RboxPoints *out= reinterpret_cast(qh->cpp_object);
    +    va_start(args, fmt);
    +    if(msgcoderbox_message += newMessage;
    +        if(out->rbox_statusrbox_status>=MSG_STDERR){
    +            out->rbox_status= msgcode;
    +        }
    +        va_end(args);
    +        return;
    +    }
    +    switch(msgcode){
    +    case 9391:
    +    case 9392:
    +        out->rbox_message += "RboxPoints error: options 'h', 'n' not supported.\n";
    +        qh_errexit_rbox(qh, 10010);
    +        /* never returns */
    +    case 9393:  // FIXUP QH11026 countT vs. int
    +        {
    +            int dimension= va_arg(args, int);
    +            string command(va_arg(args, char*));
    +            countT count= va_arg(args, countT);
    +            out->setDimension(dimension);
    +            out->appendComment(" \"");
    +            out->appendComment(command.substr(command.find(' ')+1));
    +            out->appendComment("\"");
    +            out->setNewCount(count);
    +            out->reservePoints();
    +        }
    +        break;
    +    case 9407:
    +        *out << va_arg(args, int);
    +        // fall through
    +    case 9405:
    +        *out << va_arg(args, int);
    +        // fall through
    +    case 9403:
    +        *out << va_arg(args, int);
    +        break;
    +    case 9408:
    +        *out << va_arg(args, double);
    +        // fall through
    +    case 9406:
    +        *out << va_arg(args, double);
    +        // fall through
    +    case 9404:
    +        *out << va_arg(args, double);
    +        break;
    +    }
    +    va_end(args);
    +} /* qh_fprintf_rbox */
    +
    diff --git a/xs/src/qhull/src/libqhullcpp/RboxPoints.h b/xs/src/qhull/src/libqhullcpp/RboxPoints.h
    new file mode 100644
    index 000000000..e8ec72b14
    --- /dev/null
    +++ b/xs/src/qhull/src/libqhullcpp/RboxPoints.h
    @@ -0,0 +1,69 @@
    +/****************************************************************************
    +**
    +** Copyright (c) 2008-2015 C.B. Barber. All rights reserved.
    +** $Id: //main/2015/qhull/src/libqhullcpp/RboxPoints.h#4 $$Change: 2079 $
    +** $DateTime: 2016/02/07 17:43:34 $$Author: bbarber $
    +**
    +****************************************************************************/
    +
    +#ifndef RBOXPOINTS_H
    +#define RBOXPOINTS_H
    +
    +#include "libqhull_r/qhull_ra.h"
    +#include "libqhullcpp/QhullPoint.h"
    +#include "libqhullcpp/PointCoordinates.h"
    +
    +#include 
    +#include 
    +#include 
    +#include 
    +#include 
    +#include 
    +
    +namespace orgQhull {
    +
    +#//!\name Defined here
    +    //! RboxPoints -- generate random PointCoordinates for Qhull
    +    class RboxPoints;
    +
    +class RboxPoints : public PointCoordinates {
    +
    +private:
    +#//!\name Fields and friends
    +                        //! PointCoordinates.qh() is owned by RboxPoints
    +    countT              rbox_new_count;     //! Number of points for PointCoordinates
    +    int                 rbox_status;    //! error status from rboxpoints.  qh_ERRnone if none.
    +    std::string         rbox_message;   //! stderr from rboxpoints
    +
    +    // '::' is required for friend references
    +    friend void ::qh_fprintf_rbox(qhT *qh, FILE *fp, int msgcode, const char *fmt, ... );
    +
    +public:
    +#//!\name Construct
    +                        RboxPoints();
    +    explicit            RboxPoints(const char *rboxCommand);
    +                        ~RboxPoints();
    +private:                // Disable copy constructor and assignment.  RboxPoints owns QhullQh.
    +                        RboxPoints(const RboxPoints &);
    +                        RboxPoints &operator=(const RboxPoints &);
    +private:
    +    void                allocateQhullQh();
    +
    +public:
    +#//!\name GetSet
    +    void                clearRboxMessage();
    +    countT              newCount() const { return rbox_new_count; }
    +    std::string         rboxMessage() const;
    +    int                 rboxStatus() const;
    +    bool                hasRboxMessage() const;
    +    void                setNewCount(countT pointCount) { QHULL_ASSERT(pointCount>=0); rbox_new_count= pointCount; }
    +
    +#//!\name Modify
    +    void                appendPoints(const char* rboxCommand);
    +    using               PointCoordinates::appendPoints;
    +    void                reservePoints() { reserveCoordinates((count()+newCount())*dimension()); }
    +};//class RboxPoints
    +
    +}//namespace orgQhull
    +
    +#endif // RBOXPOINTS_H
    diff --git a/xs/src/qhull/src/libqhullcpp/RoadError.cpp b/xs/src/qhull/src/libqhullcpp/RoadError.cpp
    new file mode 100644
    index 000000000..1d41ec1bc
    --- /dev/null
    +++ b/xs/src/qhull/src/libqhullcpp/RoadError.cpp
    @@ -0,0 +1,158 @@
    +/****************************************************************************
    +**
    +** Copyright (c) 2008-2015 C.B. Barber. All rights reserved.
    +** $Id: //main/2015/qhull/src/libqhullcpp/RoadError.cpp#2 $$Change: 2066 $
    +** $DateTime: 2016/01/18 19:29:17 $$Author: bbarber $
    +**
    +****************************************************************************/
    +
    +#//! RoadError -- All exceptions thrown by Qhull are RoadErrors
    +#//! Do not throw RoadError's from destructors.  Use e.logError() instead.
    +
    +#include "libqhullcpp/RoadError.h"
    +
    +#include 
    +#include 
    +#include 
    +
    +using std::cerr;
    +using std::cout;
    +using std::string;
    +
    +#ifdef _MSC_VER  // Microsoft Visual C++ -- warning level 4
    +#endif
    +
    +namespace orgQhull {
    +
    +#//!\name Class fields
    +
    +//! Identifies error messages from Qhull and Road for web searches.
    +//! See QhullError.h#QHULLlastError and user.h#MSG_ERROR
    +const char * RoadError::
    +ROADtag= "QH";
    +
    +std::ostringstream RoadError::
    +global_log;
    +
    +#//!\name Constructor
    +
    +RoadError::
    +RoadError()
    +: error_code(0)
    +, log_event()
    +, error_message()
    +{ }
    +
    +RoadError::
    +RoadError(const RoadError &other)
    +: error_code(other.error_code)
    +, log_event(other.log_event)
    +, error_message(other.error_message)
    +{
    +}//copy construct
    +
    +RoadError::
    +RoadError(int code, const std::string &message)
    +: error_code(code)
    +, log_event(message.c_str())
    +, error_message(log_event.toString(ROADtag, error_code))
    +{
    +    log_event.cstr_1= error_message.c_str(); // overwrites initial value
    +}
    +
    +RoadError::
    +RoadError(int code, const char *fmt)
    +: error_code(code)
    +, log_event(fmt)
    +, error_message()
    +{ }
    +
    +RoadError::
    +RoadError(int code, const char *fmt, int d)
    +: error_code(code)
    +, log_event(fmt, d)
    +, error_message()
    +{ }
    +
    +RoadError::
    +RoadError(int code, const char *fmt, int d, int d2)
    +: error_code(code)
    +, log_event(fmt, d, d2)
    +, error_message()
    +{ }
    +
    +RoadError::
    +RoadError(int code, const char *fmt, int d, int d2, float f)
    +: error_code(code)
    +, log_event(fmt, d, d2, f)
    +, error_message()
    +{ }
    +
    +RoadError::
    +RoadError(int code, const char *fmt, int d, int d2, float f, const char *s)
    +: error_code(code)
    +, log_event(fmt, d, d2, f, s)
    +, error_message(log_event.toString(ROADtag, code)) // char * may go out of scope
    +{ }
    +
    +RoadError::
    +RoadError(int code, const char *fmt, int d, int d2, float f, const void *x)
    +: error_code(code)
    +, log_event(fmt, d, d2, f, x)
    +, error_message()
    +{ }
    +
    +RoadError::
    +RoadError(int code, const char *fmt, int d, int d2, float f, int i)
    +: error_code(code)
    +, log_event(fmt, d, d2, f, i)
    +, error_message()
    +{ }
    +
    +RoadError::
    +RoadError(int code, const char *fmt, int d, int d2, float f, long long i)
    +: error_code(code)
    +, log_event(fmt, d, d2, f, i)
    +, error_message()
    +{ }
    +
    +RoadError::
    +RoadError(int code, const char *fmt, int d, int d2, float f, double e)
    +: error_code(code)
    +, log_event(fmt, d, d2, f, e)
    +, error_message()
    +{ }
    +
    +RoadError & RoadError::
    +operator=(const RoadError &other)
    +{
    +    error_code= other.error_code;
    +    error_message= other.error_message;
    +    log_event= other.log_event;
    +    return *this;
    +}//operator=
    +
    +#//!\name Virtual
    +const char * RoadError::
    +what() const throw()
    +{
    +    if(error_message.empty()){
    +        error_message= log_event.toString(ROADtag, error_code);
    +    }
    +    return error_message.c_str();
    +}//what
    +
    +#//!\name Updates
    +
    +//! Log error instead of throwing it.
    +//! Not reentrant, so avoid using it if possible
    +//!\todo Redesign with a thread-local stream or a reentrant ostringstream
    +void RoadError::
    +logErrorLastResort() const
    +{
    +    global_log << what() << endl;
    +}//logError
    +
    +
    +}//namespace orgQhull
    +
    diff --git a/xs/src/qhull/src/libqhullcpp/RoadError.h b/xs/src/qhull/src/libqhullcpp/RoadError.h
    new file mode 100644
    index 000000000..1c9f6cdd5
    --- /dev/null
    +++ b/xs/src/qhull/src/libqhullcpp/RoadError.h
    @@ -0,0 +1,88 @@
    +/****************************************************************************
    +**
    +** Copyright (c) 2008-2015 C.B. Barber. All rights reserved.
    +** $Id: //main/2015/qhull/src/libqhullcpp/RoadError.h#4 $$Change: 2079 $
    +** $DateTime: 2016/02/07 17:43:34 $$Author: bbarber $
    +**
    +****************************************************************************/
    +
    +#ifndef ROADERROR_H
    +#define ROADERROR_H
    +
    +#include "libqhull_r/user_r.h"  /* for QHULL_CRTDBG */
    +#include "libqhullcpp/RoadLogEvent.h"
    +
    +#include 
    +#include 
    +#include 
    +#include 
    +
    +using std::endl;
    +
    +namespace orgQhull {
    +
    +#//!\name Defined here
    +    //! RoadError -- Report and log errors
    +    //!  See discussion in Saylan, G., "Practical C++ error handling in hybrid environments," Dr. Dobb's Journal, p. 50-55, March 2007.
    +    //!   He uses an auto_ptr to track a stringstream.  It constructs a string on the fly.  RoadError uses the copy constructor to transform RoadLogEvent into a string
    +    class RoadError;
    +
    +class RoadError : public std::exception {
    +
    +private:
    +#//!\name Fields
    +    int                 error_code;  //! Non-zero code (not logged), maybe returned as program status
    +    RoadLogEvent        log_event;   //! Format string w/ arguments
    +    mutable std::string error_message;  //! Formated error message.  Must be after log_event.
    +
    +#//!\name Class fields
    +    static const char  *  ROADtag;
    +    static std::ostringstream  global_log; //!< May be replaced with any ostream object
    +                                    //!< Not reentrant -- only used by RoadError::logErrorLastResort()
    +
    +public:
    +#//!\name Constants
    +
    +#//!\name Constructors
    +    RoadError();
    +    RoadError(const RoadError &other);  //! Called on throw, generates error_message
    +    RoadError(int code, const std::string &message);
    +    RoadError(int code, const char *fmt);
    +    RoadError(int code, const char *fmt, int d);
    +    RoadError(int code, const char *fmt, int d, int d2);
    +    RoadError(int code, const char *fmt, int d, int d2, float f);
    +    RoadError(int code, const char *fmt, int d, int d2, float f, const char *s);
    +    RoadError(int code, const char *fmt, int d, int d2, float f, const void *x);
    +    RoadError(int code, const char *fmt, int d, int d2, float f, int i);
    +    RoadError(int code, const char *fmt, int d, int d2, float f, long long i);
    +    RoadError(int code, const char *fmt, int d, int d2, float f, double e);
    +
    +    RoadError &         operator=(const RoadError &other);
    +                        ~RoadError() throw() {};
    +
    +#//!\name Class methods
    +
    +    static void         clearGlobalLog() { global_log.seekp(0); }
    +    static bool         emptyGlobalLog() { return global_log.tellp()<=0; }
    +    static const char  *stringGlobalLog() { return global_log.str().c_str(); }
    +
    +#//!\name Virtual
    +    virtual const char *what() const throw();
    +
    +#//!\name GetSet
    +    bool                isValid() const { return log_event.isValid(); }
    +    int                 errorCode() const { return error_code; };
    +   // FIXUP QH11021 should RoadError provide errorMessage().  Currently what()
    +    RoadLogEvent        roadLogEvent() const { return log_event; };
    +
    +#//!\name Update
    +    void                logErrorLastResort() const;
    +};//class RoadError
    +
    +}//namespace orgQhull
    +
    +#//!\name Global
    +
    +inline std::ostream &   operator<<(std::ostream &os, const orgQhull::RoadError &e) { return os << e.what(); }
    +
    +#endif // ROADERROR_H
    diff --git a/xs/src/qhull/src/libqhullcpp/RoadLogEvent.cpp b/xs/src/qhull/src/libqhullcpp/RoadLogEvent.cpp
    new file mode 100644
    index 000000000..9a9cf5960
    --- /dev/null
    +++ b/xs/src/qhull/src/libqhullcpp/RoadLogEvent.cpp
    @@ -0,0 +1,122 @@
    +/****************************************************************************
    +**
    +** Copyright (c) 2008-2015 C.B. Barber. All rights reserved.
    +** $Id: //main/2015/qhull/src/libqhullcpp/RoadLogEvent.cpp#3 $$Change: 2066 $
    +** $Date: 2016/01/18 $$Author: bbarber $
    +**
    +****************************************************************************/
    +
    +#//! RoadLogEvent -- All exceptions thrown by Qhull are RoadErrors
    +
    +#include "libqhullcpp/RoadLogEvent.h"
    +
    +#include 
    +#include 
    +#include 
    +
    +using std::cout;
    +using std::endl;
    +using std::ostringstream;
    +using std::string;
    +
    +#ifdef _MSC_VER  // Microsoft Visual C++ -- warning level 4
    +#endif
    +
    +namespace orgQhull {
    +
    +#//!\name Conversion
    +string RoadLogEvent::
    +toString(const char *tag, int code) const
    +{
    +    ostringstream os;
    +    if(tag && code){
    +        os << tag << code;
    +        if(format_string){
    +            os << " ";
    +        }
    +    }
    +    if(!format_string){
    +        return os.str();
    +    }
    +    const char *s= format_string;
    +    int dCount= 0;  // Count of %d
    +    int fCount= 0;  // Count of %f
    +    char extraCode= '\0';
    +    while(*s){
    +        if(*s!='%'){
    +            os << *s++;
    +        }else{
    +            char c= *++s;
    +            s++;
    +            switch(c){
    +            case 'd':
    +                if(++dCount>2){
    +                    os << " ERROR_three_%d_in_format ";
    +                }else if(dCount==2){
    +                    os << int_2;
    +                }else{
    +                    os << int_1;
    +                }
    +                break;
    +            case 'e':
    +                if(firstExtraCode(os, c, &extraCode)){
    +                    os << double_1;
    +                }
    +                break;
    +            case 'f':
    +                if(++fCount>1){
    +                    os << " ERROR_two_%f_in_format ";
    +                }else{
    +                    os << float_1;
    +                }
    +                break;
    +            case 'i':
    +                if(firstExtraCode(os, c, &extraCode)){
    +                    os << int64_1;
    +                }
    +                break;
    +            case 's':
    +                if(firstExtraCode(os, c, &extraCode)){
    +                    os << cstr_1;
    +                }
    +                break;
    +            case 'u':
    +                if(firstExtraCode(os, c, &extraCode)){
    +                    os << "0x" << std::hex << int64_1 << std::dec;
    +                }
    +                break;
    +            case 'x':
    +                if(firstExtraCode(os, c, &extraCode)){
    +                    os << void_1;
    +                }
    +                break;
    +            case '%':
    +                os << c;
    +                break;
    +            default:
    +                os << " ERROR_%" << c << "_not_defined_in_format";
    +                break;
    +            }
    +        }
    +    }
    +    if(s[-1]!='\n'){
    +        os << endl;
    +    }
    +    return os.str();
    +}//toString
    +
    +#//!\name Class helpers (static)
    +
    +//! True if this char is the first extra code
    +bool RoadLogEvent::
    +firstExtraCode(std::ostream &os, char c, char *extraCode){
    +    if(*extraCode){
    +        os << " ERROR_%" << *extraCode << "_and_%" << c << "_in_format ";
    +        return false;
    +    }
    +    *extraCode= c;
    +    return true;
    +}//firstExtraCode
    +
    +}//namespace orgQhull
    +
    diff --git a/xs/src/qhull/src/libqhullcpp/RoadLogEvent.h b/xs/src/qhull/src/libqhullcpp/RoadLogEvent.h
    new file mode 100644
    index 000000000..7c4cfba0d
    --- /dev/null
    +++ b/xs/src/qhull/src/libqhullcpp/RoadLogEvent.h
    @@ -0,0 +1,77 @@
    +/****************************************************************************
    +**
    +** Copyright (c) 2008-2015 C.B. Barber. All rights reserved.
    +** $Id: //main/2015/qhull/src/libqhullcpp/RoadLogEvent.h#1 $$Change: 1981 $
    +** $DateTime: 2015/09/28 20:26:32 $$Author: bbarber $
    +**
    +****************************************************************************/
    +
    +#ifndef ROADLOGEVENT_H
    +#define ROADLOGEVENT_H
    +
    +#include 
    +#include 
    +#include 
    +
    +namespace orgQhull {
    +
    +#//!\name Defined here
    +    //! RoadLogEvent -- Record an event for the RoadLog
    +    struct RoadLogEvent;
    +
    +struct RoadLogEvent {
    +
    +public:
    +#//!\name Fields
    +    const char *    format_string; //! Format string (a literal with format codes, for logging)
    +    int             int_1;       //! Integer argument (%d, for logging)
    +    int             int_2;       //! Integer argument (%d, for logging)
    +    float           float_1;     //! Float argument (%f, for logging)
    +    union {                      //! One additional argument (for logging)
    +        const char *cstr_1;      //!   Cstr argument (%s) -- type checked at construct-time
    +        const void *void_1;      //!   Void* argument (%x) -- Use upper-case codes for object types
    +        long long   int64_1;     //!   signed int64 (%i).  Ambiguous if unsigned is also defined.
    +        double      double_1;    //!   Double argument (%e)
    +    };
    +
    +#//!\name Constants
    +
    +#//!\name Constructors
    +    RoadLogEvent() : format_string(0), int_1(0), int_2(0), float_1(0), int64_1(0) {};
    +    explicit RoadLogEvent(const char *fmt) : format_string(fmt), int_1(0), int_2(0), float_1(0), int64_1(0) {};
    +    RoadLogEvent(const char *fmt, int d) : format_string(fmt), int_1(d), int_2(0), float_1(0), int64_1(0) {};
    +    RoadLogEvent(const char *fmt, int d, int d2) : format_string(fmt), int_1(d), int_2(d2), float_1(0), int64_1(0) {};
    +    RoadLogEvent(const char *fmt, int d, int d2, float f) : format_string(fmt), int_1(d), int_2(d2), float_1(f), int64_1(0) {};
    +    RoadLogEvent(const char *fmt, int d, int d2, float f, const char *s) : format_string(fmt), int_1(d), int_2(d2), float_1(f), cstr_1(s) {};
    +    RoadLogEvent(const char *fmt, int d, int d2, float f, const void *x) : format_string(fmt), int_1(d), int_2(d2), float_1(f), void_1(x) {};
    +    RoadLogEvent(const char *fmt, int d, int d2, float f, int i) : format_string(fmt), int_1(d), int_2(d2), float_1(f), int64_1(i) {};
    +    RoadLogEvent(const char *fmt, int d, int d2, float f, long long i) : format_string(fmt), int_1(d), int_2(d2), float_1(f), int64_1(i) {};
    +    RoadLogEvent(const char *fmt, int d, int d2, float f, double g) : format_string(fmt), int_1(d), int_2(d2), float_1(f), double_1(g) {};
    +    ~RoadLogEvent() {};
    +    //! Default copy constructor and assignment
    +
    +#//!\name GetSet
    +    bool                isValid() const { return format_string!=0; }
    +    int                 int1() const { return int_1; };
    +    int                 int2() const { return int_2; };
    +    float               float1() const { return float_1; };
    +    const char *        format() const { return format_string; };
    +    const char *        cstr1() const { return cstr_1; };
    +    const void *        void1() const { return void_1; };
    +    long long           int64() const { return int64_1; };
    +    double              double1() const { return double_1; };
    +
    +#//!\name Conversion
    +
    +    std::string        toString(const char* tag, int code) const;
    +
    +private:
    +#//!\name Class helpers
    +    static bool         firstExtraCode(std::ostream &os, char c, char *extraCode);
    +
    +
    +};//class RoadLogEvent
    +
    +}//namespace orgQhull
    +
    +#endif // ROADLOGEVENT_H
    diff --git a/xs/src/qhull/src/libqhullcpp/functionObjects.h b/xs/src/qhull/src/libqhullcpp/functionObjects.h
    new file mode 100644
    index 000000000..3645c0713
    --- /dev/null
    +++ b/xs/src/qhull/src/libqhullcpp/functionObjects.h
    @@ -0,0 +1,67 @@
    +/****************************************************************************
    +**
    +** Copyright (c) 2008-2015 C.B. Barber. All rights reserved.
    +** $Id: //main/2015/qhull/src/libqhullcpp/functionObjects.h#1 $$Change: 1981 $
    +** $DateTime: 2015/09/28 20:26:32 $$Author: bbarber $
    +**
    +****************************************************************************/
    +
    +#ifndef QHFUNCTIONOBJECTS_H
    +#define QHFUNCTIONOBJECTS_H
    +
    +#include 
    +#include 
    +
    +namespace orgQhull {
    +
    +#//!\name Defined here
    +
    +    //! Sum of absolute values of the elements in a container
    +    class AbsoluteSumOf;
    +    //! Sum of the elements in a container
    +    class SumOf;
    +    //! Sum of squares of the elements in a container
    +    class SumSquaresOf;
    +
    +#//!\name Class
    +
    +//! Absolute sum of the elements in a container
    +class AbsoluteSumOf
    +{
    +private:
    +    double sum;
    +public:
    +    inline AbsoluteSumOf() : sum(0.0) {}
    +    inline void operator()(double v) { sum += fabs(v); }
    +    inline operator double() { return sum; }
    +};//AbsoluteSumOf
    +
    +//! Sum of the elements in a container
    +class SumOf
    +{
    +private:
    +    double sum;
    +public:
    +    inline SumOf() : sum(0.0) {}
    +    inline void operator()(double v) { sum += v; }
    +    inline operator double() { return sum; }
    +};//SumOf
    +
    +
    +//! Sum of squares of the elements in a container
    +class SumSquaresOf
    +{
    +private:
    +    double sum;
    +public:
    +    inline SumSquaresOf() : sum(0.0) {}
    +    inline void operator()(double v) { sum += v*v; }
    +    inline operator double() { return sum; }
    +};//SumSquaresOf
    +
    +
    +}//orgQhull
    +
    +
    +#endif //QHFUNCTIONOBJECTS_H
    +
    diff --git a/xs/src/qhull/src/libqhullcpp/libqhullcpp.pro b/xs/src/qhull/src/libqhullcpp/libqhullcpp.pro
    new file mode 100644
    index 000000000..89b967bef
    --- /dev/null
    +++ b/xs/src/qhull/src/libqhullcpp/libqhullcpp.pro
    @@ -0,0 +1,71 @@
    +# -------------------------------------------------
    +# libqhullcpp.pro -- Qt project for Qhull cpp shared library
    +#
    +# It uses reentrant Qhull
    +# -------------------------------------------------
    +
    +include(../qhull-warn.pri)
    +
    +DESTDIR = ../../lib
    +TEMPLATE = lib
    +# Do not create libqhullcpp as a shared library.  Qhull C++ classes may change layout and size. 
    +CONFIG += staticlib warn_on
    +CONFIG -= qt rtti
    +build_pass:CONFIG(debug, debug|release):{
    +   TARGET = qhullcpp_d
    +   OBJECTS_DIR = Debug
    +}else:build_pass:CONFIG(release, debug|release):{
    +   TARGET = qhullcpp
    +   OBJECTS_DIR = Release
    +}
    +MOC_DIR = moc
    +
    +INCLUDEPATH += ../../src
    +INCLUDEPATH += $$PWD # for MOC_DIR
    +
    +CONFIG += qhull_warn_shadow qhull_warn_conversion
    +
    +SOURCES += ../libqhullcpp/Coordinates.cpp
    +SOURCES += ../libqhullcpp/PointCoordinates.cpp
    +SOURCES += ../libqhullcpp/Qhull.cpp
    +SOURCES += ../libqhullcpp/QhullFacet.cpp
    +SOURCES += ../libqhullcpp/QhullFacetList.cpp
    +SOURCES += ../libqhullcpp/QhullFacetSet.cpp
    +SOURCES += ../libqhullcpp/QhullHyperplane.cpp
    +SOURCES += ../libqhullcpp/QhullPoint.cpp
    +SOURCES += ../libqhullcpp/QhullPoints.cpp
    +SOURCES += ../libqhullcpp/QhullPointSet.cpp
    +SOURCES += ../libqhullcpp/QhullQh.cpp
    +SOURCES += ../libqhullcpp/QhullRidge.cpp
    +SOURCES += ../libqhullcpp/QhullSet.cpp
    +SOURCES += ../libqhullcpp/QhullStat.cpp
    +SOURCES += ../libqhullcpp/QhullVertex.cpp
    +SOURCES += ../libqhullcpp/QhullVertexSet.cpp
    +SOURCES += ../libqhullcpp/RboxPoints.cpp
    +SOURCES += ../libqhullcpp/RoadError.cpp
    +SOURCES += ../libqhullcpp/RoadLogEvent.cpp
    +
    +HEADERS += ../libqhullcpp/Coordinates.h
    +HEADERS += ../libqhullcpp/functionObjects.h
    +HEADERS += ../libqhullcpp/PointCoordinates.h
    +HEADERS += ../libqhullcpp/Qhull.h
    +HEADERS += ../libqhullcpp/QhullError.h
    +HEADERS += ../libqhullcpp/QhullFacet.h
    +HEADERS += ../libqhullcpp/QhullFacetList.h
    +HEADERS += ../libqhullcpp/QhullFacetSet.h
    +HEADERS += ../libqhullcpp/QhullHyperplane.h
    +HEADERS += ../libqhullcpp/QhullIterator.h
    +HEADERS += ../libqhullcpp/QhullLinkedList.h
    +HEADERS += ../libqhullcpp/QhullPoint.h
    +HEADERS += ../libqhullcpp/QhullPoints.h
    +HEADERS += ../libqhullcpp/QhullPointSet.h
    +HEADERS += ../libqhullcpp/QhullQh.h
    +HEADERS += ../libqhullcpp/QhullRidge.h
    +HEADERS += ../libqhullcpp/QhullSet.h
    +HEADERS += ../libqhullcpp/QhullSets.h
    +HEADERS += ../libqhullcpp/QhullStat.h
    +HEADERS += ../libqhullcpp/QhullVertex.h
    +HEADERS += ../libqhullcpp/QhullVertexSet.h
    +HEADERS += ../libqhullcpp/RboxPoints.h
    +HEADERS += ../libqhullcpp/RoadError.h
    +HEADERS += ../libqhullcpp/RoadLogEvent.h
    diff --git a/xs/src/qhull/src/libqhullcpp/qt-qhull.cpp b/xs/src/qhull/src/libqhullcpp/qt-qhull.cpp
    new file mode 100644
    index 000000000..895f591a8
    --- /dev/null
    +++ b/xs/src/qhull/src/libqhullcpp/qt-qhull.cpp
    @@ -0,0 +1,130 @@
    +/****************************************************************************
    +**
    +** Copyright (c) 2009-2015 C.B. Barber. All rights reserved.
    +** $Id: //main/2015/qhull/src/libqhullcpp/qt-qhull.cpp#1 $$Change: 1981 $
    +** $DateTime: 2015/09/28 20:26:32 $$Author: bbarber $
    +**
    +****************************************************************************/
    +
    +#include 
    +#include "qhulltest/RoadTest.h"
    +
    +#ifndef QHULL_USES_QT
    +#define QHULL_USES_QT 1
    +#endif
    +
    +#include "Coordinates.h"
    +#include "QhullFacetList.h"
    +#include "QhullFacetSet.h"
    +#include "QhullHyperplane.h"
    +#include "QhullPoint.h"
    +#include "QhullPoints.h"
    +#include "QhullPointSet.h"
    +#include "QhullVertex.h"
    +#include "QhullVertexSet.h"
    +
    +namespace orgQhull {
    +
    +#//!\name Conversions
    +
    +QList Coordinates::
    +toQList() const
    +{
    +    CoordinatesIterator i(*this);
    +    QList cs;
    +    while(i.hasNext()){
    +        cs.append(i.next());
    +    }
    +    return cs;
    +}//toQList
    +
    +QList QhullFacetList::
    +toQList() const
    +{
    +    QhullLinkedListIterator i(*this);
    +    QList vs;
    +    while(i.hasNext()){
    +        QhullFacet f= i.next();
    +        if(isSelectAll() || f.isGood()){
    +            vs.append(f);
    +        }
    +    }
    +    return vs;
    +}//toQList
    +
    +//! Same as PrintVertices
    +QList QhullFacetList::
    +vertices_toQList() const
    +{
    +    QList vs;
    +    QhullVertexSet qvs(qh(), first().getFacetT(), NULL, isSelectAll());
    +    for(QhullVertexSet::iterator i=qvs.begin(); i!=qvs.end(); ++i){
    +        vs.push_back(*i);
    +    }
    +    return vs;
    +}//vertices_toQList
    +
    +QList QhullFacetSet::
    +toQList() const
    +{
    +    QhullSetIterator i(*this);
    +    QList vs;
    +    while(i.hasNext()){
    +        QhullFacet f= i.next();
    +        if(isSelectAll() || f.isGood()){
    +            vs.append(f);
    +        }
    +    }
    +    return vs;
    +}//toQList
    +
    +#ifdef QHULL_USES_QT
    +QList QhullHyperplane::
    +toQList() const
    +{
    +    QhullHyperplaneIterator i(*this);
    +    QList fs;
    +    while(i.hasNext()){
    +        fs.append(i.next());
    +    }
    +    fs.append(hyperplane_offset);
    +    return fs;
    +}//toQList
    +#endif //QHULL_USES_QT
    +
    +QList QhullPoint::
    +toQList() const
    +{
    +    QhullPointIterator i(*this);
    +    QList vs;
    +    while(i.hasNext()){
    +        vs.append(i.next());
    +    }
    +    return vs;
    +}//toQList
    +
    +QList QhullPoints::
    +toQList() const
    +{
    +    QhullPointsIterator i(*this);
    +    QList vs;
    +    while(i.hasNext()){
    +        vs.append(i.next());
    +    }
    +    return vs;
    +}//toQList
    +
    +/******
    +QList QhullPointSet::
    +toQList() const
    +{
    +    QhullPointSetIterator i(*this);
    +    QList vs;
    +    while(i.hasNext()){
    +        vs.append(i.next());
    +    }
    +    return vs;
    +}//toQList
    +*/
    +}//orgQhull
    +
    diff --git a/xs/src/qhull/src/libqhullcpp/usermem_r-cpp.cpp b/xs/src/qhull/src/libqhullcpp/usermem_r-cpp.cpp
    new file mode 100644
    index 000000000..bb9534d09
    --- /dev/null
    +++ b/xs/src/qhull/src/libqhullcpp/usermem_r-cpp.cpp
    @@ -0,0 +1,93 @@
    +/*
      ---------------------------------
    +
    +   usermem_r-cpp.cpp
    +
    +   Redefine qh_exit() as 'throw std::runtime_error("QH10003 ...")'
    +
    +   This file is not included in the Qhull builds.
    +
    +   qhull_r calls qh_exit() when qh_errexit() is not available.  For example,
    +   it calls qh_exit() if you linked the wrong qhull library.
    +
    +   The C++ interface avoids most of the calls to qh_exit().
    +
    +   If needed, include usermem_r-cpp.o before libqhullstatic_r.a.  You may need to
    +   override duplicate symbol errors (e.g. /FORCE:MULTIPLE for DevStudio).  It
    +   may produce a warning about throwing an error from C code.
    +*/
    +
    +extern "C" {
    +    void    qh_exit(int exitcode);
    +}
    +
    +#include 
    +#include 
    +
    +/*---------------------------------
    +
    +  qh_exit( exitcode )
    +    exit program
    +
    +  notes:
    +    same as exit()
    +*/
    +void qh_exit(int exitcode) {
    +    exitcode= exitcode;
    +    throw std::runtime_error("QH10003 Qhull error.  See stderr or errfile.");
    +} /* exit */
    +
    +/*---------------------------------
    +
    +  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 */
    +
    +/*---------------------------------
    +
    +  qh_free(qhT *qh, mem )
    +    free memory
    +
    +  notes:
    +    same as free()
    +    No calls to qh_errexit()
    +*/
    +void qh_free(void *mem) {
    +    free(mem);
    +} /* free */
    +
    +/*---------------------------------
    +
    +    qh_malloc( mem )
    +      allocate memory
    +
    +    notes:
    +      same as malloc()
    +*/
    +void *qh_malloc(size_t size) {
    +    return malloc(size);
    +} /* malloc */
    +
    +
    diff --git a/xs/src/qhull/src/libqhullstatic/libqhullstatic.pro b/xs/src/qhull/src/libqhullstatic/libqhullstatic.pro
    new file mode 100644
    index 000000000..1a516db73
    --- /dev/null
    +++ b/xs/src/qhull/src/libqhullstatic/libqhullstatic.pro
    @@ -0,0 +1,19 @@
    +# -------------------------------------------------
    +# libqhullstatic.pro -- Qt project for Qhull static library
    +#   Built with qh_QHpointer=0.  See libqhullp.pro
    +# -------------------------------------------------
    +
    +include(../qhull-warn.pri)
    +include(../qhull-libqhull-src.pri)
    +
    +DESTDIR = ../../lib
    +TEMPLATE = lib
    +CONFIG += staticlib warn_on
    +CONFIG -= qt
    +build_pass:CONFIG(debug, debug|release):{
    +    TARGET = qhullstatic_d
    +    OBJECTS_DIR = Debug
    +}else:build_pass:CONFIG(release, debug|release):{
    +    TARGET = qhullstatic
    +    OBJECTS_DIR = Release
    +}
    diff --git a/xs/src/qhull/src/libqhullstatic_r/libqhullstatic_r.pro b/xs/src/qhull/src/libqhullstatic_r/libqhullstatic_r.pro
    new file mode 100644
    index 000000000..2f5bf4d07
    --- /dev/null
    +++ b/xs/src/qhull/src/libqhullstatic_r/libqhullstatic_r.pro
    @@ -0,0 +1,21 @@
    +# -------------------------------------------------
    +# libqhullstatic_r.pro -- Qt project for Qhull static library
    +#
    +# It uses reeentrant Qhull
    +# -------------------------------------------------
    +
    +include(../qhull-warn.pri)
    +
    +DESTDIR = ../../lib
    +TEMPLATE = lib
    +CONFIG += staticlib warn_on
    +CONFIG -= qt
    +build_pass:CONFIG(debug, debug|release):{
    +    TARGET = qhullstatic_rd
    +    OBJECTS_DIR = Debug
    +}else:build_pass:CONFIG(release, debug|release):{
    +    TARGET = qhullstatic_r
    +    OBJECTS_DIR = Release
    +}
    +
    +include(../qhull-libqhull-src_r.pri)
    diff --git a/xs/src/qhull/src/qconvex/qconvex.c b/xs/src/qhull/src/qconvex/qconvex.c
    new file mode 100644
    index 000000000..41bd666da
    --- /dev/null
    +++ b/xs/src/qhull/src/qconvex/qconvex.c
    @@ -0,0 +1,326 @@
    +/*
      ---------------------------------
    +
    +   qconvex.c
    +      compute convex hulls using qhull
    +
    +   see unix.c for full interface
    +
    +   Copyright (c) 1993-2015, The Geometry Center
    +*/
    +
    +#include "libqhull/libqhull.h"
    +
    +#include 
    +#include 
    +#include 
    +#include 
    +#include 
    +
    +#if __cplusplus
    +extern "C" {
    +  int isatty(int);
    +}
    +
    +#elif _MSC_VER
    +#include 
    +#define isatty _isatty
    +/* int _isatty(int); */
    +
    +#else
    +int isatty(int);  /* returns 1 if stdin is a tty
    +                   if "Undefined symbol" this can be deleted along with call in main() */
    +#endif
    +
    +/*---------------------------------
    +
    +  qh_prompt
    +    long prompt for qconvex
    +
    +  notes:
    +    restricted version of libqhull.c
    +
    +  see:
    +    concise prompt below
    +*/
    +
    +/* duplicated in qconvex.htm */
    +char hidden_options[]=" d v H Qbb Qf Qg Qm Qr Qu Qv Qx Qz TR E V Fp Gt Q0 Q1 Q2 Q3 Q4 Q5 Q6 Q7 Q8 Q9 ";
    +
    +char qh_prompta[]= "\n\
    +qconvex- compute the convex hull\n\
    +    http://www.qhull.org  %s\n\
    +\n\
    +input (stdin):\n\
    +    first lines: dimension and number of points (or vice-versa).\n\
    +    other lines: point coordinates, best if one point per line\n\
    +    comments:    start with a non-numeric character\n\
    +\n\
    +options:\n\
    +    Qt   - triangulated output\n\
    +    QJ   - joggled input instead of merged facets\n\
    +    Qc   - keep coplanar points with nearest facet\n\
    +    Qi   - keep interior points with nearest facet\n\
    +\n\
    +Qhull control options:\n\
    +    Qbk:n   - scale coord k so that low bound is n\n\
    +      QBk:n - scale coord k so that upper bound is n (QBk is %2.2g)\n\
    +    QbB  - scale input to unit cube centered at the origin\n\
    +    Qbk:0Bk:0 - remove k-th coordinate from input\n\
    +    QJn  - randomly joggle input in range [-n,n]\n\
    +    QRn  - random rotation (n=seed, n=0 time, n=-1 time/no rotate)\n\
    +%s%s%s%s";  /* split up qh_prompt for Visual C++ */
    +char qh_promptb[]= "\
    +    Qs   - search all points for the initial simplex\n\
    +    QGn  - good facet if visible from point n, -n for not visible\n\
    +    QVn  - good facet if it includes point n, -n if not\n\
    +\n\
    +";
    +char qh_promptc[]= "\
    +Trace options:\n\
    +    T4   - trace at level n, 4=all, 5=mem/gauss, -1= events\n\
    +    Tc   - check frequently during execution\n\
    +    Ts   - print statistics\n\
    +    Tv   - verify result: structure, convexity, and point inclusion\n\
    +    Tz   - send all output to stdout\n\
    +    TFn  - report summary when n or more facets created\n\
    +    TI file - input data from file, no spaces or single quotes\n\
    +    TO file - output results to file, may be enclosed in single quotes\n\
    +    TPn  - turn on tracing when point n added to hull\n\
    +     TMn - turn on tracing at merge n\n\
    +     TWn - trace merge facets when width > n\n\
    +    TVn  - stop qhull after adding point n, -n for before (see TCn)\n\
    +     TCn - stop qhull after building cone for point n (see TVn)\n\
    +\n\
    +Precision options:\n\
    +    Cn   - radius of centrum (roundoff added).  Merge facets if non-convex\n\
    +     An  - cosine of maximum angle.  Merge facets if cosine > n or non-convex\n\
    +           C-0 roundoff, A-0.99/C-0.01 pre-merge, A0.99/C0.01 post-merge\n\
    +    Rn   - randomly perturb computations by a factor of [1-n,1+n]\n\
    +    Un   - max distance below plane for a new, coplanar point\n\
    +    Wn   - min facet width for outside point (before roundoff)\n\
    +\n\
    +Output formats (may be combined; if none, produces a summary to stdout):\n\
    +    f    - facet dump\n\
    +    G    - Geomview output (see below)\n\
    +    i    - vertices incident to each facet\n\
    +    m    - Mathematica output (2-d and 3-d)\n\
    +    n    - normals with offsets\n\
    +    o    - OFF file format (dim, points and facets; Voronoi regions)\n\
    +    p    - point coordinates \n\
    +    s    - summary (stderr)\n\
    +\n\
    +";
    +char qh_promptd[]= "\
    +More formats:\n\
    +    Fa   - area for each facet\n\
    +    FA   - compute total area and volume for option 's'\n\
    +    Fc   - count plus coplanar points for each facet\n\
    +           use 'Qc' (default) for coplanar and 'Qi' for interior\n\
    +    FC   - centrum for each facet\n\
    +    Fd   - use cdd format for input (homogeneous with offset first)\n\
    +    FD   - use cdd format for numeric output (offset first)\n\
    +    FF   - facet dump without ridges\n\
    +    Fi   - inner plane for each facet\n\
    +    FI   - ID for each facet\n\
    +    Fm   - merge count for each facet (511 max)\n\
    +    Fn   - count plus neighboring facets for each facet\n\
    +    FN   - count plus neighboring facets for each point\n\
    +    Fo   - outer plane (or max_outside) for each facet\n\
    +    FO   - options and precision constants\n\
    +    FP   - nearest vertex for each coplanar point\n\
    +    FQ   - command used for qconvex\n\
    +    Fs   - summary: #int (8), dimension, #points, tot vertices, tot facets,\n\
    +                      for output: #vertices, #facets,\n\
    +                                  #coplanar points, #non-simplicial facets\n\
    +                    #real (2), max outer plane, min vertex\n\
    +    FS   - sizes:   #int (0) \n\
    +                    #real (2) tot area, tot volume\n\
    +    Ft   - triangulation with centrums for non-simplicial facets (OFF format)\n\
    +    Fv   - count plus vertices for each facet\n\
    +    FV   - average of vertices (a feasible point for 'H')\n\
    +    Fx   - extreme points (in order for 2-d)\n\
    +\n\
    +";
    +char qh_prompte[]= "\
    +Geomview output (2-d, 3-d, and 4-d)\n\
    +    Ga   - all points as dots\n\
    +     Gp  -  coplanar points and vertices as radii\n\
    +     Gv  -  vertices as spheres\n\
    +    Gi   - inner planes only\n\
    +     Gn  -  no planes\n\
    +     Go  -  outer planes only\n\
    +    Gc   - centrums\n\
    +    Gh   - hyperplane intersections\n\
    +    Gr   - ridges\n\
    +    GDn  - drop dimension n in 3-d and 4-d output\n\
    +\n\
    +Print options:\n\
    +    PAn  - keep n largest facets by area\n\
    +    Pdk:n - drop facet if normal[k] <= n (default 0.0)\n\
    +    PDk:n - drop facet if normal[k] >= n\n\
    +    Pg   - print good facets (needs 'QGn' or 'QVn')\n\
    +    PFn  - keep facets whose area is at least n\n\
    +    PG   - print neighbors of good facets\n\
    +    PMn  - keep n facets with most merges\n\
    +    Po   - force output.  If error, output neighborhood of facet\n\
    +    Pp   - do not report precision problems\n\
    +\n\
    +    .    - list of all options\n\
    +    -    - one line descriptions of all options\n\
    +    -V   - version\n\
    +";
    +/* for opts, don't assign 'e' or 'E' to a flag (already used for exponent) */
    +
    +/*---------------------------------
    +
    +  qh_prompt2
    +    synopsis for qhull
    +*/
    +char qh_prompt2[]= "\n\
    +qconvex- compute the convex hull.  Qhull %s\n\
    +    input (stdin): dimension, number of points, point coordinates\n\
    +    comments start with a non-numeric character\n\
    +\n\
    +options (qconvex.htm):\n\
    +    Qt   - triangulated output\n\
    +    QJ   - joggled input instead of merged facets\n\
    +    Tv   - verify result: structure, convexity, and point inclusion\n\
    +    .    - concise list of all options\n\
    +    -    - one-line description of all options\n\
    +    -V   - version\n\
    +\n\
    +output options (subset):\n\
    +    s    - summary of results (default)\n\
    +    i    - vertices incident to each facet\n\
    +    n    - normals with offsets\n\
    +    p    - vertex coordinates (includes coplanar points if 'Qc')\n\
    +    Fx   - extreme points (convex hull vertices)\n\
    +    FA   - report total area and volume\n\
    +    FS   - compute total area and volume\n\
    +    o    - OFF format (dim, n, points, facets)\n\
    +    G    - Geomview output (2-d, 3-d, and 4-d)\n\
    +    m    - Mathematica output (2-d and 3-d)\n\
    +    QVn  - print facets that include point n, -n if not\n\
    +    TO file- output results to file, may be enclosed in single quotes\n\
    +\n\
    +examples:\n\
    +    rbox c D2 | qconvex s n                    rbox c D2 | qconvex i\n\
    +    rbox c D2 | qconvex o                      rbox 1000 s | qconvex s Tv FA\n\
    +    rbox c d D2 | qconvex s Qc Fx              rbox y 1000 W0 | qconvex s n\n\
    +    rbox y 1000 W0 | qconvex s QJ              rbox d G1 D12 | qconvex QR0 FA Pp\n\
    +    rbox c D7 | qconvex FA TF1000\n\
    +\n\
    +";
    +/* for opts, don't assign 'e' or 'E' to a flag (already used for exponent) */
    +
    +/*---------------------------------
    +
    +  qh_prompt3
    +    concise prompt for qhull
    +*/
    +char qh_prompt3[]= "\n\
    +Qhull %s.\n\
    +Except for 'F.' and 'PG', upper-case options take an argument.\n\
    +\n\
    + incidences     mathematica    normals        OFF_format     points\n\
    + summary        facet_dump\n\
    +\n\
    + Farea          FArea_total    Fcoplanars     FCentrums      Fd_cdd_in\n\
    + FD_cdd_out     FFacet_xridge  Finner         FIDs           Fmerges\n\
    + Fneighbors     FNeigh_vertex  Fouter         FOptions       FPoint_near\n\
    + FQhull         Fsummary       FSize          Fvertices      FVertex_ave\n\
    + Fxtremes       FMaple\n\
    +\n\
    + Gvertices      Gpoints        Gall_points    Gno_planes     Ginner\n\
    + Gcentrums      Ghyperplanes   Gridges        Gouter         GDrop_dim\n\
    +\n\
    + PArea_keep     Pdrop d0:0D0   PFacet_area_keep Pgood        PGood_neighbors\n\
    + PMerge_keep    Poutput_forced Pprecision_not\n\
    +\n\
    + QbBound 0:0.5  QbB_scale_box  Qcoplanar      QGood_point    Qinterior\n\
    + QJoggle        Qrandom        QRotate        Qsearch_1st    Qtriangulate\n\
    + QVertex_good\n\
    +\n\
    + T4_trace       Tcheck_often   Tstatistics    Tverify        Tz_stdout\n\
    + TFacet_log     TInput_file    TPoint_trace   TMerge_trace   TOutput_file\n\
    + TWide_trace    TVertex_stop   TCone_stop\n\
    +\n\
    + Angle_max      Centrum_size   Random_dist    Ucoplanar_max  Wide_outside\n\
    +";
    +
    +/*---------------------------------
    +
    +  main( argc, argv )
    +    processes the command line, calls qhull() to do the work, and exits
    +
    +  design:
    +    initializes data structures
    +    reads points
    +    finishes initialization
    +    computes convex hull and other structures
    +    checks the result
    +    writes the output
    +    frees memory
    +*/
    +int main(int argc, char *argv[]) {
    +  int curlong, totlong; /* used !qh_NOmem */
    +  int exitcode, numpoints, dim;
    +  coordT *points;
    +  boolT ismalloc;
    +
    +  QHULL_LIB_CHECK /* Check for compatible library */
    +
    +  if ((argc == 1) && isatty( 0 /*stdin*/)) {
    +    fprintf(stdout, qh_prompt2, qh_version);
    +    exit(qh_ERRnone);
    +  }
    +  if (argc > 1 && *argv[1] == '-' && !*(argv[1]+1)) {
    +    fprintf(stdout, qh_prompta, qh_version, qh_DEFAULTbox,
    +                qh_promptb, qh_promptc, qh_promptd, qh_prompte);
    +    exit(qh_ERRnone);
    +  }
    +  if (argc > 1 && *argv[1] == '.' && !*(argv[1]+1)) {
    +    fprintf(stdout, qh_prompt3, qh_version);
    +    exit(qh_ERRnone);
    +  }
    +  if (argc > 1 && *argv[1] == '-' && *(argv[1]+1)=='V') {
    +      fprintf(stdout, "%s\n", qh_version2);
    +      exit(qh_ERRnone);
    +  }
    +  qh_init_A(stdin, stdout, stderr, argc, argv);  /* sets qh qhull_command */
    +  exitcode= setjmp(qh errexit); /* simple statement for CRAY J916 */
    +  if (!exitcode) {
    +    qh NOerrexit = False;
    +    qh_checkflags(qh qhull_command, hidden_options);
    +    qh_initflags(qh qhull_command);
    +    points= qh_readpoints(&numpoints, &dim, &ismalloc);
    +    if (dim >= 5) {
    +      qh_option("Qxact_merge", NULL, NULL);
    +      qh MERGEexact= True; /* 'Qx' always */
    +    }
    +    qh_init_B(points, numpoints, dim, ismalloc);
    +    qh_qhull();
    +    qh_check_output();
    +    qh_produce_output();
    +    if (qh VERIFYoutput && !qh FORCEoutput && !qh STOPpoint && !qh STOPcone)
    +      qh_check_points();
    +    exitcode= qh_ERRnone;
    +  }
    +  qh NOerrexit= True;  /* no more setjmp */
    +#ifdef qh_NOmem
    +  qh_freeqhull(qh_ALL);
    +#else
    +  qh_freeqhull(!qh_ALL);
    +  qh_memfreeshort(&curlong, &totlong);
    +  if (curlong || totlong)
    +    qh_fprintf_stderr(6263, "qhull internal warning (main): did not free %d bytes of long memory(%d pieces)\n",
    +       totlong, curlong);
    +#endif
    +  return exitcode;
    +} /* main */
    +
    diff --git a/xs/src/qhull/src/qconvex/qconvex.pro b/xs/src/qhull/src/qconvex/qconvex.pro
    new file mode 100644
    index 000000000..1bf631bff
    --- /dev/null
    +++ b/xs/src/qhull/src/qconvex/qconvex.pro
    @@ -0,0 +1,9 @@
    +# -------------------------------------------------
    +# qconvex.pro -- Qt project file for qconvex.exe
    +# -------------------------------------------------
    +
    +include(../qhull-app-c.pri)
    +
    +TARGET = qconvex
    +
    +SOURCES += qconvex.c
    diff --git a/xs/src/qhull/src/qconvex/qconvex_r.c b/xs/src/qhull/src/qconvex/qconvex_r.c
    new file mode 100644
    index 000000000..abf68ce37
    --- /dev/null
    +++ b/xs/src/qhull/src/qconvex/qconvex_r.c
    @@ -0,0 +1,328 @@
    +/*
      ---------------------------------
    +
    +   qconvex.c
    +      compute convex hulls using qhull
    +
    +   see unix.c for full interface
    +
    +   Copyright (c) 1993-2015, The Geometry Center
    +*/
    +
    +#include "libqhull_r/libqhull_r.h"
    +
    +#include 
    +#include 
    +#include 
    +#include 
    +#include 
    +
    +#if __cplusplus
    +extern "C" {
    +  int isatty(int);
    +}
    +
    +#elif _MSC_VER
    +#include 
    +#define isatty _isatty
    +/* int _isatty(int); */
    +
    +#else
    +int isatty(int);  /* returns 1 if stdin is a tty
    +                   if "Undefined symbol" this can be deleted along with call in main() */
    +#endif
    +
    +/*---------------------------------
    +
    +  qh_prompt
    +    long prompt for qconvex
    +
    +  notes:
    +    restricted version of libqhull.c
    +
    +  see:
    +    concise prompt below
    +*/
    +
    +/* duplicated in qconvex.htm */
    +char hidden_options[]=" d v H Qbb Qf Qg Qm Qr Qu Qv Qx Qz TR E V Fp Gt Q0 Q1 Q2 Q3 Q4 Q5 Q6 Q7 Q8 Q9 ";
    +
    +char qh_prompta[]= "\n\
    +qconvex- compute the convex hull\n\
    +    http://www.qhull.org  %s\n\
    +\n\
    +input (stdin):\n\
    +    first lines: dimension and number of points (or vice-versa).\n\
    +    other lines: point coordinates, best if one point per line\n\
    +    comments:    start with a non-numeric character\n\
    +\n\
    +options:\n\
    +    Qt   - triangulated output\n\
    +    QJ   - joggled input instead of merged facets\n\
    +    Qc   - keep coplanar points with nearest facet\n\
    +    Qi   - keep interior points with nearest facet\n\
    +\n\
    +Qhull control options:\n\
    +    Qbk:n   - scale coord k so that low bound is n\n\
    +      QBk:n - scale coord k so that upper bound is n (QBk is %2.2g)\n\
    +    QbB  - scale input to unit cube centered at the origin\n\
    +    Qbk:0Bk:0 - remove k-th coordinate from input\n\
    +    QJn  - randomly joggle input in range [-n,n]\n\
    +    QRn  - random rotation (n=seed, n=0 time, n=-1 time/no rotate)\n\
    +%s%s%s%s";  /* split up qh_prompt for Visual C++ */
    +char qh_promptb[]= "\
    +    Qs   - search all points for the initial simplex\n\
    +    QGn  - good facet if visible from point n, -n for not visible\n\
    +    QVn  - good facet if it includes point n, -n if not\n\
    +\n\
    +";
    +char qh_promptc[]= "\
    +Trace options:\n\
    +    T4   - trace at level n, 4=all, 5=mem/gauss, -1= events\n\
    +    Tc   - check frequently during execution\n\
    +    Ts   - print statistics\n\
    +    Tv   - verify result: structure, convexity, and point inclusion\n\
    +    Tz   - send all output to stdout\n\
    +    TFn  - report summary when n or more facets created\n\
    +    TI file - input data from file, no spaces or single quotes\n\
    +    TO file - output results to file, may be enclosed in single quotes\n\
    +    TPn  - turn on tracing when point n added to hull\n\
    +     TMn - turn on tracing at merge n\n\
    +     TWn - trace merge facets when width > n\n\
    +    TVn  - stop qhull after adding point n, -n for before (see TCn)\n\
    +     TCn - stop qhull after building cone for point n (see TVn)\n\
    +\n\
    +Precision options:\n\
    +    Cn   - radius of centrum (roundoff added).  Merge facets if non-convex\n\
    +     An  - cosine of maximum angle.  Merge facets if cosine > n or non-convex\n\
    +           C-0 roundoff, A-0.99/C-0.01 pre-merge, A0.99/C0.01 post-merge\n\
    +    Rn   - randomly perturb computations by a factor of [1-n,1+n]\n\
    +    Un   - max distance below plane for a new, coplanar point\n\
    +    Wn   - min facet width for outside point (before roundoff)\n\
    +\n\
    +Output formats (may be combined; if none, produces a summary to stdout):\n\
    +    f    - facet dump\n\
    +    G    - Geomview output (see below)\n\
    +    i    - vertices incident to each facet\n\
    +    m    - Mathematica output (2-d and 3-d)\n\
    +    n    - normals with offsets\n\
    +    o    - OFF file format (dim, points and facets; Voronoi regions)\n\
    +    p    - point coordinates \n\
    +    s    - summary (stderr)\n\
    +\n\
    +";
    +char qh_promptd[]= "\
    +More formats:\n\
    +    Fa   - area for each facet\n\
    +    FA   - compute total area and volume for option 's'\n\
    +    Fc   - count plus coplanar points for each facet\n\
    +           use 'Qc' (default) for coplanar and 'Qi' for interior\n\
    +    FC   - centrum for each facet\n\
    +    Fd   - use cdd format for input (homogeneous with offset first)\n\
    +    FD   - use cdd format for numeric output (offset first)\n\
    +    FF   - facet dump without ridges\n\
    +    Fi   - inner plane for each facet\n\
    +    FI   - ID for each facet\n\
    +    Fm   - merge count for each facet (511 max)\n\
    +    Fn   - count plus neighboring facets for each facet\n\
    +    FN   - count plus neighboring facets for each point\n\
    +    Fo   - outer plane (or max_outside) for each facet\n\
    +    FO   - options and precision constants\n\
    +    FP   - nearest vertex for each coplanar point\n\
    +    FQ   - command used for qconvex\n\
    +    Fs   - summary: #int (8), dimension, #points, tot vertices, tot facets,\n\
    +                      for output: #vertices, #facets,\n\
    +                                  #coplanar points, #non-simplicial facets\n\
    +                    #real (2), max outer plane, min vertex\n\
    +    FS   - sizes:   #int (0) \n\
    +                    #real (2) tot area, tot volume\n\
    +    Ft   - triangulation with centrums for non-simplicial facets (OFF format)\n\
    +    Fv   - count plus vertices for each facet\n\
    +    FV   - average of vertices (a feasible point for 'H')\n\
    +    Fx   - extreme points (in order for 2-d)\n\
    +\n\
    +";
    +char qh_prompte[]= "\
    +Geomview output (2-d, 3-d, and 4-d)\n\
    +    Ga   - all points as dots\n\
    +     Gp  -  coplanar points and vertices as radii\n\
    +     Gv  -  vertices as spheres\n\
    +    Gi   - inner planes only\n\
    +     Gn  -  no planes\n\
    +     Go  -  outer planes only\n\
    +    Gc   - centrums\n\
    +    Gh   - hyperplane intersections\n\
    +    Gr   - ridges\n\
    +    GDn  - drop dimension n in 3-d and 4-d output\n\
    +\n\
    +Print options:\n\
    +    PAn  - keep n largest facets by area\n\
    +    Pdk:n - drop facet if normal[k] <= n (default 0.0)\n\
    +    PDk:n - drop facet if normal[k] >= n\n\
    +    Pg   - print good facets (needs 'QGn' or 'QVn')\n\
    +    PFn  - keep facets whose area is at least n\n\
    +    PG   - print neighbors of good facets\n\
    +    PMn  - keep n facets with most merges\n\
    +    Po   - force output.  If error, output neighborhood of facet\n\
    +    Pp   - do not report precision problems\n\
    +\n\
    +    .    - list of all options\n\
    +    -    - one line descriptions of all options\n\
    +    -V   - version\n\
    +";
    +/* for opts, don't assign 'e' or 'E' to a flag (already used for exponent) */
    +
    +/*---------------------------------
    +
    +  qh_prompt2
    +    synopsis for qhull
    +*/
    +char qh_prompt2[]= "\n\
    +qconvex- compute the convex hull.  Qhull %s\n\
    +    input (stdin): dimension, number of points, point coordinates\n\
    +    comments start with a non-numeric character\n\
    +\n\
    +options (qconvex.htm):\n\
    +    Qt   - triangulated output\n\
    +    QJ   - joggled input instead of merged facets\n\
    +    Tv   - verify result: structure, convexity, and point inclusion\n\
    +    .    - concise list of all options\n\
    +    -    - one-line description of all options\n\
    +    -V   - version\n\
    +\n\
    +output options (subset):\n\
    +    s    - summary of results (default)\n\
    +    i    - vertices incident to each facet\n\
    +    n    - normals with offsets\n\
    +    p    - vertex coordinates (includes coplanar points if 'Qc')\n\
    +    Fx   - extreme points (convex hull vertices)\n\
    +    FA   - report total area and volume\n\
    +    FS   - compute total area and volume\n\
    +    o    - OFF format (dim, n, points, facets)\n\
    +    G    - Geomview output (2-d, 3-d, and 4-d)\n\
    +    m    - Mathematica output (2-d and 3-d)\n\
    +    QVn  - print facets that include point n, -n if not\n\
    +    TO file- output results to file, may be enclosed in single quotes\n\
    +\n\
    +examples:\n\
    +    rbox c D2 | qconvex s n                    rbox c D2 | qconvex i\n\
    +    rbox c D2 | qconvex o                      rbox 1000 s | qconvex s Tv FA\n\
    +    rbox c d D2 | qconvex s Qc Fx              rbox y 1000 W0 | qconvex s n\n\
    +    rbox y 1000 W0 | qconvex s QJ              rbox d G1 D12 | qconvex QR0 FA Pp\n\
    +    rbox c D7 | qconvex FA TF1000\n\
    +\n\
    +";
    +/* for opts, don't assign 'e' or 'E' to a flag (already used for exponent) */
    +
    +/*---------------------------------
    +
    +  qh_prompt3
    +    concise prompt for qhull
    +*/
    +char qh_prompt3[]= "\n\
    +Qhull %s.\n\
    +Except for 'F.' and 'PG', upper-case options take an argument.\n\
    +\n\
    + incidences     mathematica    normals        OFF_format     points\n\
    + summary        facet_dump\n\
    +\n\
    + Farea          FArea_total    Fcoplanars     FCentrums      Fd_cdd_in\n\
    + FD_cdd_out     FFacet_xridge  Finner         FIDs           Fmerges\n\
    + Fneighbors     FNeigh_vertex  Fouter         FOptions       FPoint_near\n\
    + FQhull         Fsummary       FSize          Fvertices      FVertex_ave\n\
    + Fxtremes       FMaple\n\
    +\n\
    + Gvertices      Gpoints        Gall_points    Gno_planes     Ginner\n\
    + Gcentrums      Ghyperplanes   Gridges        Gouter         GDrop_dim\n\
    +\n\
    + PArea_keep     Pdrop d0:0D0   PFacet_area_keep Pgood        PGood_neighbors\n\
    + PMerge_keep    Poutput_forced Pprecision_not\n\
    +\n\
    + QbBound 0:0.5  QbB_scale_box  Qcoplanar      QGood_point    Qinterior\n\
    + QJoggle        Qrandom        QRotate        Qsearch_1st    Qtriangulate\n\
    + QVertex_good\n\
    +\n\
    + T4_trace       Tcheck_often   Tstatistics    Tverify        Tz_stdout\n\
    + TFacet_log     TInput_file    TPoint_trace   TMerge_trace   TOutput_file\n\
    + TWide_trace    TVertex_stop   TCone_stop\n\
    +\n\
    + Angle_max      Centrum_size   Random_dist    Ucoplanar_max  Wide_outside\n\
    +";
    +
    +/*---------------------------------
    +
    +  main( argc, argv )
    +    processes the command line, calls qhull() to do the work, and exits
    +
    +  design:
    +    initializes data structures
    +    reads points
    +    finishes initialization
    +    computes convex hull and other structures
    +    checks the result
    +    writes the output
    +    frees memory
    +*/
    +int main(int argc, char *argv[]) {
    +  int curlong, totlong; /* used !qh_NOmem */
    +  int exitcode, numpoints, dim;
    +  coordT *points;
    +  boolT ismalloc;
    +  qhT qh_qh;
    +  qhT *qh= &qh_qh;
    +
    +  QHULL_LIB_CHECK /* Check for compatible library */
    +
    +  if ((argc == 1) && isatty( 0 /*stdin*/)) {
    +    fprintf(stdout, qh_prompt2, qh_version);
    +    exit(qh_ERRnone);
    +  }
    +  if (argc > 1 && *argv[1] == '-' && !*(argv[1]+1)) {
    +    fprintf(stdout, qh_prompta, qh_version, qh_DEFAULTbox,
    +                qh_promptb, qh_promptc, qh_promptd, qh_prompte);
    +    exit(qh_ERRnone);
    +  }
    +  if (argc > 1 && *argv[1] == '.' && !*(argv[1]+1)) {
    +    fprintf(stdout, qh_prompt3, qh_version);
    +    exit(qh_ERRnone);
    +  }
    +  if (argc > 1 && *argv[1] == '-' && *(argv[1]+1)=='V') {
    +      fprintf(stdout, "%s\n", qh_version2);
    +      exit(qh_ERRnone);
    +  }
    +  qh_init_A(qh, stdin, stdout, stderr, argc, argv);  /* sets qh->qhull_command */
    +  exitcode= setjmp(qh->errexit); /* simple statement for CRAY J916 */
    +  if (!exitcode) {
    +    qh->NOerrexit = False;
    +    qh_checkflags(qh, qh->qhull_command, hidden_options);
    +    qh_initflags(qh, qh->qhull_command);
    +    points= qh_readpoints(qh, &numpoints, &dim, &ismalloc);
    +    if (dim >= 5) {
    +      qh_option(qh, "Qxact_merge", NULL, NULL);
    +      qh->MERGEexact= True; /* 'Qx' always */
    +    }
    +    qh_init_B(qh, points, numpoints, dim, ismalloc);
    +    qh_qhull(qh);
    +    qh_check_output(qh);
    +    qh_produce_output(qh);
    +    if (qh->VERIFYoutput && !qh->FORCEoutput && !qh->STOPpoint && !qh->STOPcone)
    +      qh_check_points(qh);
    +    exitcode= qh_ERRnone;
    +  }
    +  qh->NOerrexit= True;  /* no more setjmp */
    +#ifdef qh_NOmem
    +  qh_freeqhull(qh, qh_ALL);
    +#else
    +  qh_freeqhull(qh, !qh_ALL);
    +  qh_memfreeshort(qh, &curlong, &totlong);
    +  if (curlong || totlong)
    +    qh_fprintf_stderr(6263, "qhull internal warning (main): did not free %d bytes of long memory(%d pieces)\n",
    +       totlong, curlong);
    +#endif
    +  return exitcode;
    +} /* main */
    +
    diff --git a/xs/src/qhull/src/qdelaunay/qdelaun.c b/xs/src/qhull/src/qdelaunay/qdelaun.c
    new file mode 100644
    index 000000000..9011d9fcc
    --- /dev/null
    +++ b/xs/src/qhull/src/qdelaunay/qdelaun.c
    @@ -0,0 +1,315 @@
    +/*
      ---------------------------------
    +
    +   qdelaun.c
    +     compute Delaunay triangulations and furthest-point Delaunay
    +     triangulations using qhull
    +
    +   see unix.c for full interface
    +
    +   Copyright (c) 1993-2015, The Geometry Center
    +*/
    +
    +#include "libqhull/libqhull.h"
    +
    +#include 
    +#include 
    +#include 
    +#include 
    +#include 
    +
    +#if __cplusplus
    +extern "C" {
    +  int isatty(int);
    +}
    +
    +#elif _MSC_VER
    +#include 
    +#define isatty _isatty
    +/* int _isatty(int); */
    +
    +#else
    +int isatty(int);  /* returns 1 if stdin is a tty
    +                   if "Undefined symbol" this can be deleted along with call in main() */
    +#endif
    +
    +/*---------------------------------
    +
    +  qh_prompt
    +    long prompt for qhull
    +
    +  notes:
    +    restricted version of libqhull.c
    +
    +  see:
    +    concise prompt below
    +*/
    +
    +/* duplicated in qdelau_f.htm and qdelaun.htm */
    +char hidden_options[]=" d n v H U Qb QB Qc Qf Qg Qi Qm Qr QR Qv Qx TR E V FC Fi Fo Ft Fp FV Q0 Q1 Q2 Q3 Q4 Q5 Q6 Q7 Q8 Q9 ";
    +
    +char qh_prompta[]= "\n\
    +qdelaunay- compute the Delaunay triangulation\n\
    +    http://www.qhull.org  %s\n\
    +\n\
    +input (stdin):\n\
    +    first lines: dimension and number of points (or vice-versa).\n\
    +    other lines: point coordinates, best if one point per line\n\
    +    comments:    start with a non-numeric character\n\
    +\n\
    +options:\n\
    +    Qu   - compute furthest-site Delaunay triangulation\n\
    +    Qt   - triangulated output\n\
    +    QJ   - joggled input instead of merged facets\n\
    +\n\
    +Qhull control options:\n\
    +    QJn  - randomly joggle input in range [-n,n]\n\
    +%s%s%s%s";  /* split up qh_prompt for Visual C++ */
    +char qh_promptb[]= "\
    +    Qs   - search all points for the initial simplex\n\
    +    Qz   - add point-at-infinity to Delaunay triangulation\n\
    +    QGn  - print Delaunay region if visible from point n, -n if not\n\
    +    QVn  - print Delaunay regions that include point n, -n if not\n\
    +\n\
    +";
    +char qh_promptc[]= "\
    +Trace options:\n\
    +    T4   - trace at level n, 4=all, 5=mem/gauss, -1= events\n\
    +    Tc   - check frequently during execution\n\
    +    Ts   - print statistics\n\
    +    Tv   - verify result: structure, convexity, and in-circle test\n\
    +    Tz   - send all output to stdout\n\
    +    TFn  - report summary when n or more facets created\n\
    +    TI file - input data from file, no spaces or single quotes\n\
    +    TO file - output results to file, may be enclosed in single quotes\n\
    +    TPn  - turn on tracing when point n added to hull\n\
    +     TMn - turn on tracing at merge n\n\
    +     TWn - trace merge facets when width > n\n\
    +    TVn  - stop qhull after adding point n, -n for before (see TCn)\n\
    +     TCn - stop qhull after building cone for point n (see TVn)\n\
    +\n\
    +Precision options:\n\
    +    Cn   - radius of centrum (roundoff added).  Merge facets if non-convex\n\
    +     An  - cosine of maximum angle.  Merge facets if cosine > n or non-convex\n\
    +           C-0 roundoff, A-0.99/C-0.01 pre-merge, A0.99/C0.01 post-merge\n\
    +    Rn   - randomly perturb computations by a factor of [1-n,1+n]\n\
    +    Wn   - min facet width for outside point (before roundoff)\n\
    +\n\
    +Output formats (may be combined; if none, produces a summary to stdout):\n\
    +    f    - facet dump\n\
    +    G    - Geomview output (see below)\n\
    +    i    - vertices incident to each Delaunay region\n\
    +    m    - Mathematica output (2-d only, lifted to a paraboloid)\n\
    +    o    - OFF format (dim, points, and facets as a paraboloid)\n\
    +    p    - point coordinates (lifted to a paraboloid)\n\
    +    s    - summary (stderr)\n\
    +\n\
    +";
    +char qh_promptd[]= "\
    +More formats:\n\
    +    Fa   - area for each Delaunay region\n\
    +    FA   - compute total area for option 's'\n\
    +    Fc   - count plus coincident points for each Delaunay region\n\
    +    Fd   - use cdd format for input (homogeneous with offset first)\n\
    +    FD   - use cdd format for numeric output (offset first)\n\
    +    FF   - facet dump without ridges\n\
    +    FI   - ID of each Delaunay region\n\
    +    Fm   - merge count for each Delaunay region (511 max)\n\
    +    FM   - Maple output (2-d only, lifted to a paraboloid)\n\
    +    Fn   - count plus neighboring region for each Delaunay region\n\
    +    FN   - count plus neighboring region for each point\n\
    +    FO   - options and precision constants\n\
    +    FP   - nearest point and distance for each coincident point\n\
    +    FQ   - command used for qdelaunay\n\
    +    Fs   - summary: #int (8), dimension, #points, tot vertices, tot facets,\n\
    +                    for output: #vertices, #Delaunay regions,\n\
    +                                #coincident points, #non-simplicial regions\n\
    +                    #real (2), max outer plane, min vertex\n\
    +    FS   - sizes:   #int (0)\n\
    +                    #real (2), tot area, 0\n\
    +    Fv   - count plus vertices for each Delaunay region\n\
    +    Fx   - extreme points of Delaunay triangulation (on convex hull)\n\
    +\n\
    +";
    +char qh_prompte[]= "\
    +Geomview options (2-d and 3-d)\n\
    +    Ga   - all points as dots\n\
    +     Gp  -  coplanar points and vertices as radii\n\
    +     Gv  -  vertices as spheres\n\
    +    Gi   - inner planes only\n\
    +     Gn  -  no planes\n\
    +     Go  -  outer planes only\n\
    +    Gc     - centrums\n\
    +    Gh   - hyperplane intersections\n\
    +    Gr   - ridges\n\
    +    GDn  - drop dimension n in 3-d and 4-d output\n\
    +    Gt   - transparent outer ridges to view 3-d Delaunay\n\
    +\n\
    +Print options:\n\
    +    PAn  - keep n largest Delaunay regions by area\n\
    +    Pdk:n - drop facet if normal[k] <= n (default 0.0)\n\
    +    PDk:n - drop facet if normal[k] >= n\n\
    +    Pg   - print good Delaunay regions (needs 'QGn' or 'QVn')\n\
    +    PFn  - keep Delaunay regions whose area is at least n\n\
    +    PG   - print neighbors of good regions (needs 'QGn' or 'QVn')\n\
    +    PMn  - keep n Delaunay regions with most merges\n\
    +    Po   - force output.  If error, output neighborhood of facet\n\
    +    Pp   - do not report precision problems\n\
    +\n\
    +    .    - list of all options\n\
    +    -    - one line descriptions of all options\n\
    +    -V   - version\n\
    +";
    +/* for opts, don't assign 'e' or 'E' to a flag (already used for exponent) */
    +
    +/*---------------------------------
    +
    +  qh_prompt2
    +    synopsis for qhull
    +*/
    +char qh_prompt2[]= "\n\
    +qdelaunay- compute the Delaunay triangulation.  Qhull %s\n\
    +    input (stdin): dimension, number of points, point coordinates\n\
    +    comments start with a non-numeric character\n\
    +\n\
    +options (qdelaun.htm):\n\
    +    Qu   - furthest-site Delaunay triangulation\n\
    +    Qt   - triangulated output\n\
    +    QJ   - joggled input instead of merged facets\n\
    +    Tv   - verify result: structure, convexity, and in-circle test\n\
    +    .    - concise list of all options\n\
    +    -    - one-line description of all options\n\
    +    -V   - version\n\
    +\n\
    +output options (subset):\n\
    +    s    - summary of results (default)\n\
    +    i    - vertices incident to each Delaunay region\n\
    +    Fx   - extreme points (vertices of the convex hull)\n\
    +    o    - OFF format (shows the points lifted to a paraboloid)\n\
    +    G    - Geomview output (2-d and 3-d points lifted to a paraboloid)\n\
    +    m    - Mathematica output (2-d inputs lifted to a paraboloid)\n\
    +    QVn  - print Delaunay regions that include point n, -n if not\n\
    +    TO file- output results to file, may be enclosed in single quotes\n\
    +\n\
    +examples:\n\
    +    rbox c P0 D2 | qdelaunay s o          rbox c P0 D2 | qdelaunay i\n\
    +    rbox c P0 D2 | qdelaunay Fv           rbox c P0 D2 | qdelaunay s Qu Fv\n\
    +    rbox c G1 d D2 | qdelaunay s i        rbox c G1 d D2 | qdelaunay Qt\n\
    +    rbox M3,4 z 100 D2 | qdelaunay s      rbox M3,4 z 100 D2 | qdelaunay s Qt\n\
    +\n\
    +";
    +/* for opts, don't assign 'e' or 'E' to a flag (already used for exponent) */
    +
    +/*---------------------------------
    +
    +  qh_prompt3
    +    concise prompt for qhull
    +*/
    +char qh_prompt3[]= "\n\
    +Qhull %s.\n\
    +Except for 'F.' and 'PG', upper-case options take an argument.\n\
    +\n\
    + incidences     mathematica    OFF_format     points_lifted  summary\n\
    + facet_dump\n\
    +\n\
    + Farea          FArea_total    Fcoincident    Fd_cdd_in      FD_cdd_out\n\
    + FF_dump_xridge FIDs           Fmerges        Fneighbors     FNeigh_vertex\n\
    + FOptions       FPoint_near    FQdelaun       Fsummary       FSize\n\
    + Fvertices      Fxtremes       FMaple\n\
    +\n\
    + Gvertices      Gpoints        Gall_points    Gno_planes     Ginner\n\
    + Gcentrums      Ghyperplanes   Gridges        Gouter         GDrop_dim\n\
    + Gtransparent\n\
    +\n\
    + PArea_keep     Pdrop d0:0D0   Pgood          PFacet_area_keep\n\
    + PGood_neighbors PMerge_keep   Poutput_forced Pprecision_not\n\
    +\n\
    + QGood_point    QJoggle        Qsearch_1st    Qtriangulate   QupperDelaunay\n\
    + QVertex_good   Qzinfinite\n\
    +\n\
    + T4_trace       Tcheck_often   Tstatistics    Tverify        Tz_stdout\n\
    + TFacet_log     TInput_file    TPoint_trace   TMerge_trace   TOutput_file\n\
    + TWide_trace    TVertex_stop   TCone_stop\n\
    +\n\
    + Angle_max      Centrum_size   Random_dist    Wide_outside\n\
    +";
    +
    +/*---------------------------------
    +
    +  main( argc, argv )
    +    processes the command line, calls qhull() to do the work, and exits
    +
    +  design:
    +    initializes data structures
    +    reads points
    +    finishes initialization
    +    computes convex hull and other structures
    +    checks the result
    +    writes the output
    +    frees memory
    +*/
    +int main(int argc, char *argv[]) {
    +  int curlong, totlong; /* used !qh_NOmem */
    +  int exitcode, numpoints, dim;
    +  coordT *points;
    +  boolT ismalloc;
    +
    +  QHULL_LIB_CHECK /* Check for compatible library */
    +
    +  if ((argc == 1) && isatty( 0 /*stdin*/)) {
    +    fprintf(stdout, qh_prompt2, qh_version);
    +    exit(qh_ERRnone);
    +  }
    +  if (argc > 1 && *argv[1] == '-' && !*(argv[1]+1)) {
    +    fprintf(stdout, qh_prompta, qh_version,
    +                qh_promptb, qh_promptc, qh_promptd, qh_prompte);
    +    exit(qh_ERRnone);
    +  }
    +  if (argc > 1 && *argv[1] == '.' && !*(argv[1]+1)) {
    +    fprintf(stdout, qh_prompt3, qh_version);
    +    exit(qh_ERRnone);
    +  }
    +  if (argc > 1 && *argv[1] == '-' && *(argv[1]+1)=='V') {
    +      fprintf(stdout, "%s\n", qh_version2);
    +      exit(qh_ERRnone);
    +  }
    +  qh_init_A(stdin, stdout, stderr, argc, argv);  /* sets qh qhull_command */
    +  exitcode= setjmp(qh errexit); /* simple statement for CRAY J916 */
    +  if (!exitcode) {
    +    qh NOerrexit = False;
    +    qh_option("delaunay  Qbbound-last", NULL, NULL);
    +    qh DELAUNAY= True;     /* 'd'   */
    +    qh SCALElast= True;    /* 'Qbb' */
    +    qh KEEPcoplanar= True; /* 'Qc', to keep coplanars in 'p' */
    +    qh_checkflags(qh qhull_command, hidden_options);
    +    qh_initflags(qh qhull_command);
    +    points= qh_readpoints(&numpoints, &dim, &ismalloc);
    +    if (dim >= 5) {
    +      qh_option("Qxact_merge", NULL, NULL);
    +      qh MERGEexact= True; /* 'Qx' always */
    +    }
    +    qh_init_B(points, numpoints, dim, ismalloc);
    +    qh_qhull();
    +    qh_check_output();
    +    qh_produce_output();
    +    if (qh VERIFYoutput && !qh FORCEoutput && !qh STOPpoint && !qh STOPcone)
    +      qh_check_points();
    +    exitcode= qh_ERRnone;
    +  }
    +  qh NOerrexit= True;  /* no more setjmp */
    +#ifdef qh_NOmem
    +  qh_freeqhull(qh_ALL);
    +#else
    +  qh_freeqhull(!qh_ALL);
    +  qh_memfreeshort(&curlong, &totlong);
    +  if (curlong || totlong)
    +    qh_fprintf_stderr(6263, "qhull internal warning (main): did not free %d bytes of long memory(%d pieces)\n",
    +       totlong, curlong);
    +#endif
    +  return exitcode;
    +} /* main */
    +
    diff --git a/xs/src/qhull/src/qdelaunay/qdelaun_r.c b/xs/src/qhull/src/qdelaunay/qdelaun_r.c
    new file mode 100644
    index 000000000..0854b8bb9
    --- /dev/null
    +++ b/xs/src/qhull/src/qdelaunay/qdelaun_r.c
    @@ -0,0 +1,317 @@
    +/*
      ---------------------------------
    +
    +   qdelaun.c
    +     compute Delaunay triangulations and furthest-point Delaunay
    +     triangulations using qhull
    +
    +   see unix.c for full interface
    +
    +   Copyright (c) 1993-2015, The Geometry Center
    +*/
    +
    +#include "libqhull_r/libqhull_r.h"
    +
    +#include 
    +#include 
    +#include 
    +#include 
    +#include 
    +
    +#if __cplusplus
    +extern "C" {
    +  int isatty(int);
    +}
    +
    +#elif _MSC_VER
    +#include 
    +#define isatty _isatty
    +/* int _isatty(int); */
    +
    +#else
    +int isatty(int);  /* returns 1 if stdin is a tty
    +                   if "Undefined symbol" this can be deleted along with call in main() */
    +#endif
    +
    +/*---------------------------------
    +
    +  qh_prompt
    +    long prompt for qhull
    +
    +  notes:
    +    restricted version of libqhull.c
    +
    +  see:
    +    concise prompt below
    +*/
    +
    +/* duplicated in qdelau_f.htm and qdelaun.htm */
    +char hidden_options[]=" d n v H U Qb QB Qc Qf Qg Qi Qm Qr QR Qv Qx TR E V FC Fi Fo Ft Fp FV Q0 Q1 Q2 Q3 Q4 Q5 Q6 Q7 Q8 Q9 ";
    +
    +char qh_prompta[]= "\n\
    +qdelaunay- compute the Delaunay triangulation\n\
    +    http://www.qhull.org  %s\n\
    +\n\
    +input (stdin):\n\
    +    first lines: dimension and number of points (or vice-versa).\n\
    +    other lines: point coordinates, best if one point per line\n\
    +    comments:    start with a non-numeric character\n\
    +\n\
    +options:\n\
    +    Qu   - compute furthest-site Delaunay triangulation\n\
    +    Qt   - triangulated output\n\
    +    QJ   - joggled input instead of merged facets\n\
    +\n\
    +Qhull control options:\n\
    +    QJn  - randomly joggle input in range [-n,n]\n\
    +%s%s%s%s";  /* split up qh_prompt for Visual C++ */
    +char qh_promptb[]= "\
    +    Qs   - search all points for the initial simplex\n\
    +    Qz   - add point-at-infinity to Delaunay triangulation\n\
    +    QGn  - print Delaunay region if visible from point n, -n if not\n\
    +    QVn  - print Delaunay regions that include point n, -n if not\n\
    +\n\
    +";
    +char qh_promptc[]= "\
    +Trace options:\n\
    +    T4   - trace at level n, 4=all, 5=mem/gauss, -1= events\n\
    +    Tc   - check frequently during execution\n\
    +    Ts   - print statistics\n\
    +    Tv   - verify result: structure, convexity, and in-circle test\n\
    +    Tz   - send all output to stdout\n\
    +    TFn  - report summary when n or more facets created\n\
    +    TI file - input data from file, no spaces or single quotes\n\
    +    TO file - output results to file, may be enclosed in single quotes\n\
    +    TPn  - turn on tracing when point n added to hull\n\
    +     TMn - turn on tracing at merge n\n\
    +     TWn - trace merge facets when width > n\n\
    +    TVn  - stop qhull after adding point n, -n for before (see TCn)\n\
    +     TCn - stop qhull after building cone for point n (see TVn)\n\
    +\n\
    +Precision options:\n\
    +    Cn   - radius of centrum (roundoff added).  Merge facets if non-convex\n\
    +     An  - cosine of maximum angle.  Merge facets if cosine > n or non-convex\n\
    +           C-0 roundoff, A-0.99/C-0.01 pre-merge, A0.99/C0.01 post-merge\n\
    +    Rn   - randomly perturb computations by a factor of [1-n,1+n]\n\
    +    Wn   - min facet width for outside point (before roundoff)\n\
    +\n\
    +Output formats (may be combined; if none, produces a summary to stdout):\n\
    +    f    - facet dump\n\
    +    G    - Geomview output (see below)\n\
    +    i    - vertices incident to each Delaunay region\n\
    +    m    - Mathematica output (2-d only, lifted to a paraboloid)\n\
    +    o    - OFF format (dim, points, and facets as a paraboloid)\n\
    +    p    - point coordinates (lifted to a paraboloid)\n\
    +    s    - summary (stderr)\n\
    +\n\
    +";
    +char qh_promptd[]= "\
    +More formats:\n\
    +    Fa   - area for each Delaunay region\n\
    +    FA   - compute total area for option 's'\n\
    +    Fc   - count plus coincident points for each Delaunay region\n\
    +    Fd   - use cdd format for input (homogeneous with offset first)\n\
    +    FD   - use cdd format for numeric output (offset first)\n\
    +    FF   - facet dump without ridges\n\
    +    FI   - ID of each Delaunay region\n\
    +    Fm   - merge count for each Delaunay region (511 max)\n\
    +    FM   - Maple output (2-d only, lifted to a paraboloid)\n\
    +    Fn   - count plus neighboring region for each Delaunay region\n\
    +    FN   - count plus neighboring region for each point\n\
    +    FO   - options and precision constants\n\
    +    FP   - nearest point and distance for each coincident point\n\
    +    FQ   - command used for qdelaunay\n\
    +    Fs   - summary: #int (8), dimension, #points, tot vertices, tot facets,\n\
    +                    for output: #vertices, #Delaunay regions,\n\
    +                                #coincident points, #non-simplicial regions\n\
    +                    #real (2), max outer plane, min vertex\n\
    +    FS   - sizes:   #int (0)\n\
    +                    #real (2), tot area, 0\n\
    +    Fv   - count plus vertices for each Delaunay region\n\
    +    Fx   - extreme points of Delaunay triangulation (on convex hull)\n\
    +\n\
    +";
    +char qh_prompte[]= "\
    +Geomview options (2-d and 3-d)\n\
    +    Ga   - all points as dots\n\
    +     Gp  -  coplanar points and vertices as radii\n\
    +     Gv  -  vertices as spheres\n\
    +    Gi   - inner planes only\n\
    +     Gn  -  no planes\n\
    +     Go  -  outer planes only\n\
    +    Gc     - centrums\n\
    +    Gh   - hyperplane intersections\n\
    +    Gr   - ridges\n\
    +    GDn  - drop dimension n in 3-d and 4-d output\n\
    +    Gt   - transparent outer ridges to view 3-d Delaunay\n\
    +\n\
    +Print options:\n\
    +    PAn  - keep n largest Delaunay regions by area\n\
    +    Pdk:n - drop facet if normal[k] <= n (default 0.0)\n\
    +    PDk:n - drop facet if normal[k] >= n\n\
    +    Pg   - print good Delaunay regions (needs 'QGn' or 'QVn')\n\
    +    PFn  - keep Delaunay regions whose area is at least n\n\
    +    PG   - print neighbors of good regions (needs 'QGn' or 'QVn')\n\
    +    PMn  - keep n Delaunay regions with most merges\n\
    +    Po   - force output.  If error, output neighborhood of facet\n\
    +    Pp   - do not report precision problems\n\
    +\n\
    +    .    - list of all options\n\
    +    -    - one line descriptions of all options\n\
    +    -V   - version\n\
    +";
    +/* for opts, don't assign 'e' or 'E' to a flag (already used for exponent) */
    +
    +/*---------------------------------
    +
    +  qh_prompt2
    +    synopsis for qhull
    +*/
    +char qh_prompt2[]= "\n\
    +qdelaunay- compute the Delaunay triangulation.  Qhull %s\n\
    +    input (stdin): dimension, number of points, point coordinates\n\
    +    comments start with a non-numeric character\n\
    +\n\
    +options (qdelaun.htm):\n\
    +    Qu   - furthest-site Delaunay triangulation\n\
    +    Qt   - triangulated output\n\
    +    QJ   - joggled input instead of merged facets\n\
    +    Tv   - verify result: structure, convexity, and in-circle test\n\
    +    .    - concise list of all options\n\
    +    -    - one-line description of all options\n\
    +    -V   - version\n\
    +\n\
    +output options (subset):\n\
    +    s    - summary of results (default)\n\
    +    i    - vertices incident to each Delaunay region\n\
    +    Fx   - extreme points (vertices of the convex hull)\n\
    +    o    - OFF format (shows the points lifted to a paraboloid)\n\
    +    G    - Geomview output (2-d and 3-d points lifted to a paraboloid)\n\
    +    m    - Mathematica output (2-d inputs lifted to a paraboloid)\n\
    +    QVn  - print Delaunay regions that include point n, -n if not\n\
    +    TO file- output results to file, may be enclosed in single quotes\n\
    +\n\
    +examples:\n\
    +    rbox c P0 D2 | qdelaunay s o          rbox c P0 D2 | qdelaunay i\n\
    +    rbox c P0 D2 | qdelaunay Fv           rbox c P0 D2 | qdelaunay s Qu Fv\n\
    +    rbox c G1 d D2 | qdelaunay s i        rbox c G1 d D2 | qdelaunay Qt\n\
    +    rbox M3,4 z 100 D2 | qdelaunay s      rbox M3,4 z 100 D2 | qdelaunay s Qt\n\
    +\n\
    +";
    +/* for opts, don't assign 'e' or 'E' to a flag (already used for exponent) */
    +
    +/*---------------------------------
    +
    +  qh_prompt3
    +    concise prompt for qhull
    +*/
    +char qh_prompt3[]= "\n\
    +Qhull %s.\n\
    +Except for 'F.' and 'PG', upper-case options take an argument.\n\
    +\n\
    + incidences     mathematica    OFF_format     points_lifted  summary\n\
    + facet_dump\n\
    +\n\
    + Farea          FArea_total    Fcoincident    Fd_cdd_in      FD_cdd_out\n\
    + FF_dump_xridge FIDs           Fmerges        Fneighbors     FNeigh_vertex\n\
    + FOptions       FPoint_near    FQdelaun       Fsummary       FSize\n\
    + Fvertices      Fxtremes       FMaple\n\
    +\n\
    + Gvertices      Gpoints        Gall_points    Gno_planes     Ginner\n\
    + Gcentrums      Ghyperplanes   Gridges        Gouter         GDrop_dim\n\
    + Gtransparent\n\
    +\n\
    + PArea_keep     Pdrop d0:0D0   Pgood          PFacet_area_keep\n\
    + PGood_neighbors PMerge_keep   Poutput_forced Pprecision_not\n\
    +\n\
    + QGood_point    QJoggle        Qsearch_1st    Qtriangulate   QupperDelaunay\n\
    + QVertex_good   Qzinfinite\n\
    +\n\
    + T4_trace       Tcheck_often   Tstatistics    Tverify        Tz_stdout\n\
    + TFacet_log     TInput_file    TPoint_trace   TMerge_trace   TOutput_file\n\
    + TWide_trace    TVertex_stop   TCone_stop\n\
    +\n\
    + Angle_max      Centrum_size   Random_dist    Wide_outside\n\
    +";
    +
    +/*---------------------------------
    +
    +  main( argc, argv )
    +    processes the command line, calls qhull() to do the work, and exits
    +
    +  design:
    +    initializes data structures
    +    reads points
    +    finishes initialization
    +    computes convex hull and other structures
    +    checks the result
    +    writes the output
    +    frees memory
    +*/
    +int main(int argc, char *argv[]) {
    +  int curlong, totlong; /* used !qh_NOmem */
    +  int exitcode, numpoints, dim;
    +  coordT *points;
    +  boolT ismalloc;
    +  qhT qh_qh;
    +  qhT *qh= &qh_qh;
    +
    +  QHULL_LIB_CHECK /* Check for compatible library */
    +
    +  if ((argc == 1) && isatty( 0 /*stdin*/)) {
    +    fprintf(stdout, qh_prompt2, qh_version);
    +    exit(qh_ERRnone);
    +  }
    +  if (argc > 1 && *argv[1] == '-' && !*(argv[1]+1)) {
    +    fprintf(stdout, qh_prompta, qh_version,
    +                qh_promptb, qh_promptc, qh_promptd, qh_prompte);
    +    exit(qh_ERRnone);
    +  }
    +  if (argc > 1 && *argv[1] == '.' && !*(argv[1]+1)) {
    +    fprintf(stdout, qh_prompt3, qh_version);
    +    exit(qh_ERRnone);
    +  }
    +  if (argc > 1 && *argv[1] == '-' && *(argv[1]+1)=='V') {
    +      fprintf(stdout, "%s\n", qh_version2);
    +      exit(qh_ERRnone);
    +  }
    +  qh_init_A(qh, stdin, stdout, stderr, argc, argv);  /* sets qh->qhull_command */
    +  exitcode= setjmp(qh->errexit); /* simple statement for CRAY J916 */
    +  if (!exitcode) {
    +    qh->NOerrexit = False;
    +    qh_option(qh, "delaunay  Qbbound-last", NULL, NULL);
    +    qh->DELAUNAY= True;     /* 'd'   */
    +    qh->SCALElast= True;    /* 'Qbb' */
    +    qh->KEEPcoplanar= True; /* 'Qc', to keep coplanars in 'p' */
    +    qh_checkflags(qh, qh->qhull_command, hidden_options);
    +    qh_initflags(qh, qh->qhull_command);
    +    points= qh_readpoints(qh, &numpoints, &dim, &ismalloc);
    +    if (dim >= 5) {
    +      qh_option(qh, "Qxact_merge", NULL, NULL);
    +      qh->MERGEexact= True; /* 'Qx' always */
    +    }
    +    qh_init_B(qh, points, numpoints, dim, ismalloc);
    +    qh_qhull(qh);
    +    qh_check_output(qh);
    +    qh_produce_output(qh);
    +    if (qh->VERIFYoutput && !qh->FORCEoutput && !qh->STOPpoint && !qh->STOPcone)
    +      qh_check_points(qh);
    +    exitcode= qh_ERRnone;
    +  }
    +  qh->NOerrexit= True;  /* no more setjmp */
    +#ifdef qh_NOmem
    +  qh_freeqhull(qh, qh_ALL);
    +#else
    +  qh_freeqhull(qh, !qh_ALL);
    +  qh_memfreeshort(qh, &curlong, &totlong);
    +  if (curlong || totlong)
    +    qh_fprintf_stderr(6263, "qhull internal warning (main): did not free %d bytes of long memory(%d pieces)\n",
    +       totlong, curlong);
    +#endif
    +  return exitcode;
    +} /* main */
    +
    diff --git a/xs/src/qhull/src/qdelaunay/qdelaunay.pro b/xs/src/qhull/src/qdelaunay/qdelaunay.pro
    new file mode 100644
    index 000000000..138ac0589
    --- /dev/null
    +++ b/xs/src/qhull/src/qdelaunay/qdelaunay.pro
    @@ -0,0 +1,9 @@
    +# -------------------------------------------------
    +# qdelaunay.pro -- Qt project file for qvoronoi.exe
    +# -------------------------------------------------
    +
    +include(../qhull-app-c.pri)
    +
    +TARGET = qdelaunay
    +
    +SOURCES += qdelaun.c
    diff --git a/xs/src/qhull/src/qhalf/qhalf.c b/xs/src/qhull/src/qhalf/qhalf.c
    new file mode 100644
    index 000000000..4a5889ed7
    --- /dev/null
    +++ b/xs/src/qhull/src/qhalf/qhalf.c
    @@ -0,0 +1,316 @@
    +/*
      ---------------------------------
    +
    +   qhalf.c
    +     compute the intersection of halfspaces about a point
    +
    +   see unix.c for full interface
    +
    +   Copyright (c) 1993-2015, The Geometry Center
    +*/
    +
    +#include "libqhull/libqhull.h"
    +
    +#include 
    +#include 
    +#include 
    +#include 
    +#include 
    +
    +#if __cplusplus
    +extern "C" {
    +  int isatty(int);
    +}
    +
    +#elif _MSC_VER
    +#include 
    +#define isatty _isatty
    +/* int _isatty(int); */
    +
    +#else
    +int isatty(int);  /* returns 1 if stdin is a tty
    +                   if "Undefined symbol" this can be deleted along with call in main() */
    +#endif
    +
    +/*---------------------------------
    +
    +  qh_prompt
    +    long prompt for qhull
    +
    +  notes:
    +    restricted version of libqhull.c
    +
    +  see:
    +    concise prompt below
    +*/
    +
    +/* duplicated in qhalf.htm */
    +char hidden_options[]=" d n v Qbb QbB Qf Qg Qm Qr QR Qv Qx Qz TR E V Fa FA FC FD FS Ft FV Gt Q0 Q1 Q2 Q3 Q4 Q5 Q6 Q7 Q8 Q9 ";
    +
    +char qh_prompta[]= "\n\
    +qhalf- compute the intersection of halfspaces about a point\n\
    +    http://www.qhull.org  %s\n\
    +\n\
    +input (stdin):\n\
    +    optional interior point: dimension, 1, coordinates\n\
    +    first lines: dimension+1 and number of halfspaces\n\
    +    other lines: halfspace coefficients followed by offset\n\
    +    comments:    start with a non-numeric character\n\
    +\n\
    +options:\n\
    +    Hn,n - specify coordinates of interior point\n\
    +    Qt   - triangulated output\n\
    +    QJ   - joggled input instead of merged facets\n\
    +    Qc   - keep coplanar halfspaces\n\
    +    Qi   - keep other redundant halfspaces\n\
    +\n\
    +Qhull control options:\n\
    +    QJn  - randomly joggle input in range [-n,n]\n\
    +%s%s%s%s";  /* split up qh_prompt for Visual C++ */
    +char qh_promptb[]= "\
    +    Qbk:0Bk:0 - remove k-th coordinate from input\n\
    +    Qs   - search all halfspaces for the initial simplex\n\
    +    QGn  - print intersection if visible to halfspace n, -n for not\n\
    +    QVn  - print intersections for halfspace n, -n if not\n\
    +\n\
    +";
    +char qh_promptc[]= "\
    +Trace options:\n\
    +    T4   - trace at level n, 4=all, 5=mem/gauss, -1= events\n\
    +    Tc   - check frequently during execution\n\
    +    Ts   - print statistics\n\
    +    Tv   - verify result: structure, convexity, and redundancy\n\
    +    Tz   - send all output to stdout\n\
    +    TFn  - report summary when n or more facets created\n\
    +    TI file - input data from file, no spaces or single quotes\n\
    +    TO file - output results to file, may be enclosed in single quotes\n\
    +    TPn  - turn on tracing when halfspace n added to intersection\n\
    +    TMn  - turn on tracing at merge n\n\
    +    TWn  - trace merge facets when width > n\n\
    +    TVn  - stop qhull after adding halfspace n, -n for before (see TCn)\n\
    +    TCn  - stop qhull after building cone for halfspace n (see TVn)\n\
    +\n\
    +Precision options:\n\
    +    Cn   - radius of centrum (roundoff added).  Merge facets if non-convex\n\
    +     An  - cosine of maximum angle.  Merge facets if cosine > n or non-convex\n\
    +           C-0 roundoff, A-0.99/C-0.01 pre-merge, A0.99/C0.01 post-merge\n\
    +    Rn   - randomly perturb computations by a factor of [1-n,1+n]\n\
    +    Un   - max distance below plane for a new, coplanar halfspace\n\
    +    Wn   - min facet width for outside halfspace (before roundoff)\n\
    +\n\
    +Output formats (may be combined; if none, produces a summary to stdout):\n\
    +    f    - facet dump\n\
    +    G    - Geomview output (dual convex hull)\n\
    +    i    - non-redundant halfspaces incident to each intersection\n\
    +    m    - Mathematica output (dual convex hull)\n\
    +    o    - OFF format (dual convex hull: dimension, points, and facets)\n\
    +    p    - vertex coordinates of dual convex hull (coplanars if 'Qc' or 'Qi')\n\
    +    s    - summary (stderr)\n\
    +\n\
    +";
    +char qh_promptd[]= "\
    +More formats:\n\
    +    Fc   - count plus redundant halfspaces for each intersection\n\
    +         -   Qc (default) for coplanar and Qi for other redundant\n\
    +    Fd   - use cdd format for input (homogeneous with offset first)\n\
    +    FF   - facet dump without ridges\n\
    +    FI   - ID of each intersection\n\
    +    Fm   - merge count for each intersection (511 max)\n\
    +    FM   - Maple output (dual convex hull)\n\
    +    Fn   - count plus neighboring intersections for each intersection\n\
    +    FN   - count plus intersections for each non-redundant halfspace\n\
    +    FO   - options and precision constants\n\
    +    Fp   - dim, count, and intersection coordinates\n\
    +    FP   - nearest halfspace and distance for each redundant halfspace\n\
    +    FQ   - command used for qhalf\n\
    +    Fs   - summary: #int (8), dim, #halfspaces, #non-redundant, #intersections\n\
    +                      for output: #non-redundant, #intersections, #coplanar\n\
    +                                  halfspaces, #non-simplicial intersections\n\
    +                    #real (2), max outer plane, min vertex\n\
    +    Fv   - count plus non-redundant halfspaces for each intersection\n\
    +    Fx   - non-redundant halfspaces\n\
    +\n\
    +";
    +char qh_prompte[]= "\
    +Geomview output (2-d, 3-d and 4-d; dual convex hull)\n\
    +    Ga   - all points (i.e., transformed halfspaces) as dots\n\
    +     Gp  -  coplanar points and vertices as radii\n\
    +     Gv  -  vertices (i.e., non-redundant halfspaces) as spheres\n\
    +    Gi   - inner planes (i.e., halfspace intersections) only\n\
    +     Gn  -  no planes\n\
    +     Go  -  outer planes only\n\
    +    Gc   - centrums\n\
    +    Gh   - hyperplane intersections\n\
    +    Gr   - ridges\n\
    +    GDn  - drop dimension n in 3-d and 4-d output\n\
    +\n\
    +Print options:\n\
    +    PAn  - keep n largest facets (i.e., intersections) by area\n\
    +    Pdk:n- drop facet if normal[k] <= n (default 0.0)\n\
    +    PDk:n- drop facet if normal[k] >= n\n\
    +    Pg   - print good facets (needs 'QGn' or 'QVn')\n\
    +    PFn  - keep facets whose area is at least n\n\
    +    PG   - print neighbors of good facets\n\
    +    PMn  - keep n facets with most merges\n\
    +    Po   - force output.  If error, output neighborhood of facet\n\
    +    Pp   - do not report precision problems\n\
    +\n\
    +    .    - list of all options\n\
    +    -    - one line descriptions of all options\n\
    +    -V   - version\n\
    +";
    +/* for opts, don't assign 'e' or 'E' to a flag (already used for exponent) */
    +
    +/*---------------------------------
    +
    +  qh_prompt2
    +    synopsis for qhull
    +*/
    +char qh_prompt2[]= "\n\
    +qhalf- halfspace intersection about a point.  Qhull %s\n\
    +    input (stdin): [dim, 1, interior point], dim+1, n, coefficients+offset\n\
    +    comments start with a non-numeric character\n\
    +\n\
    +options (qhalf.htm):\n\
    +    Hn,n - specify coordinates of interior point\n\
    +    Qt   - triangulated output\n\
    +    QJ   - joggled input instead of merged facets\n\
    +    Tv   - verify result: structure, convexity, and redundancy\n\
    +    .    - concise list of all options\n\
    +    -    - one-line description of all options\n\
    +    -V   - version\n\
    +\n\
    +output options (subset):\n\
    +    s    - summary of results (default)\n\
    +    Fp   - intersection coordinates\n\
    +    Fv   - non-redundant halfspaces incident to each intersection\n\
    +    Fx   - non-redundant halfspaces\n\
    +    o    - OFF file format (dual convex hull)\n\
    +    G    - Geomview output (dual convex hull)\n\
    +    m    - Mathematica output (dual convex hull)\n\
    +    QVn  - print intersections for halfspace n, -n if not\n\
    +    TO file - output results to file, may be enclosed in single quotes\n\
    +\n\
    +examples:\n\
    +    rbox d | qconvex FQ n | qhalf s H0,0,0 Fp\n\
    +    rbox c | qconvex FQ FV n | qhalf s i\n\
    +    rbox c | qconvex FQ FV n | qhalf s o\n\
    +\n\
    +";
    +/* for opts, don't assign 'e' or 'E' to a flag (already used for exponent) */
    +
    +/*---------------------------------
    +
    +  qh_prompt3
    +    concise prompt for qhull
    +*/
    +char qh_prompt3[]= "\n\
    +Qhull %s.\n\
    +Except for 'F.' and 'PG', upper_case options take an argument.\n\
    +\n\
    + incidences     Geomview       mathematica    OFF_format     point_dual\n\
    + summary        facet_dump\n\
    +\n\
    + Fc_redundant   Fd_cdd_in      FF_dump_xridge FIDs           Fmerges\n\
    + Fneighbors     FN_intersect   FOptions       Fp_coordinates FP_nearest\n\
    + FQhalf         Fsummary       Fv_halfspace   FMaple         Fx_non_redundant\n\
    +\n\
    + Gvertices      Gpoints        Gall_points    Gno_planes     Ginner\n\
    + Gcentrums      Ghyperplanes   Gridges        Gouter         GDrop_dim\n\
    +\n\
    + PArea_keep     Pdrop d0:0D0   Pgood          PFacet_area_keep\n\
    + PGood_neighbors PMerge_keep   Poutput_forced Pprecision_not\n\
    +\n\
    + Qbk:0Bk:0_drop Qcoplanar      QG_half_good   Qi_redundant   QJoggle\n\
    + Qsearch_1st    Qtriangulate   QVertex_good\n\
    +\n\
    + T4_trace       Tcheck_often   Tstatistics    Tverify        Tz_stdout\n\
    + TFacet_log     TInput_file    TPoint_trace   TMerge_trace   TOutput_file\n\
    + TWide_trace    TVertex_stop   TCone_stop\n\
    +\n\
    + Angle_max      Centrum_size   Random_dist    Ucoplanar_max  Wide_outside\n\
    +";
    +
    +/*---------------------------------
    +
    +  main( argc, argv )
    +    processes the command line, calls qhull() to do the work, and exits
    +
    +  design:
    +    initializes data structures
    +    reads points
    +    finishes initialization
    +    computes convex hull and other structures
    +    checks the result
    +    writes the output
    +    frees memory
    +*/
    +int main(int argc, char *argv[]) {
    +  int curlong, totlong; /* used !qh_NOmem */
    +  int exitcode, numpoints, dim;
    +  coordT *points;
    +  boolT ismalloc;
    +
    +  QHULL_LIB_CHECK /* Check for compatible library */
    +
    +  if ((argc == 1) && isatty( 0 /*stdin*/)) {
    +    fprintf(stdout, qh_prompt2, qh_version);
    +    exit(qh_ERRnone);
    +  }
    +  if (argc > 1 && *argv[1] == '-' && !*(argv[1]+1)) {
    +    fprintf(stdout, qh_prompta, qh_version,
    +        qh_promptb, qh_promptc, qh_promptd, qh_prompte);
    +    exit(qh_ERRnone);
    +  }
    +  if (argc > 1 && *argv[1] == '.' && !*(argv[1]+1)) {
    +    fprintf(stdout, qh_prompt3, qh_version);
    +    exit(qh_ERRnone);
    +  }
    +  if (argc > 1 && *argv[1] == '-' && *(argv[1]+1)=='V') {
    +      fprintf(stdout, "%s\n", qh_version2);
    +      exit(qh_ERRnone);
    +  }
    +  qh_init_A(stdin, stdout, stderr, argc, argv);  /* sets qh qhull_command */
    +  exitcode= setjmp(qh errexit); /* simple statement for CRAY J916 */
    +  if (!exitcode) {
    +    qh NOerrexit = False;
    +    qh_option("Halfspace", NULL, NULL);
    +    qh HALFspace= True;    /* 'H'   */
    +    qh_checkflags(qh qhull_command, hidden_options);
    +    qh_initflags(qh qhull_command);
    +    if (qh SCALEinput) {
    +      fprintf(qh ferr, "\
    +qhull error: options 'Qbk:n' and 'QBk:n' are not used with qhalf.\n\
    +             Use 'Qbk:0Bk:0 to drop dimension k.\n");
    +      qh_errexit(qh_ERRinput, NULL, NULL);
    +    }
    +    points= qh_readpoints(&numpoints, &dim, &ismalloc);
    +    if (dim >= 5) {
    +      qh_option("Qxact_merge", NULL, NULL);
    +      qh MERGEexact= True; /* 'Qx' always */
    +    }
    +    qh_init_B(points, numpoints, dim, ismalloc);
    +    qh_qhull();
    +    qh_check_output();
    +    qh_produce_output();
    +    if (qh VERIFYoutput && !qh FORCEoutput && !qh STOPpoint && !qh STOPcone)
    +      qh_check_points();
    +    exitcode= qh_ERRnone;
    +  }
    +  qh NOerrexit= True;  /* no more setjmp */
    +#ifdef qh_NOmem
    +  qh_freeqhull(qh_ALL);
    +#else
    +  qh_freeqhull(!qh_ALL);
    +  qh_memfreeshort(&curlong, &totlong);
    +  if (curlong || totlong)
    +    qh_fprintf_stderr(6263, "qhull internal warning (main): did not free %d bytes of long memory(%d pieces)\n",
    +       totlong, curlong);
    +#endif
    +  return exitcode;
    +} /* main */
    +
    diff --git a/xs/src/qhull/src/qhalf/qhalf.pro b/xs/src/qhull/src/qhalf/qhalf.pro
    new file mode 100644
    index 000000000..ebad38789
    --- /dev/null
    +++ b/xs/src/qhull/src/qhalf/qhalf.pro
    @@ -0,0 +1,9 @@
    +# -------------------------------------------------
    +# qhalf.pro -- Qt project file for qconvex.exe
    +# -------------------------------------------------
    +
    +include(../qhull-app-c.pri)
    +
    +TARGET = qhalf
    +
    +SOURCES += qhalf.c
    diff --git a/xs/src/qhull/src/qhalf/qhalf_r.c b/xs/src/qhull/src/qhalf/qhalf_r.c
    new file mode 100644
    index 000000000..c49d777f9
    --- /dev/null
    +++ b/xs/src/qhull/src/qhalf/qhalf_r.c
    @@ -0,0 +1,318 @@
    +/*
      ---------------------------------
    +
    +   qhalf.c
    +     compute the intersection of halfspaces about a point
    +
    +   see unix.c for full interface
    +
    +   Copyright (c) 1993-2015, The Geometry Center
    +*/
    +
    +#include "libqhull_r/libqhull_r.h"
    +
    +#include 
    +#include 
    +#include 
    +#include 
    +#include 
    +
    +#if __cplusplus
    +extern "C" {
    +  int isatty(int);
    +}
    +
    +#elif _MSC_VER
    +#include 
    +#define isatty _isatty
    +/* int _isatty(int); */
    +
    +#else
    +int isatty(int);  /* returns 1 if stdin is a tty
    +                   if "Undefined symbol" this can be deleted along with call in main() */
    +#endif
    +
    +/*---------------------------------
    +
    +  qh_prompt
    +    long prompt for qhull
    +
    +  notes:
    +    restricted version of libqhull.c
    +
    +  see:
    +    concise prompt below
    +*/
    +
    +/* duplicated in qhalf.htm */
    +char hidden_options[]=" d n v Qbb QbB Qf Qg Qm Qr QR Qv Qx Qz TR E V Fa FA FC FD FS Ft FV Gt Q0 Q1 Q2 Q3 Q4 Q5 Q6 Q7 Q8 Q9 ";
    +
    +char qh_prompta[]= "\n\
    +qhalf- compute the intersection of halfspaces about a point\n\
    +    http://www.qhull.org  %s\n\
    +\n\
    +input (stdin):\n\
    +    optional interior point: dimension, 1, coordinates\n\
    +    first lines: dimension+1 and number of halfspaces\n\
    +    other lines: halfspace coefficients followed by offset\n\
    +    comments:    start with a non-numeric character\n\
    +\n\
    +options:\n\
    +    Hn,n - specify coordinates of interior point\n\
    +    Qt   - triangulated output\n\
    +    QJ   - joggled input instead of merged facets\n\
    +    Qc   - keep coplanar halfspaces\n\
    +    Qi   - keep other redundant halfspaces\n\
    +\n\
    +Qhull control options:\n\
    +    QJn  - randomly joggle input in range [-n,n]\n\
    +%s%s%s%s";  /* split up qh_prompt for Visual C++ */
    +char qh_promptb[]= "\
    +    Qbk:0Bk:0 - remove k-th coordinate from input\n\
    +    Qs   - search all halfspaces for the initial simplex\n\
    +    QGn  - print intersection if visible to halfspace n, -n for not\n\
    +    QVn  - print intersections for halfspace n, -n if not\n\
    +\n\
    +";
    +char qh_promptc[]= "\
    +Trace options:\n\
    +    T4   - trace at level n, 4=all, 5=mem/gauss, -1= events\n\
    +    Tc   - check frequently during execution\n\
    +    Ts   - print statistics\n\
    +    Tv   - verify result: structure, convexity, and redundancy\n\
    +    Tz   - send all output to stdout\n\
    +    TFn  - report summary when n or more facets created\n\
    +    TI file - input data from file, no spaces or single quotes\n\
    +    TO file - output results to file, may be enclosed in single quotes\n\
    +    TPn  - turn on tracing when halfspace n added to intersection\n\
    +    TMn  - turn on tracing at merge n\n\
    +    TWn  - trace merge facets when width > n\n\
    +    TVn  - stop qhull after adding halfspace n, -n for before (see TCn)\n\
    +    TCn  - stop qhull after building cone for halfspace n (see TVn)\n\
    +\n\
    +Precision options:\n\
    +    Cn   - radius of centrum (roundoff added).  Merge facets if non-convex\n\
    +     An  - cosine of maximum angle.  Merge facets if cosine > n or non-convex\n\
    +           C-0 roundoff, A-0.99/C-0.01 pre-merge, A0.99/C0.01 post-merge\n\
    +    Rn   - randomly perturb computations by a factor of [1-n,1+n]\n\
    +    Un   - max distance below plane for a new, coplanar halfspace\n\
    +    Wn   - min facet width for outside halfspace (before roundoff)\n\
    +\n\
    +Output formats (may be combined; if none, produces a summary to stdout):\n\
    +    f    - facet dump\n\
    +    G    - Geomview output (dual convex hull)\n\
    +    i    - non-redundant halfspaces incident to each intersection\n\
    +    m    - Mathematica output (dual convex hull)\n\
    +    o    - OFF format (dual convex hull: dimension, points, and facets)\n\
    +    p    - vertex coordinates of dual convex hull (coplanars if 'Qc' or 'Qi')\n\
    +    s    - summary (stderr)\n\
    +\n\
    +";
    +char qh_promptd[]= "\
    +More formats:\n\
    +    Fc   - count plus redundant halfspaces for each intersection\n\
    +         -   Qc (default) for coplanar and Qi for other redundant\n\
    +    Fd   - use cdd format for input (homogeneous with offset first)\n\
    +    FF   - facet dump without ridges\n\
    +    FI   - ID of each intersection\n\
    +    Fm   - merge count for each intersection (511 max)\n\
    +    FM   - Maple output (dual convex hull)\n\
    +    Fn   - count plus neighboring intersections for each intersection\n\
    +    FN   - count plus intersections for each non-redundant halfspace\n\
    +    FO   - options and precision constants\n\
    +    Fp   - dim, count, and intersection coordinates\n\
    +    FP   - nearest halfspace and distance for each redundant halfspace\n\
    +    FQ   - command used for qhalf\n\
    +    Fs   - summary: #int (8), dim, #halfspaces, #non-redundant, #intersections\n\
    +                      for output: #non-redundant, #intersections, #coplanar\n\
    +                                  halfspaces, #non-simplicial intersections\n\
    +                    #real (2), max outer plane, min vertex\n\
    +    Fv   - count plus non-redundant halfspaces for each intersection\n\
    +    Fx   - non-redundant halfspaces\n\
    +\n\
    +";
    +char qh_prompte[]= "\
    +Geomview output (2-d, 3-d and 4-d; dual convex hull)\n\
    +    Ga   - all points (i.e., transformed halfspaces) as dots\n\
    +     Gp  -  coplanar points and vertices as radii\n\
    +     Gv  -  vertices (i.e., non-redundant halfspaces) as spheres\n\
    +    Gi   - inner planes (i.e., halfspace intersections) only\n\
    +     Gn  -  no planes\n\
    +     Go  -  outer planes only\n\
    +    Gc   - centrums\n\
    +    Gh   - hyperplane intersections\n\
    +    Gr   - ridges\n\
    +    GDn  - drop dimension n in 3-d and 4-d output\n\
    +\n\
    +Print options:\n\
    +    PAn  - keep n largest facets (i.e., intersections) by area\n\
    +    Pdk:n- drop facet if normal[k] <= n (default 0.0)\n\
    +    PDk:n- drop facet if normal[k] >= n\n\
    +    Pg   - print good facets (needs 'QGn' or 'QVn')\n\
    +    PFn  - keep facets whose area is at least n\n\
    +    PG   - print neighbors of good facets\n\
    +    PMn  - keep n facets with most merges\n\
    +    Po   - force output.  If error, output neighborhood of facet\n\
    +    Pp   - do not report precision problems\n\
    +\n\
    +    .    - list of all options\n\
    +    -    - one line descriptions of all options\n\
    +    -V   - version\n\
    +";
    +/* for opts, don't assign 'e' or 'E' to a flag (already used for exponent) */
    +
    +/*---------------------------------
    +
    +  qh_prompt2
    +    synopsis for qhull
    +*/
    +char qh_prompt2[]= "\n\
    +qhalf- halfspace intersection about a point.  Qhull %s\n\
    +    input (stdin): [dim, 1, interior point], dim+1, n, coefficients+offset\n\
    +    comments start with a non-numeric character\n\
    +\n\
    +options (qhalf.htm):\n\
    +    Hn,n - specify coordinates of interior point\n\
    +    Qt   - triangulated output\n\
    +    QJ   - joggled input instead of merged facets\n\
    +    Tv   - verify result: structure, convexity, and redundancy\n\
    +    .    - concise list of all options\n\
    +    -    - one-line description of all options\n\
    +    -V   - version\n\
    +\n\
    +output options (subset):\n\
    +    s    - summary of results (default)\n\
    +    Fp   - intersection coordinates\n\
    +    Fv   - non-redundant halfspaces incident to each intersection\n\
    +    Fx   - non-redundant halfspaces\n\
    +    o    - OFF file format (dual convex hull)\n\
    +    G    - Geomview output (dual convex hull)\n\
    +    m    - Mathematica output (dual convex hull)\n\
    +    QVn  - print intersections for halfspace n, -n if not\n\
    +    TO file - output results to file, may be enclosed in single quotes\n\
    +\n\
    +examples:\n\
    +    rbox d | qconvex FQ n | qhalf s H0,0,0 Fp\n\
    +    rbox c | qconvex FQ FV n | qhalf s i\n\
    +    rbox c | qconvex FQ FV n | qhalf s o\n\
    +\n\
    +";
    +/* for opts, don't assign 'e' or 'E' to a flag (already used for exponent) */
    +
    +/*---------------------------------
    +
    +  qh_prompt3
    +    concise prompt for qhull
    +*/
    +char qh_prompt3[]= "\n\
    +Qhull %s.\n\
    +Except for 'F.' and 'PG', upper_case options take an argument.\n\
    +\n\
    + incidences     Geomview       mathematica    OFF_format     point_dual\n\
    + summary        facet_dump\n\
    +\n\
    + Fc_redundant   Fd_cdd_in      FF_dump_xridge FIDs           Fmerges\n\
    + Fneighbors     FN_intersect   FOptions       Fp_coordinates FP_nearest\n\
    + FQhalf         Fsummary       Fv_halfspace   FMaple         Fx_non_redundant\n\
    +\n\
    + Gvertices      Gpoints        Gall_points    Gno_planes     Ginner\n\
    + Gcentrums      Ghyperplanes   Gridges        Gouter         GDrop_dim\n\
    +\n\
    + PArea_keep     Pdrop d0:0D0   Pgood          PFacet_area_keep\n\
    + PGood_neighbors PMerge_keep   Poutput_forced Pprecision_not\n\
    +\n\
    + Qbk:0Bk:0_drop Qcoplanar      QG_half_good   Qi_redundant   QJoggle\n\
    + Qsearch_1st    Qtriangulate   QVertex_good\n\
    +\n\
    + T4_trace       Tcheck_often   Tstatistics    Tverify        Tz_stdout\n\
    + TFacet_log     TInput_file    TPoint_trace   TMerge_trace   TOutput_file\n\
    + TWide_trace    TVertex_stop   TCone_stop\n\
    +\n\
    + Angle_max      Centrum_size   Random_dist    Ucoplanar_max  Wide_outside\n\
    +";
    +
    +/*---------------------------------
    +
    +  main( argc, argv )
    +    processes the command line, calls qhull() to do the work, and exits
    +
    +  design:
    +    initializes data structures
    +    reads points
    +    finishes initialization
    +    computes convex hull and other structures
    +    checks the result
    +    writes the output
    +    frees memory
    +*/
    +int main(int argc, char *argv[]) {
    +  int curlong, totlong; /* used !qh_NOmem */
    +  int exitcode, numpoints, dim;
    +  coordT *points;
    +  boolT ismalloc;
    +  qhT qh_qh;
    +  qhT *qh= &qh_qh;
    +
    +  QHULL_LIB_CHECK /* Check for compatible library */
    +
    +  if ((argc == 1) && isatty( 0 /*stdin*/)) {
    +    fprintf(stdout, qh_prompt2, qh_version);
    +    exit(qh_ERRnone);
    +  }
    +  if (argc > 1 && *argv[1] == '-' && !*(argv[1]+1)) {
    +    fprintf(stdout, qh_prompta, qh_version,
    +        qh_promptb, qh_promptc, qh_promptd, qh_prompte);
    +    exit(qh_ERRnone);
    +  }
    +  if (argc > 1 && *argv[1] == '.' && !*(argv[1]+1)) {
    +    fprintf(stdout, qh_prompt3, qh_version);
    +    exit(qh_ERRnone);
    +  }
    +  if (argc > 1 && *argv[1] == '-' && *(argv[1]+1)=='V') {
    +      fprintf(stdout, "%s\n", qh_version2);
    +      exit(qh_ERRnone);
    +  }
    +  qh_init_A(qh, stdin, stdout, stderr, argc, argv);  /* sets qh->qhull_command */
    +  exitcode= setjmp(qh->errexit); /* simple statement for CRAY J916 */
    +  if (!exitcode) {
    +    qh->NOerrexit = False;
    +    qh_option(qh, "Halfspace", NULL, NULL);
    +    qh->HALFspace= True;    /* 'H'   */
    +    qh_checkflags(qh, qh->qhull_command, hidden_options);
    +    qh_initflags(qh, qh->qhull_command);
    +    if (qh->SCALEinput) {
    +      fprintf(qh->ferr, "\
    +qhull error: options 'Qbk:n' and 'QBk:n' are not used with qhalf.\n\
    +             Use 'Qbk:0Bk:0 to drop dimension k.\n");
    +      qh_errexit(qh, qh_ERRinput, NULL, NULL);
    +    }
    +    points= qh_readpoints(qh, &numpoints, &dim, &ismalloc);
    +    if (dim >= 5) {
    +      qh_option(qh, "Qxact_merge", NULL, NULL);
    +      qh->MERGEexact= True; /* 'Qx' always */
    +    }
    +    qh_init_B(qh, points, numpoints, dim, ismalloc);
    +    qh_qhull(qh);
    +    qh_check_output(qh);
    +    qh_produce_output(qh);
    +    if (qh->VERIFYoutput && !qh->FORCEoutput && !qh->STOPpoint && !qh->STOPcone)
    +      qh_check_points(qh);
    +    exitcode= qh_ERRnone;
    +  }
    +  qh->NOerrexit= True;  /* no more setjmp */
    +#ifdef qh_NOmem
    +  qh_freeqhull(qh, qh_ALL);
    +#else
    +  qh_freeqhull(qh, !qh_ALL);
    +  qh_memfreeshort(qh, &curlong, &totlong);
    +  if (curlong || totlong)
    +    qh_fprintf_stderr(6263, "qhull internal warning (main): did not free %d bytes of long memory(%d pieces)\n",
    +       totlong, curlong);
    +#endif
    +  return exitcode;
    +} /* main */
    +
    diff --git a/xs/src/qhull/src/qhull-all.pro b/xs/src/qhull/src/qhull-all.pro
    new file mode 100644
    index 000000000..1d3a0ac6f
    --- /dev/null
    +++ b/xs/src/qhull/src/qhull-all.pro
    @@ -0,0 +1,94 @@
    +# -------------------------------------------------
    +# qhull-all.pro -- Qt project to build executables and static libraries
    +#
    +# To build with Qt on mingw
    +#   Download Qt SDK, install Perl
    +#   /c/qt/2010.05/qt> ./configure -static -platform win32-g++ -fast -no-qt3support
    +#
    +# To build DevStudio sln and proj files (Qhull ships with cmake derived files)
    +# qmake is in Qt's bin directory
    +# mkdir -p build && cd build && qmake -tp vc -r ../src/qhull-all.pro
    +# Additional Library Directories -- C:\qt\Qt5.2.0\5.2.0\msvc2012_64\lib
    +# libqhullcpp and libqhullstatic refered to $(QTDIR) but apparently didn't retrieve (should be %QTDIR%?)
    +# libqhull_r also needs ..\..\lib
    +# Need to change build/x64/Debug/*.lib to lib/ (or copy libs by hand, each time)
    +# Additional Build Dependencies
    +# See README.txt -- Need to add Build Dependencies, disable rtti, rename targets to qhull.dll, qhull6_p.dll and qhull6_pd.dll
    +# -------------------------------------------------
    +
    +TEMPLATE = subdirs
    +CONFIG += ordered
    +
    +SUBDIRS += libqhull_r      #shared library with reentrant code
    +SUBDIRS += libqhullstatic  #static library
    +SUBDIRS += libqhullstatic_r #static library with reentrant code
    +SUBDIRS += libqhullcpp     #static library for C++ interface with libqhullstatic_r
    +
    +SUBDIRS += qhull           #qhull program linked to libqhullstatic_r
    +SUBDIRS += rbox         
    +SUBDIRS += qconvex         #qhull programs linked to libqhullstatic
    +SUBDIRS += qdelaunay
    +SUBDIRS += qhalf
    +SUBDIRS += qvoronoi
    +
    +SUBDIRS += user_eg         #user programs linked to libqhull_r
    +SUBDIRS += user_eg2  
    +SUBDIRS += user_eg3        #user program with libqhullcpp and libqhullstatic_r
    +
    +SUBDIRS += qhulltest       #C++ test program with Qt, libqhullcpp, and libqhullstatic_r
    +SUBDIRS += testqset        #test program for qset.c with mem.c
    +SUBDIRS += testqset_r      #test program for qset_r.c with mem_r.c
    +                           #See eg/q_test for qhull tests
    +
    +OTHER_FILES += Changes.txt
    +OTHER_FILES += CMakeLists.txt
    +OTHER_FILES += Make-config.sh
    +OTHER_FILES += ../Announce.txt
    +OTHER_FILES += ../CMakeLists.txt
    +OTHER_FILES += ../COPYING.txt
    +OTHER_FILES += ../File_id.diz
    +OTHER_FILES += ../index.htm
    +OTHER_FILES += ../Makefile
    +OTHER_FILES += ../README.txt
    +OTHER_FILES += ../REGISTER.txt
    +OTHER_FILES += ../eg/q_eg
    +OTHER_FILES += ../eg/q_egtest
    +OTHER_FILES += ../eg/q_test
    +OTHER_FILES += ../html/index.htm
    +OTHER_FILES += ../html/qconvex.htm
    +OTHER_FILES += ../html/qdelau_f.htm
    +OTHER_FILES += ../html/qdelaun.htm
    +OTHER_FILES += ../html/qhalf.htm
    +OTHER_FILES += ../html/qh-code.htm
    +OTHER_FILES += ../html/qh-eg.htm
    +OTHER_FILES += ../html/qh-faq.htm
    +OTHER_FILES += ../html/qh-get.htm
    +OTHER_FILES += ../html/qh-impre.htm
    +OTHER_FILES += ../html/qh-optc.htm
    +OTHER_FILES += ../html/qh-optf.htm
    +OTHER_FILES += ../html/qh-optg.htm
    +OTHER_FILES += ../html/qh-opto.htm
    +OTHER_FILES += ../html/qh-optp.htm
    +OTHER_FILES += ../html/qh-optq.htm
    +OTHER_FILES += ../html/qh-optt.htm
    +OTHER_FILES += ../html/qh-quick.htm
    +OTHER_FILES += ../html/qhull.htm
    +OTHER_FILES += ../html/qhull.man
    +OTHER_FILES += ../html/qhull.txt
    +OTHER_FILES += ../html/qhull-cpp.xml
    +OTHER_FILES += ../html/qvoron_f.htm
    +OTHER_FILES += ../html/qvoronoi.htm
    +OTHER_FILES += ../html/rbox.htm
    +OTHER_FILES += ../html/rbox.man
    +OTHER_FILES += ../html/rbox.txt
    +OTHER_FILES += ../src/libqhull/Makefile
    +OTHER_FILES += ../src/libqhull_r/Makefile
    +OTHER_FILES += ../src/libqhull_r/qhull_r-exports.def
    +OTHER_FILES += ../src/qconvex/qconvex_r.c
    +OTHER_FILES += ../src/qdelaunay/qdelaun_r.c
    +OTHER_FILES += ../src/qhalf/qhalf_r.c
    +OTHER_FILES += ../src/qhull/rbox_r.c
    +OTHER_FILES += ../src/qvoronoi/qvoronoi_r.c
    +OTHER_FILES += ../src/qhull/unix.c
    +OTHER_FILES += ../src/user_eg/user_eg.c
    +OTHER_FILES += ../src/user_eg2/user_eg2.c
    diff --git a/xs/src/qhull/src/qhull-app-c.pri b/xs/src/qhull/src/qhull-app-c.pri
    new file mode 100644
    index 000000000..05e5a00f2
    --- /dev/null
    +++ b/xs/src/qhull/src/qhull-app-c.pri
    @@ -0,0 +1,24 @@
    +# -------------------------------------------------
    +# qhull-app-c.pri -- Qt include project for C qhull applications linked to libqhull
    +# -------------------------------------------------
    +
    +include(qhull-warn.pri)
    +
    +DESTDIR = ../../bin
    +TEMPLATE = app
    +CONFIG += console warn_on
    +CONFIG -= qt
    +
    +LIBS += -L../../lib
    +build_pass:CONFIG(debug, debug|release){
    +   LIBS += -lqhullstatic_d
    +   OBJECTS_DIR = Debug
    +}else:build_pass:CONFIG(release, debug|release){
    +   LIBS += -lqhullstatic
    +   OBJECTS_DIR = Release
    +}
    +win32-msvc* : QMAKE_LFLAGS += /INCREMENTAL:NO
    +
    +INCLUDEPATH += ..
    +CONFIG += qhull_warn_conversion
    +
    diff --git a/xs/src/qhull/src/qhull-app-c_r.pri b/xs/src/qhull/src/qhull-app-c_r.pri
    new file mode 100644
    index 000000000..9c2ef5600
    --- /dev/null
    +++ b/xs/src/qhull/src/qhull-app-c_r.pri
    @@ -0,0 +1,26 @@
    +# -------------------------------------------------
    +# qhull-app-c_r.pri -- Qt include project for C qhull applications linked to qhullstatic_r
    +#
    +# It uses reentrant Qhull
    +# -------------------------------------------------
    +
    +include(qhull-warn.pri)
    +
    +DESTDIR = ../../bin
    +TEMPLATE = app
    +CONFIG += console warn_on
    +CONFIG -= qt
    +
    +LIBS += -L../../lib
    +build_pass:CONFIG(debug, debug|release){
    +   LIBS += -lqhullstatic_rd
    +   OBJECTS_DIR = Debug
    +}else:build_pass:CONFIG(release, debug|release){
    +   LIBS += -lqhullstatic_r
    +   OBJECTS_DIR = Release
    +}
    +win32-msvc* : QMAKE_LFLAGS += /INCREMENTAL:NO
    +
    +INCLUDEPATH += ..
    +CONFIG += qhull_warn_conversion
    +
    diff --git a/xs/src/qhull/src/qhull-app-cpp.pri b/xs/src/qhull/src/qhull-app-cpp.pri
    new file mode 100644
    index 000000000..a6f17d8ec
    --- /dev/null
    +++ b/xs/src/qhull/src/qhull-app-cpp.pri
    @@ -0,0 +1,23 @@
    +# -------------------------------------------------
    +# qhull-app-cpp.pri -- Qt include project for qhull as C++ classes
    +# -------------------------------------------------
    +
    +include(qhull-warn.pri)
    +
    +DESTDIR = ../../bin
    +TEMPLATE = app
    +CONFIG += console warn_on
    +CONFIG -= rtti
    +LIBS += -L../../lib
    +build_pass:CONFIG(debug, debug|release){
    +   LIBS += -lqhullcpp_d
    +   LIBS += -lqhullstatic_rd  # Must be last, otherwise qh_fprintf,etc. are loaded from here instead of qhullcpp-d.lib
    +   OBJECTS_DIR = Debug
    +}else:build_pass:CONFIG(release, debug|release){
    +   LIBS += -lqhullcpp
    +   LIBS += -lqhullstatic_r  # Must be last, otherwise qh_fprintf,etc. are loaded from here instead of qhullcpp.lib
    +   OBJECTS_DIR = Release
    +}
    +win32-msvc* : QMAKE_LFLAGS += /INCREMENTAL:NO
    +
    +INCLUDEPATH += ../../src # "libqhull_r/qhull_a.h"
    diff --git a/xs/src/qhull/src/qhull-app-shared.pri b/xs/src/qhull/src/qhull-app-shared.pri
    new file mode 100644
    index 000000000..1f4026a6a
    --- /dev/null
    +++ b/xs/src/qhull/src/qhull-app-shared.pri
    @@ -0,0 +1,27 @@
    +# -------------------------------------------------
    +# qhull-app-shared.pri -- Deprecated Qt include project for C qhull applications linked with libqhull (shared library)
    +# -------------------------------------------------
    +
    +include(qhull-warn.pri)
    +
    +DESTDIR = ../../bin
    +TEMPLATE = app
    +CONFIG += console warn_on
    +CONFIG -= qt
    +
    +LIBS += -L../../lib
    +build_pass:CONFIG(debug, debug|release){
    +   LIBS += -lqhull_d
    +   OBJECTS_DIR = Debug
    +}else:build_pass:CONFIG(release, debug|release){
    +   LIBS += -lqhull
    +   OBJECTS_DIR = Release
    +}
    +win32-msvc* : QMAKE_LFLAGS += /INCREMENTAL:NO
    +
    +win32-msvc* : DEFINES += qh_dllimport # libqhull/user.h
    +
    +INCLUDEPATH += ../libqhull
    +CONFIG += qhull_warn_conversion
    +
    +
    diff --git a/xs/src/qhull/src/qhull-app-shared_r.pri b/xs/src/qhull/src/qhull-app-shared_r.pri
    new file mode 100644
    index 000000000..e55c1a65f
    --- /dev/null
    +++ b/xs/src/qhull/src/qhull-app-shared_r.pri
    @@ -0,0 +1,29 @@
    +# -------------------------------------------------
    +# qhull-app-shared_r.pri -- Qt include project for C qhull applications linked with libqhull_r (shared library)
    +#
    +# It uses reentrant Qhull
    +# -------------------------------------------------
    +
    +include(qhull-warn.pri)
    +
    +DESTDIR = ../../bin
    +TEMPLATE = app
    +CONFIG += console warn_on
    +CONFIG -= qt
    +
    +LIBS += -L../../lib
    +build_pass:CONFIG(debug, debug|release){
    +   LIBS += -lqhull_rd
    +   OBJECTS_DIR = Debug
    +}else:build_pass:CONFIG(release, debug|release){
    +   LIBS += -lqhull_r
    +   OBJECTS_DIR = Release
    +}
    +win32-msvc* : QMAKE_LFLAGS += /INCREMENTAL:NO
    +
    +win32-msvc* : DEFINES += qh_dllimport # libqhull_r/user.h
    +
    +INCLUDEPATH += ..
    +CONFIG += qhull_warn_conversion
    +
    +
    diff --git a/xs/src/qhull/src/qhull-libqhull-src.pri b/xs/src/qhull/src/qhull-libqhull-src.pri
    new file mode 100644
    index 000000000..e7aff3f78
    --- /dev/null
    +++ b/xs/src/qhull/src/qhull-libqhull-src.pri
    @@ -0,0 +1,39 @@
    +# -------------------------------------------------
    +# qhull-libqhull-src.pri -- Qt include project for libqhull sources and headers
    +#   libqhull.pro, libqhullp.pro, and libqhulldll.pro are the same for SOURCES and HEADERS
    +# -------------------------------------------------
    +
    +# Order object files by frequency of execution.  Small files at end.
    +# Current directory is caller
    +
    +# libqhull/libqhull.pro and ../qhull-libqhull-src.pri have the same SOURCES and HEADERS
    +SOURCES += ../libqhull/global.c
    +SOURCES += ../libqhull/stat.c
    +SOURCES += ../libqhull/geom2.c
    +SOURCES += ../libqhull/poly2.c
    +SOURCES += ../libqhull/merge.c
    +SOURCES += ../libqhull/libqhull.c
    +SOURCES += ../libqhull/geom.c
    +SOURCES += ../libqhull/poly.c
    +SOURCES += ../libqhull/qset.c
    +SOURCES += ../libqhull/mem.c
    +SOURCES += ../libqhull/random.c
    +SOURCES += ../libqhull/usermem.c
    +SOURCES += ../libqhull/userprintf.c
    +SOURCES += ../libqhull/io.c
    +SOURCES += ../libqhull/user.c
    +SOURCES += ../libqhull/rboxlib.c
    +SOURCES += ../libqhull/userprintf_rbox.c
    +
    +# [2014] qmake locates the headers in the shadow build directory not the src directory
    +HEADERS += ../libqhull/geom.h
    +HEADERS += ../libqhull/io.h
    +HEADERS += ../libqhull/libqhull.h
    +HEADERS += ../libqhull/mem.h
    +HEADERS += ../libqhull/merge.h
    +HEADERS += ../libqhull/poly.h
    +HEADERS += ../libqhull/random.h
    +HEADERS += ../libqhull/qhull_a.h
    +HEADERS += ../libqhull/qset.h
    +HEADERS += ../libqhull/stat.h
    +HEADERS += ../libqhull/user.h
    diff --git a/xs/src/qhull/src/qhull-libqhull-src_r.pri b/xs/src/qhull/src/qhull-libqhull-src_r.pri
    new file mode 100644
    index 000000000..3b53291b1
    --- /dev/null
    +++ b/xs/src/qhull/src/qhull-libqhull-src_r.pri
    @@ -0,0 +1,39 @@
    +# -------------------------------------------------
    +# qhull-libqhull-src_r.pri -- Qt include project for libqhull_r sources and headers
    +#
    +# It uses reentrant Qhull
    +# -------------------------------------------------
    +
    +# Order object files by frequency of execution.  Small files at end.
    +# Current directory is caller
    +
    +# libqhull_r/libqhull_r.pro and ../qhull-libqhull-src_r.pri have the same SOURCES and HEADERS
    +SOURCES += ../libqhull_r/global_r.c
    +SOURCES += ../libqhull_r/stat_r.c
    +SOURCES += ../libqhull_r/geom2_r.c
    +SOURCES += ../libqhull_r/poly2_r.c
    +SOURCES += ../libqhull_r/merge_r.c
    +SOURCES += ../libqhull_r/libqhull_r.c
    +SOURCES += ../libqhull_r/geom_r.c
    +SOURCES += ../libqhull_r/poly_r.c
    +SOURCES += ../libqhull_r/qset_r.c
    +SOURCES += ../libqhull_r/mem_r.c
    +SOURCES += ../libqhull_r/random_r.c
    +SOURCES += ../libqhull_r/usermem_r.c
    +SOURCES += ../libqhull_r/userprintf_r.c
    +SOURCES += ../libqhull_r/io_r.c
    +SOURCES += ../libqhull_r/user_r.c
    +SOURCES += ../libqhull_r/rboxlib_r.c
    +SOURCES += ../libqhull_r/userprintf_rbox_r.c
    +
    +HEADERS += ../libqhull_r/geom_r.h
    +HEADERS += ../libqhull_r/io_r.h
    +HEADERS += ../libqhull_r/libqhull_r.h
    +HEADERS += ../libqhull_r/mem_r.h
    +HEADERS += ../libqhull_r/merge_r.h
    +HEADERS += ../libqhull_r/poly_r.h
    +HEADERS += ../libqhull_r/random_r.h
    +HEADERS += ../libqhull_r/qhull_ra.h
    +HEADERS += ../libqhull_r/qset_r.h
    +HEADERS += ../libqhull_r/stat_r.h
    +HEADERS += ../libqhull_r/user_r.h
    diff --git a/xs/src/qhull/src/qhull-warn.pri b/xs/src/qhull/src/qhull-warn.pri
    new file mode 100644
    index 000000000..7d0e7fa2f
    --- /dev/null
    +++ b/xs/src/qhull/src/qhull-warn.pri
    @@ -0,0 +1,57 @@
    +# -------------------------------------------------
    +# qhull-warn.pri -- Qt project warnings for warn_on
    +#   CONFIG += qhull_warn_all        # Qhull compiles with all warnings except for qhull_warn_shadow and qhull_warn_conversion
    +#   CONFIG += qhull_warn_conversion # Warn in Qt and Qhull about conversion errors
    +#   CONFIG += qhull_warn_error      # Turn warnings into errors
    +#   CONFIG += qhull_warn_shadow     # Warn in Qt about shadowing of functions and fields
    +# -------------------------------------------------
    +
    +# [apr'11] VERSION works erratically for msvc builds
    +# VERSION = 7.2.0
    +qhull_SOVERSION = 7
    +
    +# Uncomment to report warnings as errors
    +#CONFIG += qhull_warn_error
    +
    +*g++{
    +    qhull_warn_error{
    +        QMAKE_CFLAGS_WARN_ON += -Werror
    +        QMAKE_CXXFLAGS_WARN_ON += -Werror
    +    }
    +
    +    QMAKE_CFLAGS_WARN_ON += -Wcast-qual -Wextra -Wshadow -Wwrite-strings
    +
    +    QMAKE_CXXFLAGS_WARN_ON += -Wcast-qual -Wextra -Wwrite-strings
    +    QMAKE_CXXFLAGS_WARN_ON += -Wno-sign-conversion
    +
    +    qhull_warn_shadow{
    +        QMAKE_CXXFLAGS_WARN_ON += -Wshadow     # Shadowing occurs in Qt, e.g., nested foreach
    +    }
    +
    +    qhull_warn_conversion{
    +        QMAKE_CFLAGS_WARN_ON += -Wno-sign-conversion   # libqhullstatic has many size_t vs. int warnings
    +        QMAKE_CFLAGS_WARN_ON += -Wconversion           # libqhullstatic has no workaround for bit-field conversions
    +        QMAKE_CXXFLAGS_WARN_ON += -Wconversion         # Qt has conversion errors for qbitarray and qpalette
    +    }
    +
    +    qhull_warn_all{
    +        QMAKE_CFLAGS_WARN_ON += -Waddress -Warray-bounds -Wchar-subscripts -Wclobbered -Wcomment -Wempty-body
    +        QMAKE_CFLAGS_WARN_ON += -Wformat -Wignored-qualifiers -Wimplicit-function-declaration -Wimplicit-int
    +        QMAKE_CFLAGS_WARN_ON += -Wmain -Wmissing-braces -Wmissing-field-initializers -Wmissing-parameter-type
    +        QMAKE_CFLAGS_WARN_ON += -Wnonnull -Wold-style-declaration -Woverride-init -Wparentheses
    +        QMAKE_CFLAGS_WARN_ON += -Wpointer-sign -Wreturn-type -Wsequence-point -Wsign-compare
    +        QMAKE_CFLAGS_WARN_ON += -Wsign-compare -Wstrict-aliasing -Wstrict-overflow=1 -Wswitch
    +        QMAKE_CFLAGS_WARN_ON += -Wtrigraphs -Wtype-limits -Wuninitialized -Wuninitialized
    +        QMAKE_CFLAGS_WARN_ON += -Wunknown-pragmas -Wunused-function -Wunused-label -Wunused-parameter
    +        QMAKE_CFLAGS_WARN_ON += -Wunused-value -Wunused-variable -Wvolatile-register-var
    +
    +        QMAKE_CXXFLAGS_WARN_ON += -Waddress -Warray-bounds -Wc++0x-compat -Wchar-subscripts
    +        QMAKE_CXXFLAGS_WARN_ON += -Wclobbered -Wcomment -Wempty-body -Wenum-compare
    +        QMAKE_CXXFLAGS_WARN_ON += -Wformat -Wignored-qualifiers -Wmain -Wmissing-braces
    +        QMAKE_CXXFLAGS_WARN_ON += -Wmissing-field-initializers -Wparentheses -Wreorder -Wreturn-type
    +        QMAKE_CXXFLAGS_WARN_ON += -Wsequence-point -Wsign-compare -Wsign-compare -Wstrict-aliasing
    +        QMAKE_CXXFLAGS_WARN_ON += -Wstrict-overflow=1 -Wswitch -Wtrigraphs -Wtype-limits
    +        QMAKE_CXXFLAGS_WARN_ON += -Wuninitialized -Wunknown-pragmas -Wunused-function -Wunused-label
    +        QMAKE_CXXFLAGS_WARN_ON += -Wunused-parameter -Wunused-value -Wunused-variable -Wvolatile-register-var
    +    }
    +}
    diff --git a/xs/src/qhull/src/qhull/qhull.pro b/xs/src/qhull/src/qhull/qhull.pro
    new file mode 100644
    index 000000000..839372856
    --- /dev/null
    +++ b/xs/src/qhull/src/qhull/qhull.pro
    @@ -0,0 +1,9 @@
    +# -------------------------------------------------
    +# qhull.pro -- Qt project file for qhull.exe with libqhullstatic_r
    +# -------------------------------------------------
    +
    +include(../qhull-app-c_r.pri)
    +
    +TARGET = qhull
    +
    +SOURCES += unix_r.c
    diff --git a/xs/src/qhull/src/qhull/unix.c b/xs/src/qhull/src/qhull/unix.c
    new file mode 100644
    index 000000000..892a819c3
    --- /dev/null
    +++ b/xs/src/qhull/src/qhull/unix.c
    @@ -0,0 +1,372 @@
    +/*
      ---------------------------------
    +
    +   unix.c
    +     command line interface to qhull
    +         includes SIOUX interface for Macintoshes
    +
    +   see qh-qhull.htm
    +
    +   Copyright (c) 1993-2015 The Geometry Center.
    +   $Id: //main/2015/qhull/src/qhull/unix.c#4 $$Change: 2066 $
    +   $DateTime: 2016/01/18 19:29:17 $$Author: bbarber $
    +*/
    +
    +#include "libqhull/libqhull.h"
    +#include "libqhull/qset.h"
    +
    +#include 
    +#include 
    +#include 
    +#include 
    +#include 
    +
    +#if __cplusplus
    +extern "C" {
    +  int isatty(int);
    +}
    +
    +#elif _MSC_VER
    +#include 
    +#define isatty _isatty
    +/* int _isatty(int); */
    +
    +#else
    +int isatty(int);  /* returns 1 if stdin is a tty
    +                   if "Undefined symbol" this can be deleted along with call in main() */
    +#endif
    +
    +/*---------------------------------
    +
    +  qh_prompt
    +    long prompt for qhull
    +
    +  see:
    +    concise prompt below
    +*/
    +char qh_prompta[]= "\n\
    +qhull- compute convex hulls and related structures.\n\
    +    http://www.qhull.org  %s\n\
    +\n\
    +input (stdin):\n\
    +    first lines: dimension and number of points (or vice-versa).\n\
    +    other lines: point coordinates, best if one point per line\n\
    +    comments:    start with a non-numeric character\n\
    +    halfspaces:  use dim plus one and put offset after coefficients.\n\
    +                 May be preceded by a single interior point ('H').\n\
    +\n\
    +options:\n\
    +    d    - Delaunay triangulation by lifting points to a paraboloid\n\
    +    d Qu - furthest-site Delaunay triangulation (upper convex hull)\n\
    +    v    - Voronoi diagram (dual of the Delaunay triangulation)\n\
    +    v Qu - furthest-site Voronoi diagram\n\
    +    Hn,n,... - halfspace intersection about point [n,n,0,...]\n\
    +    Qt   - triangulated output\n\
    +    QJ   - joggled input instead of merged facets\n\
    +    Qc   - keep coplanar points with nearest facet\n\
    +    Qi   - keep interior points with nearest facet\n\
    +\n\
    +Qhull control options:\n\
    +    Qbk:n   - scale coord k so that low bound is n\n\
    +      QBk:n - scale coord k so that upper bound is n (QBk is %2.2g)\n\
    +    QbB  - scale input to unit cube centered at the origin\n\
    +    Qbb  - scale last coordinate to [0,m] for Delaunay triangulations\n\
    +    Qbk:0Bk:0 - remove k-th coordinate from input\n\
    +    QJn  - randomly joggle input in range [-n,n]\n\
    +    QRn  - random rotation (n=seed, n=0 time, n=-1 time/no rotate)\n\
    +%s%s%s%s";  /* split up qh_prompt for Visual C++ */
    +char qh_promptb[]= "\
    +    Qf   - partition point to furthest outside facet\n\
    +    Qg   - only build good facets (needs 'QGn', 'QVn', or 'PdD')\n\
    +    Qm   - only process points that would increase max_outside\n\
    +    Qr   - process random outside points instead of furthest ones\n\
    +    Qs   - search all points for the initial simplex\n\
    +    Qu   - for 'd' or 'v', compute upper hull without point at-infinity\n\
    +              returns furthest-site Delaunay triangulation\n\
    +    Qv   - test vertex neighbors for convexity\n\
    +    Qx   - exact pre-merges (skips coplanar and angle-coplanar facets)\n\
    +    Qz   - add point-at-infinity to Delaunay triangulation\n\
    +    QGn  - good facet if visible from point n, -n for not visible\n\
    +    QVn  - good facet if it includes point n, -n if not\n\
    +    Q0   - turn off default premerge with 'C-0'/'Qx'\n\
    +    Q1     - sort merges by type instead of angle\n\
    +    Q2   - merge all non-convex at once instead of independent sets\n\
    +    Q3   - do not merge redundant vertices\n\
    +    Q4   - avoid old->new merges\n\
    +    Q5   - do not correct outer planes at end of qhull\n\
    +    Q6   - do not pre-merge concave or coplanar facets\n\
    +    Q7   - depth-first processing instead of breadth-first\n\
    +    Q8   - do not process near-inside points\n\
    +    Q9   - process furthest of furthest points\n\
    +    Q10  - no special processing for narrow distributions\n\
    +    Q11  - copy normals and recompute centrums for tricoplanar facets\n\
    +    Q12  - no error on wide merge due to duplicate ridge\n\
    +\n\
    +";
    +char qh_promptc[]= "\
    +Topts- Trace options:\n\
    +    T4   - trace at level n, 4=all, 5=mem/gauss, -1= events\n\
    +    Ta   - annotate output with message codes\n\
    +    Tc   - check frequently during execution\n\
    +    Ts   - print statistics\n\
    +    Tv   - verify result: structure, convexity, and point inclusion\n\
    +    Tz   - send all output to stdout\n\
    +    TFn  - report summary when n or more facets created\n\
    +    TI file - input data from file, no spaces or single quotes\n\
    +    TO file - output results to file, may be enclosed in single quotes\n\
    +    TPn  - turn on tracing when point n added to hull\n\
    +     TMn - turn on tracing at merge n\n\
    +     TWn - trace merge facets when width > n\n\
    +    TRn  - rerun qhull n times.  Use with 'QJn'\n\
    +    TVn  - stop qhull after adding point n, -n for before (see TCn)\n\
    +     TCn - stop qhull after building cone for point n (see TVn)\n\
    +\n\
    +Precision options:\n\
    +    Cn   - radius of centrum (roundoff added).  Merge facets if non-convex\n\
    +     An  - cosine of maximum angle.  Merge facets if cosine > n or non-convex\n\
    +           C-0 roundoff, A-0.99/C-0.01 pre-merge, A0.99/C0.01 post-merge\n\
    +    En   - max roundoff error for distance computation\n\
    +    Rn   - randomly perturb computations by a factor of [1-n,1+n]\n\
    +    Vn   - min distance above plane for a visible facet (default 3C-n or En)\n\
    +    Un   - max distance below plane for a new, coplanar point (default Vn)\n\
    +    Wn   - min facet width for outside point (before roundoff, default 2Vn)\n\
    +\n\
    +Output formats (may be combined; if none, produces a summary to stdout):\n\
    +    f    - facet dump\n\
    +    G    - Geomview output (see below)\n\
    +    i    - vertices incident to each facet\n\
    +    m    - Mathematica output (2-d and 3-d)\n\
    +    o    - OFF format (dim, points and facets; Voronoi regions)\n\
    +    n    - normals with offsets\n\
    +    p    - vertex coordinates or Voronoi vertices (coplanar points if 'Qc')\n\
    +    s    - summary (stderr)\n\
    +\n\
    +";
    +char qh_promptd[]= "\
    +More formats:\n\
    +    Fa   - area for each facet\n\
    +    FA   - compute total area and volume for option 's'\n\
    +    Fc   - count plus coplanar points for each facet\n\
    +           use 'Qc' (default) for coplanar and 'Qi' for interior\n\
    +    FC   - centrum or Voronoi center for each facet\n\
    +    Fd   - use cdd format for input (homogeneous with offset first)\n\
    +    FD   - use cdd format for numeric output (offset first)\n\
    +    FF   - facet dump without ridges\n\
    +    Fi   - inner plane for each facet\n\
    +           for 'v', separating hyperplanes for bounded Voronoi regions\n\
    +    FI   - ID of each facet\n\
    +    Fm   - merge count for each facet (511 max)\n\
    +    FM   - Maple output (2-d and 3-d)\n\
    +    Fn   - count plus neighboring facets for each facet\n\
    +    FN   - count plus neighboring facets for each point\n\
    +    Fo   - outer plane (or max_outside) for each facet\n\
    +           for 'v', separating hyperplanes for unbounded Voronoi regions\n\
    +    FO   - options and precision constants\n\
    +    Fp   - dim, count, and intersection coordinates (halfspace only)\n\
    +    FP   - nearest vertex and distance for each coplanar point\n\
    +    FQ   - command used for qhull\n\
    +    Fs   - summary: #int (8), dimension, #points, tot vertices, tot facets,\n\
    +                      output: #vertices, #facets, #coplanars, #nonsimplicial\n\
    +                    #real (2), max outer plane, min vertex\n\
    +    FS   - sizes:   #int (0)\n\
    +                    #real (2) tot area, tot volume\n\
    +    Ft   - triangulation with centrums for non-simplicial facets (OFF format)\n\
    +    Fv   - count plus vertices for each facet\n\
    +           for 'v', Voronoi diagram as Voronoi vertices for pairs of sites\n\
    +    FV   - average of vertices (a feasible point for 'H')\n\
    +    Fx   - extreme points (in order for 2-d)\n\
    +\n\
    +";
    +char qh_prompte[]= "\
    +Geomview options (2-d, 3-d, and 4-d; 2-d Voronoi)\n\
    +    Ga   - all points as dots\n\
    +     Gp  -  coplanar points and vertices as radii\n\
    +     Gv  -  vertices as spheres\n\
    +    Gi   - inner planes only\n\
    +     Gn  -  no planes\n\
    +     Go  -  outer planes only\n\
    +    Gc   - centrums\n\
    +    Gh   - hyperplane intersections\n\
    +    Gr   - ridges\n\
    +    GDn  - drop dimension n in 3-d and 4-d output\n\
    +    Gt   - for 3-d 'd', transparent outer ridges\n\
    +\n\
    +Print options:\n\
    +    PAn  - keep n largest facets by area\n\
    +    Pdk:n - drop facet if normal[k] <= n (default 0.0)\n\
    +    PDk:n - drop facet if normal[k] >= n\n\
    +    Pg   - print good facets (needs 'QGn' or 'QVn')\n\
    +    PFn  - keep facets whose area is at least n\n\
    +    PG   - print neighbors of good facets\n\
    +    PMn  - keep n facets with most merges\n\
    +    Po   - force output.  If error, output neighborhood of facet\n\
    +    Pp   - do not report precision problems\n\
    +\n\
    +    .    - list of all options\n\
    +    -    - one line descriptions of all options\n\
    +    -V   - version\n\
    +";
    +/* for opts, don't assign 'e' or 'E' to a flag (already used for exponent) */
    +
    +/*---------------------------------
    +
    +  qh_prompt2
    +    synopsis for qhull
    +*/
    +char qh_prompt2[]= "\n\
    +qhull- compute convex hulls and related structures.  Qhull %s\n\
    +    input (stdin): dimension, n, point coordinates\n\
    +    comments start with a non-numeric character\n\
    +    halfspace: use dim+1 and put offsets after coefficients\n\
    +\n\
    +options (qh-quick.htm):\n\
    +    d    - Delaunay triangulation by lifting points to a paraboloid\n\
    +    d Qu - furthest-site Delaunay triangulation (upper convex hull)\n\
    +    v    - Voronoi diagram as the dual of the Delaunay triangulation\n\
    +    v Qu - furthest-site Voronoi diagram\n\
    +    H1,1 - Halfspace intersection about [1,1,0,...] via polar duality\n\
    +    Qt   - triangulated output\n\
    +    QJ   - joggled input instead of merged facets\n\
    +    Tv   - verify result: structure, convexity, and point inclusion\n\
    +    .    - concise list of all options\n\
    +    -    - one-line description of each option\n\
    +    -V   - version\n\
    +\n\
    +Output options (subset):\n\
    +    s    - summary of results (default)\n\
    +    i    - vertices incident to each facet\n\
    +    n    - normals with offsets\n\
    +    p    - vertex coordinates (if 'Qc', includes coplanar points)\n\
    +           if 'v', Voronoi vertices\n\
    +    Fp   - halfspace intersections\n\
    +    Fx   - extreme points (convex hull vertices)\n\
    +    FA   - compute total area and volume\n\
    +    o    - OFF format (if 'v', outputs Voronoi regions)\n\
    +    G    - Geomview output (2-d, 3-d and 4-d)\n\
    +    m    - Mathematica output (2-d and 3-d)\n\
    +    QVn  - print facets that include point n, -n if not\n\
    +    TO file- output results to file, may be enclosed in single quotes\n\
    +\n\
    +examples:\n\
    +    rbox D4 | qhull Tv                        rbox 1000 s | qhull Tv s FA\n\
    +    rbox 10 D2 | qhull d QJ s i TO result     rbox 10 D2 | qhull v Qbb Qt p\n\
    +    rbox 10 D2 | qhull d Qu QJ m              rbox 10 D2 | qhull v Qu QJ o\n\
    +    rbox c d D2 | qhull Qc s f Fx | more      rbox c | qhull FV n | qhull H Fp\n\
    +    rbox d D12 | qhull QR0 FA                 rbox c D7 | qhull FA TF1000\n\
    +    rbox y 1000 W0 | qhull                    rbox c | qhull n\n\
    +\n\
    +";
    +/* for opts, don't assign 'e' or 'E' to a flag (already used for exponent) */
    +
    +/*---------------------------------
    +
    +  qh_prompt3
    +    concise prompt for qhull
    +*/
    +char qh_prompt3[]= "\n\
    +Qhull %s.\n\
    +Except for 'F.' and 'PG', upper-case options take an argument.\n\
    +\n\
    + delaunay       voronoi        Geomview       Halfspace      facet_dump\n\
    + incidences     mathematica    normals        OFF_format     points\n\
    + summary\n\
    +\n\
    + Farea          FArea-total    Fcoplanars     FCentrums      Fd-cdd-in\n\
    + FD-cdd-out     FF-dump-xridge Finner         FIDs           Fmerges\n\
    + Fneighbors     FNeigh-vertex  Fouter         FOptions       Fpoint-intersect\n\
    + FPoint_near    FQhull         Fsummary       FSize          Ftriangles\n\
    + Fvertices      Fvoronoi       FVertex-ave    Fxtremes       FMaple\n\
    +\n\
    + Gvertices      Gpoints        Gall_points    Gno_planes     Ginner\n\
    + Gcentrums      Ghyperplanes   Gridges        Gouter         GDrop_dim\n\
    + Gtransparent\n\
    +\n\
    + PArea-keep     Pdrop d0:0D0   Pgood          PFacet_area_keep\n\
    + PGood_neighbors PMerge-keep   Poutput_forced Pprecision_not\n\
    +\n\
    + QbBound 0:0.5  Qbk:0Bk:0_drop QbB-scale-box  Qbb-scale-last Qcoplanar\n\
    + Qfurthest      Qgood_only     QGood_point    Qinterior      Qmax_out\n\
    + QJoggle        Qrandom        QRotate        Qsearch_1st    Qtriangulate\n\
    + QupperDelaunay QVertex_good   Qvneighbors    Qxact_merge    Qzinfinite\n\
    +\n\
    + Q0_no_premerge Q1_no_angle    Q2_no_independ Q3_no_redundant Q4_no_old\n\
    + Q5_no_check_out Q6_no_concave Q7_depth_first Q8_no_near_in  Q9_pick_furthest\n\
    + Q10_no_narrow  Q11_trinormals Q12_no_wide_dup\n\
    +\n\
    + T4_trace       Tannotate      Tcheck_often   Tstatistics    Tverify\n\
    + Tz_stdout      TFacet_log     TInput_file    TPoint_trace   TMerge_trace\n\
    + TOutput_file   TRerun         TWide_trace    TVertex_stop   TCone_stop\n\
    +\n\
    + Angle_max      Centrum_size   Error_round    Random_dist    Visible_min\n\
    + Ucoplanar_max  Wide_outside\n\
    +";
    +
    +/*---------------------------------
    +
    +  main( argc, argv )
    +    processes the command line, calls qhull() to do the work, and exits
    +
    +  design:
    +    initializes data structures
    +    reads points
    +    finishes initialization
    +    computes convex hull and other structures
    +    checks the result
    +    writes the output
    +    frees memory
    +*/
    +int main(int argc, char *argv[]) {
    +  int curlong, totlong; /* used !qh_NOmem */
    +  int exitcode, numpoints, dim;
    +  coordT *points;
    +  boolT ismalloc;
    +
    +  QHULL_LIB_CHECK /* Check for compatible library */
    +
    +  if ((argc == 1) && isatty( 0 /*stdin*/)) {
    +    fprintf(stdout, qh_prompt2, qh_version);
    +    exit(qh_ERRnone);
    +  }
    +  if (argc > 1 && *argv[1] == '-' && !*(argv[1]+1)) {
    +    fprintf(stdout, qh_prompta, qh_version, qh_DEFAULTbox,
    +                qh_promptb, qh_promptc, qh_promptd, qh_prompte);
    +    exit(qh_ERRnone);
    +  }
    +  if (argc > 1 && *argv[1] == '.' && !*(argv[1]+1)) {
    +    fprintf(stdout, qh_prompt3, qh_version);
    +    exit(qh_ERRnone);
    +  }
    +  if (argc > 1 && *argv[1] == '-' && *(argv[1]+1)=='V') {
    +      fprintf(stdout, "%s\n", qh_version2);
    +      exit(qh_ERRnone);
    +  }
    +  qh_init_A(stdin, stdout, stderr, argc, argv);  /* sets qh qhull_command */
    +  exitcode= setjmp(qh errexit); /* simple statement for CRAY J916 */
    +  if (!exitcode) {
    +    qh_initflags(qh qhull_command);
    +    points= qh_readpoints(&numpoints, &dim, &ismalloc);
    +    qh_init_B(points, numpoints, dim, ismalloc);
    +    qh_qhull();
    +    qh_check_output();
    +    qh_produce_output();
    +    if (qh VERIFYoutput && !qh FORCEoutput && !qh STOPpoint && !qh STOPcone)
    +      qh_check_points();
    +    exitcode= qh_ERRnone;
    +  }
    +  qh NOerrexit= True;  /* no more setjmp */
    +#ifdef qh_NOmem
    +  qh_freeqhull( qh_ALL);
    +#else
    +  qh_freeqhull( !qh_ALL);
    +  qh_memfreeshort(&curlong, &totlong);
    +  if (curlong || totlong)
    +    qh_fprintf_stderr(6263, "qhull internal warning (main): did not free %d bytes of long memory(%d pieces)\n",
    +       totlong, curlong);
    +#endif
    +  return exitcode;
    +} /* main */
    +
    diff --git a/xs/src/qhull/src/qhull/unix_r.c b/xs/src/qhull/src/qhull/unix_r.c
    new file mode 100644
    index 000000000..3f999f7fa
    --- /dev/null
    +++ b/xs/src/qhull/src/qhull/unix_r.c
    @@ -0,0 +1,374 @@
    +/*
      ---------------------------------
    +
    +   unix.c
    +     command line interface to qhull
    +         includes SIOUX interface for Macintoshes
    +
    +   see qh-qhull.htm
    +
    +   Copyright (c) 1993-2015 The Geometry Center.
    +   $Id: //main/2015/qhull/src/qhull/unix_r.c#6 $$Change: 2066 $
    +   $DateTime: 2016/01/18 19:29:17 $$Author: bbarber $
    +*/
    +
    +#include "libqhull_r/libqhull_r.h"
    +
    +#include 
    +#include 
    +#include 
    +#include 
    +#include 
    +
    +#if __cplusplus
    +extern "C" {
    +  int isatty(int);
    +}
    +
    +#elif _MSC_VER
    +#include 
    +#define isatty _isatty
    +/* int _isatty(int); */
    +
    +#else
    +int isatty(int);  /* returns 1 if stdin is a tty
    +                   if "Undefined symbol" this can be deleted along with call in main() */
    +#endif
    +
    +/*---------------------------------
    +
    +  qh_prompt
    +    long prompt for qhull
    +
    +  see:
    +    concise prompt below
    +*/
    +char qh_prompta[]= "\n\
    +qhull- compute convex hulls and related structures.\n\
    +    http://www.qhull.org  %s\n\
    +\n\
    +input (stdin):\n\
    +    first lines: dimension and number of points (or vice-versa).\n\
    +    other lines: point coordinates, best if one point per line\n\
    +    comments:    start with a non-numeric character\n\
    +    halfspaces:  use dim plus one and put offset after coefficients.\n\
    +                 May be preceded by a single interior point ('H').\n\
    +\n\
    +options:\n\
    +    d    - Delaunay triangulation by lifting points to a paraboloid\n\
    +    d Qu - furthest-site Delaunay triangulation (upper convex hull)\n\
    +    v    - Voronoi diagram (dual of the Delaunay triangulation)\n\
    +    v Qu - furthest-site Voronoi diagram\n\
    +    Hn,n,... - halfspace intersection about point [n,n,0,...]\n\
    +    Qt   - triangulated output\n\
    +    QJ   - joggled input instead of merged facets\n\
    +    Qc   - keep coplanar points with nearest facet\n\
    +    Qi   - keep interior points with nearest facet\n\
    +\n\
    +Qhull control options:\n\
    +    Qbk:n   - scale coord k so that low bound is n\n\
    +      QBk:n - scale coord k so that upper bound is n (QBk is %2.2g)\n\
    +    QbB  - scale input to unit cube centered at the origin\n\
    +    Qbb  - scale last coordinate to [0,m] for Delaunay triangulations\n\
    +    Qbk:0Bk:0 - remove k-th coordinate from input\n\
    +    QJn  - randomly joggle input in range [-n,n]\n\
    +    QRn  - random rotation (n=seed, n=0 time, n=-1 time/no rotate)\n\
    +%s%s%s%s";  /* split up qh_prompt for Visual C++ */
    +char qh_promptb[]= "\
    +    Qf   - partition point to furthest outside facet\n\
    +    Qg   - only build good facets (needs 'QGn', 'QVn', or 'PdD')\n\
    +    Qm   - only process points that would increase max_outside\n\
    +    Qr   - process random outside points instead of furthest ones\n\
    +    Qs   - search all points for the initial simplex\n\
    +    Qu   - for 'd' or 'v', compute upper hull without point at-infinity\n\
    +              returns furthest-site Delaunay triangulation\n\
    +    Qv   - test vertex neighbors for convexity\n\
    +    Qx   - exact pre-merges (skips coplanar and angle-coplanar facets)\n\
    +    Qz   - add point-at-infinity to Delaunay triangulation\n\
    +    QGn  - good facet if visible from point n, -n for not visible\n\
    +    QVn  - good facet if it includes point n, -n if not\n\
    +    Q0   - turn off default premerge with 'C-0'/'Qx'\n\
    +    Q1     - sort merges by type instead of angle\n\
    +    Q2   - merge all non-convex at once instead of independent sets\n\
    +    Q3   - do not merge redundant vertices\n\
    +    Q4   - avoid old->new merges\n\
    +    Q5   - do not correct outer planes at end of qhull\n\
    +    Q6   - do not pre-merge concave or coplanar facets\n\
    +    Q7   - depth-first processing instead of breadth-first\n\
    +    Q8   - do not process near-inside points\n\
    +    Q9   - process furthest of furthest points\n\
    +    Q10  - no special processing for narrow distributions\n\
    +    Q11  - copy normals and recompute centrums for tricoplanar facets\n\
    +    Q12  - no error on wide merge due to duplicate ridge\n\
    +\n\
    +";
    +char qh_promptc[]= "\
    +Topts- Trace options:\n\
    +    T4   - trace at level n, 4=all, 5=mem/gauss, -1= events\n\
    +    Ta   - annotate output with message codes\n\
    +    Tc   - check frequently during execution\n\
    +    Ts   - print statistics\n\
    +    Tv   - verify result: structure, convexity, and point inclusion\n\
    +    Tz   - send all output to stdout\n\
    +    TFn  - report summary when n or more facets created\n\
    +    TI file - input data from file, no spaces or single quotes\n\
    +    TO file - output results to file, may be enclosed in single quotes\n\
    +    TPn  - turn on tracing when point n added to hull\n\
    +     TMn - turn on tracing at merge n\n\
    +     TWn - trace merge facets when width > n\n\
    +    TRn  - rerun qhull n times.  Use with 'QJn'\n\
    +    TVn  - stop qhull after adding point n, -n for before (see TCn)\n\
    +     TCn - stop qhull after building cone for point n (see TVn)\n\
    +\n\
    +Precision options:\n\
    +    Cn   - radius of centrum (roundoff added).  Merge facets if non-convex\n\
    +     An  - cosine of maximum angle.  Merge facets if cosine > n or non-convex\n\
    +           C-0 roundoff, A-0.99/C-0.01 pre-merge, A0.99/C0.01 post-merge\n\
    +    En   - max roundoff error for distance computation\n\
    +    Rn   - randomly perturb computations by a factor of [1-n,1+n]\n\
    +    Vn   - min distance above plane for a visible facet (default 3C-n or En)\n\
    +    Un   - max distance below plane for a new, coplanar point (default Vn)\n\
    +    Wn   - min facet width for outside point (before roundoff, default 2Vn)\n\
    +\n\
    +Output formats (may be combined; if none, produces a summary to stdout):\n\
    +    f    - facet dump\n\
    +    G    - Geomview output (see below)\n\
    +    i    - vertices incident to each facet\n\
    +    m    - Mathematica output (2-d and 3-d)\n\
    +    o    - OFF format (dim, points and facets; Voronoi regions)\n\
    +    n    - normals with offsets\n\
    +    p    - vertex coordinates or Voronoi vertices (coplanar points if 'Qc')\n\
    +    s    - summary (stderr)\n\
    +\n\
    +";
    +char qh_promptd[]= "\
    +More formats:\n\
    +    Fa   - area for each facet\n\
    +    FA   - compute total area and volume for option 's'\n\
    +    Fc   - count plus coplanar points for each facet\n\
    +           use 'Qc' (default) for coplanar and 'Qi' for interior\n\
    +    FC   - centrum or Voronoi center for each facet\n\
    +    Fd   - use cdd format for input (homogeneous with offset first)\n\
    +    FD   - use cdd format for numeric output (offset first)\n\
    +    FF   - facet dump without ridges\n\
    +    Fi   - inner plane for each facet\n\
    +           for 'v', separating hyperplanes for bounded Voronoi regions\n\
    +    FI   - ID of each facet\n\
    +    Fm   - merge count for each facet (511 max)\n\
    +    FM   - Maple output (2-d and 3-d)\n\
    +    Fn   - count plus neighboring facets for each facet\n\
    +    FN   - count plus neighboring facets for each point\n\
    +    Fo   - outer plane (or max_outside) for each facet\n\
    +           for 'v', separating hyperplanes for unbounded Voronoi regions\n\
    +    FO   - options and precision constants\n\
    +    Fp   - dim, count, and intersection coordinates (halfspace only)\n\
    +    FP   - nearest vertex and distance for each coplanar point\n\
    +    FQ   - command used for qhull\n\
    +    Fs   - summary: #int (8), dimension, #points, tot vertices, tot facets,\n\
    +                      output: #vertices, #facets, #coplanars, #nonsimplicial\n\
    +                    #real (2), max outer plane, min vertex\n\
    +    FS   - sizes:   #int (0)\n\
    +                    #real (2) tot area, tot volume\n\
    +    Ft   - triangulation with centrums for non-simplicial facets (OFF format)\n\
    +    Fv   - count plus vertices for each facet\n\
    +           for 'v', Voronoi diagram as Voronoi vertices for pairs of sites\n\
    +    FV   - average of vertices (a feasible point for 'H')\n\
    +    Fx   - extreme points (in order for 2-d)\n\
    +\n\
    +";
    +char qh_prompte[]= "\
    +Geomview options (2-d, 3-d, and 4-d; 2-d Voronoi)\n\
    +    Ga   - all points as dots\n\
    +     Gp  -  coplanar points and vertices as radii\n\
    +     Gv  -  vertices as spheres\n\
    +    Gi   - inner planes only\n\
    +     Gn  -  no planes\n\
    +     Go  -  outer planes only\n\
    +    Gc   - centrums\n\
    +    Gh   - hyperplane intersections\n\
    +    Gr   - ridges\n\
    +    GDn  - drop dimension n in 3-d and 4-d output\n\
    +    Gt   - for 3-d 'd', transparent outer ridges\n\
    +\n\
    +Print options:\n\
    +    PAn  - keep n largest facets by area\n\
    +    Pdk:n - drop facet if normal[k] <= n (default 0.0)\n\
    +    PDk:n - drop facet if normal[k] >= n\n\
    +    Pg   - print good facets (needs 'QGn' or 'QVn')\n\
    +    PFn  - keep facets whose area is at least n\n\
    +    PG   - print neighbors of good facets\n\
    +    PMn  - keep n facets with most merges\n\
    +    Po   - force output.  If error, output neighborhood of facet\n\
    +    Pp   - do not report precision problems\n\
    +\n\
    +    .    - list of all options\n\
    +    -    - one line descriptions of all options\n\
    +    -V   - version\n\
    +";
    +/* for opts, don't assign 'e' or 'E' to a flag (already used for exponent) */
    +
    +/*---------------------------------
    +
    +  qh_prompt2
    +    synopsis for qhull
    +*/
    +char qh_prompt2[]= "\n\
    +qhull- compute convex hulls and related structures.  Qhull %s\n\
    +    input (stdin): dimension, n, point coordinates\n\
    +    comments start with a non-numeric character\n\
    +    halfspace: use dim+1 and put offsets after coefficients\n\
    +\n\
    +options (qh-quick.htm):\n\
    +    d    - Delaunay triangulation by lifting points to a paraboloid\n\
    +    d Qu - furthest-site Delaunay triangulation (upper convex hull)\n\
    +    v    - Voronoi diagram as the dual of the Delaunay triangulation\n\
    +    v Qu - furthest-site Voronoi diagram\n\
    +    H1,1 - Halfspace intersection about [1,1,0,...] via polar duality\n\
    +    Qt   - triangulated output\n\
    +    QJ   - joggled input instead of merged facets\n\
    +    Tv   - verify result: structure, convexity, and point inclusion\n\
    +    .    - concise list of all options\n\
    +    -    - one-line description of each option\n\
    +    -V   - version\n\
    +\n\
    +Output options (subset):\n\
    +    s    - summary of results (default)\n\
    +    i    - vertices incident to each facet\n\
    +    n    - normals with offsets\n\
    +    p    - vertex coordinates (if 'Qc', includes coplanar points)\n\
    +           if 'v', Voronoi vertices\n\
    +    Fp   - halfspace intersections\n\
    +    Fx   - extreme points (convex hull vertices)\n\
    +    FA   - compute total area and volume\n\
    +    o    - OFF format (if 'v', outputs Voronoi regions)\n\
    +    G    - Geomview output (2-d, 3-d and 4-d)\n\
    +    m    - Mathematica output (2-d and 3-d)\n\
    +    QVn  - print facets that include point n, -n if not\n\
    +    TO file- output results to file, may be enclosed in single quotes\n\
    +\n\
    +examples:\n\
    +    rbox D4 | qhull Tv                        rbox 1000 s | qhull Tv s FA\n\
    +    rbox 10 D2 | qhull d QJ s i TO result     rbox 10 D2 | qhull v Qbb Qt p\n\
    +    rbox 10 D2 | qhull d Qu QJ m              rbox 10 D2 | qhull v Qu QJ o\n\
    +    rbox c d D2 | qhull Qc s f Fx | more      rbox c | qhull FV n | qhull H Fp\n\
    +    rbox d D12 | qhull QR0 FA                 rbox c D7 | qhull FA TF1000\n\
    +    rbox y 1000 W0 | qhull                    rbox c | qhull n\n\
    +\n\
    +";
    +/* for opts, don't assign 'e' or 'E' to a flag (already used for exponent) */
    +
    +/*---------------------------------
    +
    +  qh_prompt3
    +    concise prompt for qhull
    +*/
    +char qh_prompt3[]= "\n\
    +Qhull %s.\n\
    +Except for 'F.' and 'PG', upper-case options take an argument.\n\
    +\n\
    + delaunay       voronoi        Geomview       Halfspace      facet_dump\n\
    + incidences     mathematica    normals        OFF_format     points\n\
    + summary\n\
    +\n\
    + Farea          FArea-total    Fcoplanars     FCentrums      Fd-cdd-in\n\
    + FD-cdd-out     FF-dump-xridge Finner         FIDs           Fmerges\n\
    + Fneighbors     FNeigh-vertex  Fouter         FOptions       Fpoint-intersect\n\
    + FPoint_near    FQhull         Fsummary       FSize          Ftriangles\n\
    + Fvertices      Fvoronoi       FVertex-ave    Fxtremes       FMaple\n\
    +\n\
    + Gvertices      Gpoints        Gall_points    Gno_planes     Ginner\n\
    + Gcentrums      Ghyperplanes   Gridges        Gouter         GDrop_dim\n\
    + Gtransparent\n\
    +\n\
    + PArea-keep     Pdrop d0:0D0   Pgood          PFacet_area_keep\n\
    + PGood_neighbors PMerge-keep   Poutput_forced Pprecision_not\n\
    +\n\
    + QbBound 0:0.5  Qbk:0Bk:0_drop QbB-scale-box  Qbb-scale-last Qcoplanar\n\
    + Qfurthest      Qgood_only     QGood_point    Qinterior      Qmax_out\n\
    + QJoggle        Qrandom        QRotate        Qsearch_1st    Qtriangulate\n\
    + QupperDelaunay QVertex_good   Qvneighbors    Qxact_merge    Qzinfinite\n\
    +\n\
    + Q0_no_premerge Q1_no_angle    Q2_no_independ Q3_no_redundant Q4_no_old\n\
    + Q5_no_check_out Q6_no_concave Q7_depth_first Q8_no_near_in  Q9_pick_furthest\n\
    + Q10_no_narrow  Q11_trinormals Q12_no_wide_dup\n\
    +\n\
    + T4_trace       Tannotate      Tcheck_often   Tstatistics    Tverify\n\
    + Tz_stdout      TFacet_log     TInput_file    TPoint_trace   TMerge_trace\n\
    + TOutput_file   TRerun         TWide_trace    TVertex_stop   TCone_stop\n\
    +\n\
    + Angle_max      Centrum_size   Error_round    Random_dist    Visible_min\n\
    + Ucoplanar_max  Wide_outside\n\
    +";
    +
    +/*---------------------------------
    +
    +  main( argc, argv )
    +    processes the command line, calls qhull() to do the work, and exits
    +
    +  design:
    +    initializes data structures
    +    reads points
    +    finishes initialization
    +    computes convex hull and other structures
    +    checks the result
    +    writes the output
    +    frees memory
    +*/
    +int main(int argc, char *argv[]) {
    +  int curlong, totlong; /* used !qh_NOmem */
    +  int exitcode, numpoints, dim;
    +  coordT *points;
    +  boolT ismalloc;
    +  qhT qh_qh;
    +  qhT *qh= &qh_qh;
    +
    +  QHULL_LIB_CHECK /* Check for compatible library */
    +
    +  if ((argc == 1) && isatty( 0 /*stdin*/)) {
    +    fprintf(stdout, qh_prompt2, qh_version);
    +    exit(qh_ERRnone);
    +  }
    +  if (argc > 1 && *argv[1] == '-' && !*(argv[1]+1)) {
    +    fprintf(stdout, qh_prompta, qh_version, qh_DEFAULTbox,
    +                qh_promptb, qh_promptc, qh_promptd, qh_prompte);
    +    exit(qh_ERRnone);
    +  }
    +  if (argc > 1 && *argv[1] == '.' && !*(argv[1]+1)) {
    +    fprintf(stdout, qh_prompt3, qh_version);
    +    exit(qh_ERRnone);
    +  }
    +  if (argc > 1 && *argv[1] == '-' && *(argv[1]+1)=='V') {
    +      fprintf(stdout, "%s\n", qh_version2);
    +      exit(qh_ERRnone);
    +  }
    +  qh_init_A(qh, stdin, stdout, stderr, argc, argv);  /* sets qh->qhull_command */
    +  exitcode= setjmp(qh->errexit); /* simple statement for CRAY J916 */
    +  if (!exitcode) {
    +    qh->NOerrexit = False;
    +    qh_initflags(qh, qh->qhull_command);
    +    points= qh_readpoints(qh, &numpoints, &dim, &ismalloc);
    +    qh_init_B(qh, points, numpoints, dim, ismalloc);
    +    qh_qhull(qh);
    +    qh_check_output(qh);
    +    qh_produce_output(qh);
    +    if (qh->VERIFYoutput && !qh->FORCEoutput && !qh->STOPpoint && !qh->STOPcone)
    +      qh_check_points(qh);
    +    exitcode= qh_ERRnone;
    +  }
    +  qh->NOerrexit= True;  /* no more setjmp */
    +#ifdef qh_NOmem
    +  qh_freeqhull(qh, qh_ALL);
    +#else
    +  qh_freeqhull(qh, !qh_ALL);
    +  qh_memfreeshort(qh, &curlong, &totlong);
    +  if (curlong || totlong)
    +    qh_fprintf_stderr(6263, "qhull internal warning (main): did not free %d bytes of long memory(%d pieces)\n",
    +       totlong, curlong);
    +#endif
    +  return exitcode;
    +} /* main */
    +
    diff --git a/xs/src/qhull/src/qhulltest/Coordinates_test.cpp b/xs/src/qhull/src/qhulltest/Coordinates_test.cpp
    new file mode 100644
    index 000000000..3e8658a5b
    --- /dev/null
    +++ b/xs/src/qhull/src/qhulltest/Coordinates_test.cpp
    @@ -0,0 +1,539 @@
    +/****************************************************************************
    +**
    +** Copyright (c) 2009-2015 C.B. Barber. All rights reserved.
    +** $Id: //main/2015/qhull/src/qhulltest/Coordinates_test.cpp#2 $$Change: 2062 $
    +** $DateTime: 2016/01/17 13:13:18 $$Author: bbarber $
    +**
    +****************************************************************************/
    +
    +//pre-compiled headers
    +#include 
    +#include "qhulltest/RoadTest.h" // QT_VERSION
    +
    +#include "libqhullcpp/Coordinates.h"
    +#include "libqhullcpp/QhullError.h"
    +#include "libqhullcpp/RboxPoints.h"
    +#include "libqhullcpp/Qhull.h"
    +
    +using std::cout;
    +using std::endl;
    +using std::ostringstream;
    +using std::ostream;
    +using std::string;
    +
    +namespace orgQhull {
    +
    +class Coordinates_test : public RoadTest
    +{
    +    Q_OBJECT
    +
    +#//!\name Test slots
    +private slots:
    +    void t_construct();
    +    void t_convert();
    +    void t_element();
    +    void t_readonly();
    +    void t_operator();
    +    void t_const_iterator();
    +    void t_iterator();
    +    void t_coord_iterator();
    +    void t_mutable_coord_iterator();
    +    void t_readwrite();
    +    void t_search();
    +    void t_io();
    +};//Coordinates_test
    +
    +void
    +add_Coordinates_test()
    +{
    +    new Coordinates_test();  // RoadTest::s_testcases
    +}
    +
    +void Coordinates_test::
    +t_construct()
    +{
    +    Coordinates c;
    +    QCOMPARE(c.size(), 0U);
    +    QVERIFY(c.isEmpty());
    +    c << 1.0;
    +    QCOMPARE(c.count(), 1);
    +    Coordinates c2(c);
    +    c2 << 2.0;
    +    QCOMPARE(c2.count(), 2);
    +    Coordinates c3;
    +    c3 = c2;
    +    QCOMPARE(c3.count(), 2);
    +    QCOMPARE(c3[0]+c3[1], 3.0);
    +    QVERIFY(c2==c3);
    +    std::vector vc;
    +    vc.push_back(3.0);
    +    vc.push_back(4.0);
    +    Coordinates c4(vc);
    +    QCOMPARE(c4[0]+c4[1], 7.0);
    +    Coordinates c5(c3);
    +    QVERIFY(c5==c3);
    +    c5= vc;
    +    QVERIFY(c5!=c3);
    +    QVERIFY(c5==c4);
    +}//t_construct
    +
    +void Coordinates_test::
    +t_convert()
    +{
    +    Coordinates c;
    +    c << 1.0 << 3.0;
    +    QCOMPARE(c.data()[1], 3.0);
    +    coordT *c2= c.data();
    +    const coordT *c3= c.data();
    +    QCOMPARE(c2, c3);
    +    std::vector vc= c.toStdVector();
    +    QCOMPARE((size_t)vc.size(), c.size());
    +    for(int k= (int)vc.size(); k--; ){
    +        QCOMPARE(vc[k], c[k]);
    +    }
    +    QList qc= c.toQList();
    +    QCOMPARE(qc.count(), c.count());
    +    for(int k= qc.count(); k--; ){
    +        QCOMPARE(qc[k], c[k]);
    +    }
    +    Coordinates c4;
    +    c4= std::vector(2, 0.0);
    +    QCOMPARE(c4.back(), 0.0);
    +    Coordinates c5(std::vector(2, 0.0));
    +    QCOMPARE(c4.size(), c5.size());
    +    QVERIFY(c4==c5);
    +}//t_convert
    +
    +void Coordinates_test::
    +t_element()
    +{
    +    Coordinates c;
    +    c << 1.0 << -2.0;
    +    c.at(1)= -3;
    +    QCOMPARE(c.at(1), -3.0);
    +    QCOMPARE(c.back(), -3.0);
    +    QCOMPARE(c.front(), 1.0);
    +    c[1]= -2.0;
    +    QCOMPARE(c[1],-2.0);
    +    QCOMPARE(c.first(), 1.0);
    +    c.first()= 2.0;
    +    QCOMPARE(c.first(), 2.0);
    +    QCOMPARE(c.last(), -2.0);
    +    c.last()= 0.0;
    +    QCOMPARE(c.first()+c.last(), 2.0);
    +    coordT *c4= &c.first();
    +    const coordT *c5= &c.first();
    +    QCOMPARE(c4, c5);
    +    coordT *c6= &c.last();
    +    const coordT *c7= &c.last();
    +    QCOMPARE(c6, c7);
    +    Coordinates c2= c.mid(1);
    +    QCOMPARE(c2.count(), 1);
    +    c << 3.0;
    +    Coordinates c3= c.mid(1,1);
    +    QCOMPARE(c2, c3);
    +    QCOMPARE(c3.value(-1, -1.0), -1.0);
    +    QCOMPARE(c3.value(3, 4.0), 4.0);
    +    QCOMPARE(c.value(2, 4.0), 3.0);
    +}//t_element
    +
    +void Coordinates_test::
    +t_readonly()
    +{
    +    Coordinates c;
    +    QCOMPARE(c.size(), 0u);
    +    QCOMPARE(c.count(), 0);
    +    QVERIFY(c.isEmpty());
    +    c << 1.0 << -2.0;
    +    QCOMPARE(c.size(), 2u);
    +    QCOMPARE(c.count(), 2);
    +    QVERIFY(!c.isEmpty());
    +}//t_readonly
    +
    +void Coordinates_test::
    +t_operator()
    +{
    +    Coordinates c;
    +    Coordinates c2(c);
    +    QVERIFY(c==c2);
    +    QVERIFY(!(c!=c2));
    +    c << 1.0;
    +    QVERIFY(!(c==c2));
    +    QVERIFY(c!=c2);
    +    c2 << 1.0;
    +    QVERIFY(c==c2);
    +    QVERIFY(!(c!=c2));
    +    c[0]= 0.0;
    +    QVERIFY(c!=c2);
    +    Coordinates c3= c+c2;
    +    QCOMPARE(c3.count(), 2);
    +    QCOMPARE(c3[0], 0.0);
    +    QCOMPARE(c3[1], 1.0);
    +    c3 += c3;
    +    QCOMPARE(c3.count(), 4);
    +    QCOMPARE(c3[2], 0.0);
    +    QCOMPARE(c3[3], 1.0);
    +    c3 += c2;
    +    QCOMPARE(c3[4], 1.0);
    +    c3 += 5.0;
    +    QCOMPARE(c3.count(), 6);
    +    QCOMPARE(c3[5], 5.0);
    +    // << checked above
    +}//t_operator
    +
    +void Coordinates_test::
    +t_const_iterator()
    +{
    +    Coordinates c;
    +    QCOMPARE(c.begin(), c.end());
    +    // begin and end checked elsewhere
    +    c << 1.0 << 3.0;
    +    Coordinates::const_iterator i= c.begin();
    +    QCOMPARE(*i, 1.0);
    +    QCOMPARE(i[1], 3.0);
    +    // i[1]= -3.0; // compiler error
    +    // operator-> is not applicable to double
    +    QCOMPARE(*i++, 1.0);
    +    QCOMPARE(*i, 3.0);
    +    QCOMPARE(*i--, 3.0);
    +    QCOMPARE(*i, 1.0);
    +    QCOMPARE(*(i+1), 3.0);
    +    QCOMPARE(*++i, 3.0);
    +    QCOMPARE(*(i-1), 1.0);
    +    QCOMPARE(*--i, 1.0);
    +    QVERIFY(i==c.begin());
    +    QVERIFY(i==c.constBegin());
    +    QVERIFY(i!=c.end());
    +    QVERIFY(i!=c.constEnd());
    +    QVERIFY(i=c.begin());
    +    QVERIFY(i+1<=c.end());
    +    QVERIFY(i+1>c.begin());
    +    Coordinates::iterator i2= c.begin();
    +    Coordinates::const_iterator i3(i2);
    +    QCOMPARE(*i3, 1.0);
    +    QCOMPARE(i3[1], 3.0);
    +}//t_const_iterator
    +
    +void Coordinates_test::
    +t_iterator()
    +{
    +    Coordinates c;
    +    QCOMPARE(c.begin(), c.end());
    +    // begin and end checked elsewhere
    +    c << 1.0 << 3.0;
    +    Coordinates::iterator i= c.begin();
    +    QCOMPARE(*i, 1.0);
    +    QCOMPARE(i[1], 3.0);
    +    *i= -1.0;
    +    QCOMPARE(*i, -1.0);
    +    i[1]= -3.0;
    +    QCOMPARE(i[1], -3.0);
    +    *i= 1.0;
    +    // operator-> is not applicable to double
    +    QCOMPARE(*i++, 1.0);
    +    QCOMPARE(*i, -3.0);
    +    *i= 3.0;
    +    QCOMPARE(*i--, 3.0);
    +    QCOMPARE(*i, 1.0);
    +    QCOMPARE(*(i+1), 3.0);
    +    QCOMPARE(*++i, 3.0);
    +    QCOMPARE(*(i-1), 1.0);
    +    QCOMPARE(*--i, 1.0);
    +    QVERIFY(i==c.begin());
    +    QVERIFY(i==c.constBegin());
    +    QVERIFY(i!=c.end());
    +    QVERIFY(i!=c.constEnd());
    +    QVERIFY(i=c.begin());
    +    QVERIFY(i+1<=c.end());
    +    QVERIFY(i+1>c.begin());
    +}//t_iterator
    +
    +void Coordinates_test::
    +t_coord_iterator()
    +{
    +    Coordinates c;
    +    c << 1.0 << 3.0;
    +    CoordinatesIterator i(c);
    +    CoordinatesIterator i2= c;
    +    QVERIFY(i.findNext(1.0));
    +    QVERIFY(!i.findNext(2.0));
    +    QVERIFY(!i.findNext(3.0));
    +    QVERIFY(i.findPrevious(3.0));
    +    QVERIFY(!i.findPrevious(2.0));
    +    QVERIFY(!i.findPrevious(1.0));
    +    QVERIFY(i2.findNext(3.0));
    +    QVERIFY(i2.findPrevious(3.0));
    +    QVERIFY(i2.findNext(3.0));
    +    QVERIFY(i2.findPrevious(1.0));
    +    QVERIFY(i2.hasNext());
    +    QVERIFY(!i2.hasPrevious());
    +    QVERIFY(i.hasNext());
    +    QVERIFY(!i.hasPrevious());
    +    i.toBack();
    +    i2.toFront();
    +    QVERIFY(!i.hasNext());
    +    QVERIFY(i.hasPrevious());
    +    QVERIFY(i2.hasNext());
    +    QVERIFY(!i2.hasPrevious());
    +    Coordinates c2;
    +    i2= c2;
    +    QVERIFY(!i2.hasNext());
    +    QVERIFY(!i2.hasPrevious());
    +    i2.toBack();
    +    QVERIFY(!i2.hasNext());
    +    QVERIFY(!i2.hasPrevious());
    +    QCOMPARE(i.peekPrevious(), 3.0);
    +    QCOMPARE(i.previous(), 3.0);
    +    QCOMPARE(i.previous(), 1.0);
    +    QVERIFY(!i.hasPrevious());
    +    QCOMPARE(i.peekNext(), 1.0);
    +    // i.peekNext()= 1.0; // compiler error
    +    QCOMPARE(i.next(), 1.0);
    +    QCOMPARE(i.peekNext(), 3.0);
    +    QCOMPARE(i.next(), 3.0);
    +    QVERIFY(!i.hasNext());
    +    i.toFront();
    +    QCOMPARE(i.next(), 1.0);
    +}//t_coord_iterator
    +
    +void Coordinates_test::
    +t_mutable_coord_iterator()
    +{
    +    // Same tests as CoordinatesIterator
    +    Coordinates c;
    +    c << 1.0 << 3.0;
    +    MutableCoordinatesIterator i(c);
    +    MutableCoordinatesIterator i2= c;
    +    QVERIFY(i.findNext(1.0));
    +    QVERIFY(!i.findNext(2.0));
    +    QVERIFY(!i.findNext(3.0));
    +    QVERIFY(i.findPrevious(3.0));
    +    QVERIFY(!i.findPrevious(2.0));
    +    QVERIFY(!i.findPrevious(1.0));
    +    QVERIFY(i2.findNext(3.0));
    +    QVERIFY(i2.findPrevious(3.0));
    +    QVERIFY(i2.findNext(3.0));
    +    QVERIFY(i2.findPrevious(1.0));
    +    QVERIFY(i2.hasNext());
    +    QVERIFY(!i2.hasPrevious());
    +    QVERIFY(i.hasNext());
    +    QVERIFY(!i.hasPrevious());
    +    i.toBack();
    +    i2.toFront();
    +    QVERIFY(!i.hasNext());
    +    QVERIFY(i.hasPrevious());
    +    QVERIFY(i2.hasNext());
    +    QVERIFY(!i2.hasPrevious());
    +    Coordinates c2;
    +    i2= c2;
    +    QVERIFY(!i2.hasNext());
    +    QVERIFY(!i2.hasPrevious());
    +    i2.toBack();
    +    QVERIFY(!i2.hasNext());
    +    QVERIFY(!i2.hasPrevious());
    +    QCOMPARE(i.peekPrevious(), 3.0);
    +    QCOMPARE(i.peekPrevious(), 3.0);
    +    QCOMPARE(i.previous(), 3.0);
    +    QCOMPARE(i.previous(), 1.0);
    +    QVERIFY(!i.hasPrevious());
    +    QCOMPARE(i.peekNext(), 1.0);
    +    QCOMPARE(i.next(), 1.0);
    +    QCOMPARE(i.peekNext(), 3.0);
    +    QCOMPARE(i.next(), 3.0);
    +    QVERIFY(!i.hasNext());
    +    i.toFront();
    +    QCOMPARE(i.next(), 1.0);
    +
    +    // Mutable tests
    +    i.toFront();
    +    i.peekNext()= -1.0;
    +    QCOMPARE(i.peekNext(), -1.0);
    +    QCOMPARE((i.next()= 1.0), 1.0);
    +    QCOMPARE(i.peekPrevious(), 1.0);
    +    i.remove();
    +    QCOMPARE(c.count(), 1);
    +    i.remove();
    +    QCOMPARE(c.count(), 1);
    +    QCOMPARE(i.peekNext(), 3.0);
    +    i.insert(1.0);
    +    i.insert(2.0);
    +    QCOMPARE(c.count(), 3);
    +    QCOMPARE(i.peekNext(), 3.0);
    +    QCOMPARE(i.peekPrevious(), 2.0);
    +    i.peekPrevious()= -2.0;
    +    QCOMPARE(i.peekPrevious(), -2.0);
    +    QCOMPARE((i.previous()= 2.0), 2.0);
    +    QCOMPARE(i.peekNext(), 2.0);
    +    i.toBack();
    +    i.remove();
    +    QCOMPARE(c.count(), 3); // unchanged
    +    i.toFront();
    +    i.remove();
    +    QCOMPARE(c.count(), 3); // unchanged
    +    QCOMPARE(i.peekNext(), 1.0);
    +    i.remove();
    +    QCOMPARE(c.count(), 3); // unchanged
    +    i.insert(0.0);
    +    QCOMPARE(c.count(), 4);
    +    QCOMPARE(i.value(), 0.0);
    +    QCOMPARE(i.peekPrevious(), 0.0);
    +    i.setValue(-10.0);
    +    QCOMPARE(c.count(), 4); // unchanged
    +    QCOMPARE(i.peekNext(), 1.0);
    +    QCOMPARE(i.peekPrevious(), -10.0);
    +    i.findNext(1.0);
    +    i.setValue(-1.0);
    +    QCOMPARE(i.peekPrevious(), -1.0);
    +    i.setValue(1.0);
    +    QCOMPARE(i.peekPrevious(), 1.0);
    +    QCOMPARE(i.value(), 1.0);
    +    i.findPrevious(1.0);
    +    i.setValue(-1.0);
    +    QCOMPARE(i.peekNext(), -1.0);
    +    i.toBack();
    +    QCOMPARE(i.previous(), 3.0);
    +    i.setValue(-3.0);
    +    QCOMPARE(i.peekNext(), -3.0);
    +    double d= i.value();
    +    QCOMPARE(d, -3.0);
    +    QCOMPARE(i.previous(), 2.0);
    +}//t_mutable_coord_iterator
    +
    +void Coordinates_test::
    +t_readwrite()
    +{
    +    Coordinates c;
    +    c.clear();
    +    QCOMPARE(c.count(), 0);
    +    c << 1.0 << 3.0;
    +    c.clear();
    +    QCOMPARE(c.count(), 0);
    +    coordT c2[4]= { 0.0, 1.0, 2.0, 3.0};
    +    c.append(4, c2);
    +    QCOMPARE(c.count(), 4);
    +    QCOMPARE(c[0], 0.0);
    +    QCOMPARE(c[1], 1.0);
    +    QCOMPARE(c[3], 3.0);
    +    c.clear();
    +    c << 1.0 << 3.0;
    +    c.erase(c.begin(), c.end());
    +    QCOMPARE(c.count(), 0);
    +    c << 1.0 << 0.0;
    +    Coordinates::iterator i= c.erase(c.begin());
    +    QCOMPARE(*i, 0.0);
    +    i= c.insert(c.end(), 1.0);
    +    QCOMPARE(*i, 1.0);
    +    QCOMPARE(c.count(), 2);
    +    c.pop_back();
    +    QCOMPARE(c.count(), 1);   // 0
    +    QCOMPARE(c[0], 0.0);
    +    c.push_back(2.0);
    +    QCOMPARE(c.count(), 2);
    +    c.append(3.0);
    +    QCOMPARE(c.count(), 3);   // 0, 2, 3
    +    QCOMPARE(c[2], 3.0);
    +    c.insert(0, 4.0);
    +    QCOMPARE(c[0], 4.0);
    +    QCOMPARE(c[3], 3.0);
    +    c.insert(c.count(), 5.0);
    +    QCOMPARE(c.count(), 5);   // 4, 0, 2, 3, 5
    +    QCOMPARE(c[4], 5.0);
    +    c.move(4, 0);
    +    QCOMPARE(c.count(), 5);   // 5, 4, 0, 2, 3
    +    QCOMPARE(c[0], 5.0);
    +    c.pop_front();
    +    QCOMPARE(c.count(), 4);
    +    QCOMPARE(c[0], 4.0);
    +    c.prepend(6.0);
    +    QCOMPARE(c.count(), 5);   // 6, 4, 0, 2, 3
    +    QCOMPARE(c[0], 6.0);
    +    c.push_front(7.0);
    +    QCOMPARE(c.count(), 6);
    +    QCOMPARE(c[0], 7.0);
    +    c.removeAt(1);
    +    QCOMPARE(c.count(), 5);   // 7, 4, 0, 2, 3
    +    QCOMPARE(c[1], 4.0);
    +    c.removeFirst();
    +    QCOMPARE(c.count(), 4);   // 4, 0, 2, 3
    +    QCOMPARE(c[0], 4.0);
    +    c.removeLast();
    +    QCOMPARE(c.count(), 3);
    +    QCOMPARE(c.last(), 2.0);
    +    c.replace(2, 8.0);
    +    QCOMPARE(c.count(), 3);   // 4, 0, 8
    +    QCOMPARE(c[2], 8.0);
    +    c.swap(0, 2);
    +    QCOMPARE(c[2], 4.0);
    +    double d= c.takeAt(2);
    +    QCOMPARE(c.count(), 2);   // 8, 0
    +    QCOMPARE(d, 4.0);
    +    double d2= c.takeFirst();
    +    QCOMPARE(c.count(), 1);   // 0
    +    QCOMPARE(d2, 8.0);
    +    double d3= c.takeLast();
    +    QVERIFY(c.isEmpty()); \
    +    QCOMPARE(d3, 0.0);
    +}//t_readwrite
    +
    +void Coordinates_test::
    +t_search()
    +{
    +    Coordinates c;
    +    c << 1.0 << 3.0 << 1.0;
    +    QVERIFY(c.contains(1.0));
    +    QVERIFY(c.contains(3.0));
    +    QVERIFY(!c.contains(0.0));
    +    QCOMPARE(c.count(1.0), 2);
    +    QCOMPARE(c.count(3.0), 1);
    +    QCOMPARE(c.count(0.0), 0);
    +    QCOMPARE(c.indexOf(1.0), 0);
    +    QCOMPARE(c.indexOf(3.0), 1);
    +    QCOMPARE(c.indexOf(1.0, -1), 2);
    +    QCOMPARE(c.indexOf(3.0, -1), -1);
    +    QCOMPARE(c.indexOf(3.0, -2), 1);
    +    QCOMPARE(c.indexOf(1.0, -3), 0);
    +    QCOMPARE(c.indexOf(1.0, -4), 0);
    +    QCOMPARE(c.indexOf(1.0, 1), 2);
    +    QCOMPARE(c.indexOf(3.0, 2), -1);
    +    QCOMPARE(c.indexOf(1.0, 2), 2);
    +    QCOMPARE(c.indexOf(1.0, 3), -1);
    +    QCOMPARE(c.indexOf(1.0, 4), -1);
    +    QCOMPARE(c.lastIndexOf(1.0), 2);
    +    QCOMPARE(c.lastIndexOf(3.0), 1);
    +    QCOMPARE(c.lastIndexOf(1.0, -1), 2);
    +    QCOMPARE(c.lastIndexOf(3.0, -1), 1);
    +    QCOMPARE(c.lastIndexOf(3.0, -2), 1);
    +    QCOMPARE(c.lastIndexOf(1.0, -3), 0);
    +    QCOMPARE(c.lastIndexOf(1.0, -4), -1);
    +    QCOMPARE(c.lastIndexOf(1.0, 1), 0);
    +    QCOMPARE(c.lastIndexOf(3.0, 2), 1);
    +    QCOMPARE(c.lastIndexOf(1.0, 2), 2);
    +    QCOMPARE(c.lastIndexOf(1.0, 3), 2);
    +    QCOMPARE(c.lastIndexOf(1.0, 4), 2);
    +    c.removeAll(3.0);
    +    QCOMPARE(c.count(), 2);
    +    c.removeAll(4.0);
    +    QCOMPARE(c.count(), 2);
    +    c.removeAll(1.0);
    +    QCOMPARE(c.count(), 0);
    +    c.removeAll(4.0);
    +    QCOMPARE(c.count(), 0);
    +}//t_search
    +
    +void Coordinates_test::
    +t_io()
    +{
    +    Coordinates c;
    +    c << 1.0 << 2.0 << 3.0;
    +    ostringstream os;
    +    os << "Coordinates 1-2-3\n" << c;
    +    cout << os.str();
    +    QString s= QString::fromStdString(os.str());
    +    QCOMPARE(s.count("2"), 2);
    +}//t_io
    +
    +}//orgQhull
    +
    +#include "moc/Coordinates_test.moc"
    diff --git a/xs/src/qhull/src/qhulltest/PointCoordinates_test.cpp b/xs/src/qhull/src/qhulltest/PointCoordinates_test.cpp
    new file mode 100644
    index 000000000..09285954d
    --- /dev/null
    +++ b/xs/src/qhull/src/qhulltest/PointCoordinates_test.cpp
    @@ -0,0 +1,478 @@
    +/****************************************************************************
    +**
    +** Copyright (c) 2009-2015 C.B. Barber. All rights reserved.
    +** $Id: //main/2015/qhull/src/qhulltest/PointCoordinates_test.cpp#2 $$Change: 2062 $
    +** $DateTime: 2016/01/17 13:13:18 $$Author: bbarber $
    +**
    +****************************************************************************/
    +
    +//pre-compiled headers
    +#include 
    +#include "qhulltest/RoadTest.h" // QT_VERSION
    +
    +#include "libqhullcpp/PointCoordinates.h"
    +#include "libqhullcpp/QhullError.h"
    +#include "libqhullcpp/RboxPoints.h"
    +#include "libqhullcpp/Qhull.h"
    +
    +using std::cout;
    +using std::endl;
    +using std::ostringstream;
    +using std::ostream;
    +using std::string;
    +using std::stringstream;
    +
    +namespace orgQhull {
    +
    +class PointCoordinates_test : public RoadTest
    +{
    +    Q_OBJECT
    +
    +#//!\name Test slots
    +private slots:
    +    void t_construct_q();
    +    void t_construct_qh();
    +    void t_convert();
    +    void t_getset();
    +    void t_element();
    +    void t_foreach();
    +    void t_search();
    +    void t_modify();
    +    void t_append_points();
    +    void t_coord_iterator();
    +    void t_io();
    +};//PointCoordinates_test
    +
    +void
    +add_PointCoordinates_test()
    +{
    +    new PointCoordinates_test();  // RoadTest::s_testcases
    +}
    +
    +void PointCoordinates_test::
    +t_construct_q()
    +{
    +    Qhull q;
    +    PointCoordinates pc(q);
    +    QCOMPARE(pc.size(), 0U);
    +    QCOMPARE(pc.coordinateCount(), 0);
    +    QCOMPARE(pc.dimension(), 0);
    +    QCOMPARE(pc.coordinates(), (coordT *)0);
    +    QVERIFY(pc.isEmpty());
    +    pc.checkValid();
    +    PointCoordinates pc7(q, 2, "test explicit dimension");
    +    QCOMPARE(pc7.dimension(), 2);
    +    QCOMPARE(pc7.count(), 0);
    +    QVERIFY(pc7.isEmpty());
    +    QCOMPARE(pc7.comment(), std::string("test explicit dimension"));
    +    pc7.checkValid();
    +    PointCoordinates pc2(q, "Test pc2");
    +    QCOMPARE(pc2.count(), 0);
    +    QVERIFY(pc2.isEmpty());
    +    QCOMPARE(pc2.comment(), std::string("Test pc2"));
    +    pc2.checkValid();
    +    PointCoordinates pc3(q, 3, "Test 3-d pc3");
    +    QCOMPARE(pc3.dimension(), 3);
    +    QVERIFY(pc3.isEmpty());
    +    pc3.checkValid();
    +    coordT c[]= { 0.0, 1.0, 2.0, 3.0, 4.0, 5.0 };
    +    PointCoordinates pc4(q, 2, "Test 2-d pc4", 6, c);
    +    QCOMPARE(pc4.dimension(), 2);
    +    QCOMPARE(pc4.count(), 3);
    +    QCOMPARE(pc4.size(), 3u);
    +    QVERIFY(!pc4.isEmpty());
    +    pc4.checkValid();
    +    QhullPoint p= pc4[2];
    +    QCOMPARE(p[1], 5.0);
    +    // QhullPoint refers to PointCoordinates
    +    p[1] += 1.0;
    +    QCOMPARE(pc4[2][1], 6.0);
    +    PointCoordinates pc5(q, 4, "Test 4-d pc5 with insufficient coordinates", 6, c);
    +    QCOMPARE(pc5.dimension(), 4);
    +    QCOMPARE(pc5.count(), 1);
    +    QCOMPARE(pc5.extraCoordinatesCount(), 2);
    +    QCOMPARE(pc5.extraCoordinates()[1], 5.0);
    +    QVERIFY(!pc5.isEmpty());;
    +    std::vector vc;
    +    vc.push_back(3.0);
    +    vc.push_back(4.0);
    +    vc.push_back(5.0);
    +    vc.push_back(6.0);
    +    vc.push_back(7.0);
    +    vc.push_back(9.0);
    +    pc5.append(2, &vc[3]); // Copy of vc[]
    +    pc5.checkValid();
    +    QhullPoint p5(q, 4, &vc[1]);
    +    QCOMPARE(pc5[1], p5);
    +    PointCoordinates pc6(pc5); // Makes copy of point_coordinates
    +    QCOMPARE(pc6[1], p5);
    +    QVERIFY(pc6==pc5);
    +    QhullPoint p6= pc5[1];  // Refers to pc5.coordinates
    +    pc5[1][0] += 1.0;
    +    QCOMPARE(pc5[1], p6);
    +    QVERIFY(pc5[1]!=p5);
    +    QVERIFY(pc6!=pc5);
    +    pc6= pc5;
    +    QVERIFY(pc6==pc5);
    +    PointCoordinates pc8(q);
    +    pc6= pc8;
    +    QVERIFY(pc6!=pc5);
    +    QVERIFY(pc6.isEmpty());
    +}//t_construct_q
    +
    +void PointCoordinates_test::
    +t_construct_qh()
    +{
    +    QhullQh qh;
    +    PointCoordinates pc(&qh);
    +    QCOMPARE(pc.size(), 0U);
    +    QCOMPARE(pc.coordinateCount(), 0);
    +    QCOMPARE(pc.dimension(), 0);
    +    QCOMPARE(pc.coordinates(), (coordT *)0);
    +    QVERIFY(pc.isEmpty());
    +    pc.checkValid();
    +    PointCoordinates pc7(&qh, 2, "test explicit dimension");
    +    QCOMPARE(pc7.dimension(), 2);
    +    QCOMPARE(pc7.count(), 0);
    +    QVERIFY(pc7.isEmpty());
    +    QCOMPARE(pc7.comment(), std::string("test explicit dimension"));
    +    pc7.checkValid();
    +    PointCoordinates pc2(&qh, "Test pc2");
    +    QCOMPARE(pc2.count(), 0);
    +    QVERIFY(pc2.isEmpty());
    +    QCOMPARE(pc2.comment(), std::string("Test pc2"));
    +    pc2.checkValid();
    +    PointCoordinates pc3(&qh, 3, "Test 3-d pc3");
    +    QCOMPARE(pc3.dimension(), 3);
    +    QVERIFY(pc3.isEmpty());
    +    pc3.checkValid();
    +    coordT c[]= { 0.0, 1.0, 2.0, 3.0, 4.0, 5.0 };
    +    PointCoordinates pc4(&qh, 2, "Test 2-d pc4", 6, c);
    +    QCOMPARE(pc4.dimension(), 2);
    +    QCOMPARE(pc4.count(), 3);
    +    QCOMPARE(pc4.size(), 3u);
    +    QVERIFY(!pc4.isEmpty());
    +    pc4.checkValid();
    +    QhullPoint p= pc4[2];
    +    QCOMPARE(p[1], 5.0);
    +    // QhullPoint refers to PointCoordinates
    +    p[1] += 1.0;
    +    QCOMPARE(pc4[2][1], 6.0);
    +    PointCoordinates pc5(&qh, 4, "Test 4-d pc5 with insufficient coordinates", 6, c);
    +    QCOMPARE(pc5.dimension(), 4);
    +    QCOMPARE(pc5.count(), 1);
    +    QCOMPARE(pc5.extraCoordinatesCount(), 2);
    +    QCOMPARE(pc5.extraCoordinates()[1], 5.0);
    +    QVERIFY(!pc5.isEmpty());;
    +    std::vector vc;
    +    vc.push_back(3.0);
    +    vc.push_back(4.0);
    +    vc.push_back(5.0);
    +    vc.push_back(6.0);
    +    vc.push_back(7.0);
    +    vc.push_back(9.0);
    +    pc5.append(2, &vc[3]); // Copy of vc[]
    +    pc5.checkValid();
    +    QhullPoint p5(&qh, 4, &vc[1]);
    +    QCOMPARE(pc5[1], p5);
    +    PointCoordinates pc6(pc5); // Makes copy of point_coordinates
    +    QCOMPARE(pc6[1], p5);
    +    QVERIFY(pc6==pc5);
    +    QhullPoint p6= pc5[1];  // Refers to pc5.coordinates
    +    pc5[1][0] += 1.0;
    +    QCOMPARE(pc5[1], p6);
    +    QVERIFY(pc5[1]!=p5);
    +    QVERIFY(pc6!=pc5);
    +    pc6= pc5;
    +    QVERIFY(pc6==pc5);
    +    PointCoordinates pc8(&qh);
    +    pc6= pc8;
    +    QVERIFY(pc6!=pc5);
    +    QVERIFY(pc6.isEmpty());
    +}//t_construct_qh
    +
    +void PointCoordinates_test::
    +t_convert()
    +{
    +    Qhull q;
    +    //defineAs tested above
    +    coordT c[]= {0.0, 1.0, 2.0, 3.0, 4.0, 5.0};
    +    PointCoordinates ps(q, 3, "two 3-d points", 6, c);
    +    QCOMPARE(ps.dimension(), 3);
    +    QCOMPARE(ps.size(), 2u);
    +    const coordT *c2= ps.constData();
    +    QVERIFY(c!=c2);
    +    QCOMPARE(c[0], c2[0]);
    +    const coordT *c3= ps.data();
    +    QCOMPARE(c3, c2);
    +    coordT *c4= ps.data();
    +    QCOMPARE(c4, c2);
    +    std::vector vs= ps.toStdVector();
    +    QCOMPARE(vs.size(), 6u);
    +    QCOMPARE(vs[5], 5.0);
    +    QList qs= ps.toQList();
    +    QCOMPARE(qs.size(), 6);
    +    QCOMPARE(qs[5], 5.0);
    +}//t_convert
    +
    +void PointCoordinates_test::
    +t_getset()
    +{
    +    // See t_construct() for test of coordinates, coordinateCount, dimension, empty, isEmpty, ==, !=
    +    // See t_construct() for test of checkValid, comment, setDimension
    +    Qhull q;
    +    PointCoordinates pc(q, "Coordinates c");
    +    pc.setComment("New comment");
    +    QCOMPARE(pc.comment(), std::string("New comment"));
    +    pc.checkValid();
    +    pc.makeValid();  // A no-op
    +    pc.checkValid();
    +    Coordinates cs= pc.getCoordinates();
    +    QVERIFY(cs.isEmpty());
    +    PointCoordinates pc2(pc);
    +    pc.setDimension(3);
    +    QVERIFY(pc2!=pc);
    +    coordT c[]= {0.0, 1.0, 2.0, 3.0, 4.0, 5.0};
    +    pc.append(6, c);
    +    pc.checkValid();
    +    pc.makeValid();  // A no-op
    +    QhullPoint p= pc[0];
    +    QCOMPARE(p[2], 2.0);
    +    try{
    +        pc.setDimension(2);
    +        QFAIL("setDimension(2) did not fail for 3-d.");
    +    }catch (const std::exception &e) {
    +        const char *s= e.what();
    +        cout << "INFO   : Caught " << s;
    +    }
    +}//t_getset
    +
    +void PointCoordinates_test::
    +t_element()
    +{
    +    Qhull q;
    +    coordT c[]= {0.0, 1.0, 2.0, 3.0, 4.0, 5.0};
    +    PointCoordinates pc(q, 2, "2-d points", 6, c);
    +    QhullPoint p= pc.at(0);
    +    QCOMPARE(p, pc[0]);
    +    QCOMPARE(p, pc.first());
    +    QCOMPARE(p, pc.value(0));
    +    p= pc.back();
    +    QCOMPARE(p, pc[2]);
    +    QCOMPARE(p, pc.last());
    +    QCOMPARE(p, pc.value(2));
    +    QhullPoints ps= pc.mid(1, 2);
    +    QCOMPARE(ps[1], p);
    +}//t_element
    +
    +void PointCoordinates_test::
    +t_foreach()
    +{
    +    Qhull q;
    +    coordT c[]= {0.0, 1.0, 2.0, 3.0, 4.0, 5.0};
    +    PointCoordinates pc(q, 2, "2-d points", 6, c);
    +    QhullPoints::Iterator i= pc.begin();
    +    QhullPoint p= pc[0];
    +    QCOMPARE(*i, p);
    +    QCOMPARE((*i)[0], 0.0);
    +    QhullPoint p3= pc[2];
    +    i= pc.end();
    +    QCOMPARE(i[-1], p3);
    +    const PointCoordinates pc2(q, 2, "2-d points", 6, c);
    +    QhullPoints::ConstIterator i2= pc.begin();
    +    const QhullPoint p0= pc2[0];
    +    QCOMPARE(*i2, p0);
    +    QCOMPARE((*i2)[0], 0.0);
    +    QhullPoints::ConstIterator i3= i2;
    +    QCOMPARE(i3, i2);
    +    QCOMPARE((*i3)[0], 0.0);
    +    i3= pc.constEnd();
    +    --i3;
    +    QhullPoint p2= pc2[2];
    +    QCOMPARE(*i3, p2);
    +    i= pc.end();
    +    QVERIFY(i-1==i3);
    +    i2= pc2.end();
    +    QVERIFY(i2-1!=i3);
    +    QCOMPARE(*(i2-1), *i3);
    +    foreach(QhullPoint p3, pc){ //Qt only
    +        QVERIFY(p3[0]>=0.0);
    +        QVERIFY(p3[0]<=5.0);
    +    }
    +    Coordinates::ConstIterator i4= pc.beginCoordinates();
    +    QCOMPARE(*i4, 0.0);
    +    Coordinates::Iterator i5= pc.beginCoordinates();
    +    QCOMPARE(*i5, 0.0);
    +    i4= pc.beginCoordinates(1);
    +    QCOMPARE(*i4, 2.0);
    +    i5= pc.beginCoordinates(1);
    +    QCOMPARE(*i5, 2.0);
    +    i4= pc.endCoordinates();
    +    QCOMPARE(*--i4, 5.0);
    +    i5= pc.endCoordinates();
    +    QCOMPARE(*--i5, 5.0);
    +}//t_foreach
    +
    +void PointCoordinates_test::
    +t_search()
    +{
    +    Qhull q;
    +    coordT c[]= {0.0, 1.0, 2.0, 3.0, 4.0, 5.0};
    +    PointCoordinates pc(q, 2, "2-d points", 6, c);
    +    QhullPoint p0= pc[0];
    +    QhullPoint p2= pc[2];
    +    QVERIFY(pc.contains(p0));
    +    QVERIFY(pc.contains(p2));
    +    QCOMPARE(pc.count(p0), 1);
    +    QCOMPARE(pc.indexOf(p2), 2);
    +    QCOMPARE(pc.lastIndexOf(p0), 0);
    +}//t_search
    +
    +void PointCoordinates_test::
    +t_modify()
    +{
    +    Qhull q;
    +    coordT c[]= {0.0, 1.0, 2.0, 3.0, 4.0, 5.0};
    +    PointCoordinates pc(q, 2, "2-d points", 6, c);
    +    coordT c3[]= {0.0, 1.0, 2.0, 3.0, 4.0, 5.0};
    +    PointCoordinates pc5(q, 2, "test explicit dimension");
    +    pc5.append(6, c3); // 0-5
    +    QVERIFY(pc5==pc);
    +    PointCoordinates pc2(q, 2, "2-d");
    +    coordT c2[]= {6.0, 7.0, 8.0, 9.0, 10.0, 11.0};
    +    pc2.append(6, c2);
    +    QCOMPARE(pc2.count(), 3);
    +    pc2.append(14.0);
    +    QCOMPARE(pc2.count(), 3);
    +    QCOMPARE(pc2.extraCoordinatesCount(), 1);
    +    pc2.append(15.0); // 6-11, 14,15
    +    QCOMPARE(pc2.count(), 4);
    +    QCOMPARE(pc2.extraCoordinatesCount(), 0);
    +    QhullPoint p(pc[0]);
    +    pc2.append(p); // 6-11, 14,15, 0,1
    +    QCOMPARE(pc2.count(), 5);
    +    QCOMPARE(pc2.extraCoordinatesCount(), 0);
    +    QCOMPARE(pc2.lastIndexOf(p), 4);
    +    pc.append(pc2); // Invalidates p
    +    QCOMPARE(pc.count(), 8); // 0-11, 14,15, 0,1
    +    QCOMPARE(pc.extraCoordinatesCount(), 0);
    +    QCOMPARE(pc.lastIndexOf(pc[0]), 7);
    +    pc.appendComment(" operators");
    +    QCOMPARE(pc.comment(), std::string("2-d points operators"));
    +    pc.checkValid();
    +    // see t_append_points for appendPoints
    +    PointCoordinates pc3= pc+pc2;
    +    pc3.checkValid();
    +    QCOMPARE(pc3.count(), 13);
    +    QCOMPARE(pc3[6][0], 14.0);
    +    QCOMPARE(pc3[8][0], 6.0);
    +    pc3 += pc;
    +    QCOMPARE(pc3.count(), 21);
    +    QCOMPARE(pc3[14][0], 2.0);
    +    pc3 += 12.0;
    +    pc3 += 14.0;
    +    QCOMPARE(pc3.count(), 22);
    +    QCOMPARE(pc3.last()[0], 12.0);
    +    // QhullPoint p3= pc3.first(); // += throws error because append may move the data
    +    QhullPoint p3= pc2.first();
    +    pc3 += p3;
    +    QCOMPARE(pc3.count(), 23);
    +    QCOMPARE(pc3.last()[0], 6.0);
    +    pc3 << pc;
    +    QCOMPARE(pc3.count(), 31);
    +    QCOMPARE(pc3.last()[0], 0.0);
    +    pc3 << 12.0 << 14.0;
    +    QCOMPARE(pc3.count(), 32);
    +    QCOMPARE(pc3.last()[0], 12.0);
    +    PointCoordinates pc4(pc3);
    +    pc4.reserveCoordinates(100);
    +    QVERIFY(pc3==pc4);
    +}//t_modify
    +
    +void PointCoordinates_test::
    +t_append_points()
    +{
    +    Qhull q;
    +    PointCoordinates pc(q, 2, "stringstream");
    +    stringstream s("2 3 1 2 3 4 5 6");
    +    pc.appendPoints(s);
    +    QCOMPARE(pc.count(), 3);
    +}//t_append_points
    +
    +void PointCoordinates_test::
    +t_coord_iterator()
    +{
    +    Qhull q;
    +    PointCoordinates c(q, 2, "2-d");
    +    c << 0.0 << 1.0 << 2.0 << 3.0 << 4.0 << 5.0;
    +    PointCoordinatesIterator i(c);
    +    QhullPoint p0(c[0]);
    +    QhullPoint p1(c[1]);
    +    QhullPoint p2(c[2]);
    +    coordT c2[] = {-1.0, -2.0};
    +    QhullPoint p3(q, 2, c2);
    +    PointCoordinatesIterator i2= c;
    +    QVERIFY(i.findNext(p1));
    +    QVERIFY(!i.findNext(p1));
    +    QVERIFY(!i.findNext(p2));
    +    QVERIFY(!i.findNext(p3));
    +    QVERIFY(i.findPrevious(p2));
    +    QVERIFY(!i.findPrevious(p2));
    +    QVERIFY(!i.findPrevious(p0));
    +    QVERIFY(!i.findPrevious(p3));
    +    QVERIFY(i2.findNext(p2));
    +    QVERIFY(i2.findPrevious(p0));
    +    QVERIFY(i2.findNext(p1));
    +    QVERIFY(i2.findPrevious(p0));
    +    QVERIFY(i2.hasNext());
    +    QVERIFY(!i2.hasPrevious());
    +    QVERIFY(i.hasNext());
    +    QVERIFY(!i.hasPrevious());
    +    i.toBack();
    +    i2.toFront();
    +    QVERIFY(!i.hasNext());
    +    QVERIFY(i.hasPrevious());
    +    QVERIFY(i2.hasNext());
    +    QVERIFY(!i2.hasPrevious());
    +    PointCoordinates c3(q);
    +    PointCoordinatesIterator i3= c3;
    +    QVERIFY(!i3.hasNext());
    +    QVERIFY(!i3.hasPrevious());
    +    i3.toBack();
    +    QVERIFY(!i3.hasNext());
    +    QVERIFY(!i3.hasPrevious());
    +    QCOMPARE(i.peekPrevious(), p2);
    +    QCOMPARE(i.previous(), p2);
    +    QCOMPARE(i.previous(), p1);
    +    QCOMPARE(i.previous(), p0);
    +    QVERIFY(!i.hasPrevious());
    +    QCOMPARE(i.peekNext(), p0);
    +    // i.peekNext()= 1.0; // compiler error
    +    QCOMPARE(i.next(), p0);
    +    QCOMPARE(i.peekNext(), p1);
    +    QCOMPARE(i.next(), p1);
    +    QCOMPARE(i.next(), p2);
    +    QVERIFY(!i.hasNext());
    +    i.toFront();
    +    QCOMPARE(i.next(), p0);
    +}//t_coord_iterator
    +
    +void PointCoordinates_test::
    +t_io()
    +{
    +    Qhull q;
    +    PointCoordinates c(q);
    +    ostringstream os;
    +    os << "PointCoordinates 0-d\n" << c;
    +    c.setDimension(2);
    +    c << 1.0 << 2.0 << 3.0 << 1.0 << 2.0 << 3.0;
    +    os << "PointCoordinates 1,2 3,1 2,3\n" << c;
    +    cout << os.str();
    +    QString s= QString::fromStdString(os.str());
    +    QCOMPARE(s.count("0"), 3);
    +    QCOMPARE(s.count("2"), 5);
    +}//t_io
    +
    +}//orgQhull
    +
    +#include "moc/PointCoordinates_test.moc"
    diff --git a/xs/src/qhull/src/qhulltest/QhullFacetList_test.cpp b/xs/src/qhull/src/qhulltest/QhullFacetList_test.cpp
    new file mode 100644
    index 000000000..5a09d01da
    --- /dev/null
    +++ b/xs/src/qhull/src/qhulltest/QhullFacetList_test.cpp
    @@ -0,0 +1,196 @@
    +/****************************************************************************
    +**
    +** Copyright (c) 2008-2015 C.B. Barber. All rights reserved.
    +** $Id: //main/2015/qhull/src/qhulltest/QhullFacetList_test.cpp#3 $$Change: 2062 $
    +** $DateTime: 2016/01/17 13:13:18 $$Author: bbarber $
    +**
    +****************************************************************************/
    +
    +//pre-compiled headers
    +#include 
    +#include "qhulltest/RoadTest.h" // QT_VERSION
    +
    +#include "libqhullcpp/QhullFacetList.h"
    +#include "libqhullcpp/QhullError.h"
    +#include "libqhullcpp/QhullFacet.h"
    +#include "libqhullcpp/QhullVertex.h"
    +#include "libqhullcpp/QhullVertexSet.h"
    +#include "libqhullcpp/Qhull.h"
    +#include "libqhullcpp/RboxPoints.h"
    +
    +using std::cout;
    +using std::endl;
    +using std::ostringstream;
    +using std::ostream;
    +using std::string;
    +
    +namespace orgQhull {
    +
    +class QhullFacetList_test : public RoadTest
    +{
    +    Q_OBJECT
    +
    +#//!\name Test slots
    +private slots:
    +    void cleanup();
    +    void t_construct_qh();
    +    void t_construct_q();
    +    void t_convert();
    +    void t_readonly();
    +    void t_foreach();
    +    void t_io();
    +};//QhullFacetList_test
    +
    +void
    +add_QhullFacetList_test()
    +{
    +    new QhullFacetList_test();  // RoadTest::s_testcases
    +}
    +
    +//Executed after each testcase
    +void QhullFacetList_test::
    +cleanup()
    +{
    +    RoadTest::cleanup();
    +}
    +
    +void QhullFacetList_test::
    +t_construct_qh()
    +{
    +    RboxPoints rcube("c");
    +    Qhull q(rcube,"QR0");  // rotated unit cube
    +    QhullFacetList fs2= q.facetList();
    +    QVERIFY(!fs2.isEmpty());
    +    QCOMPARE(fs2.count(),6);
    +    QhullFacetList fs3(q.endFacet(), q.endFacet());
    +    QVERIFY(fs3.isEmpty());
    +    QhullFacetList fs4(q.endFacet().previous(), q.endFacet());
    +    QCOMPARE(fs4.count(), 1);
    +    QhullFacetList fs5(q.beginFacet(), q.endFacet());
    +    QCOMPARE(fs2.count(), fs5.count());
    +    QVERIFY(fs2==fs5);
    +    QhullFacetList fs6= fs2; // copy constructor
    +    QVERIFY(fs6==fs2);
    +    std::vector fv= fs2.toStdVector();
    +    QCOMPARE(fv.size(), 6u);
    +}//t_construct_qh
    +
    +void QhullFacetList_test::
    +t_construct_q()
    +{
    +    RboxPoints rcube("c");
    +    Qhull q(rcube,"QR0");  // rotated unit cube
    +    QhullFacetList fs2= q.facetList();
    +    QVERIFY(!fs2.isEmpty());
    +    QCOMPARE(fs2.count(),6);
    +    QhullFacetList fs3(q.endFacet(), q.endFacet());
    +    QVERIFY(fs3.isEmpty());
    +    QhullFacetList fs4(q.endFacet().previous(), q.endFacet());
    +    QCOMPARE(fs4.count(), 1);
    +    QhullFacetList fs5(q.beginFacet(), q.endFacet());
    +    QCOMPARE(fs2.count(), fs5.count());
    +    QVERIFY(fs2==fs5);
    +    QhullFacetList fs6= fs2; // copy constructor
    +    QVERIFY(fs6==fs2);
    +    std::vector fv= fs2.toStdVector();
    +    QCOMPARE(fv.size(), 6u);
    +}//t_construct_q
    +
    +void QhullFacetList_test::
    +t_convert()
    +{
    +    RboxPoints rcube("c");
    +    Qhull q(rcube,"QR0 QV2");  // rotated unit cube
    +    QhullFacetList fs2= q.facetList();
    +    QVERIFY(!fs2.isSelectAll());
    +    QVERIFY(!fs2.isEmpty());
    +    QCOMPARE(fs2.count(),3);
    +    std::vector fv= fs2.toStdVector();
    +    QCOMPARE(fv.size(), 3u);
    +    QList fv2= fs2.toQList();
    +    QCOMPARE(fv2.size(), 3);
    +    std::vector fv5= fs2.vertices_toStdVector();
    +    QCOMPARE(fv5.size(), 7u);
    +    QList fv6= fs2.vertices_toQList();
    +    QCOMPARE(fv6.size(), 7);
    +    fs2.selectAll();
    +    QVERIFY(fs2.isSelectAll());
    +    std::vector fv3= fs2.toStdVector();
    +    QCOMPARE(fv3.size(), 6u);
    +    QList fv4= fs2.toQList();
    +    QCOMPARE(fv4.size(), 6);
    +    std::vector fv7= fs2.vertices_toStdVector();
    +    QCOMPARE(fv7.size(), 8u);
    +    QList fv8= fs2.vertices_toQList();
    +    QCOMPARE(fv8.size(), 8);
    +}//t_convert
    +
    +//! Spot check properties and read-only.  See QhullLinkedList_test
    +void QhullFacetList_test::
    +t_readonly()
    +{
    +    RboxPoints rcube("c");
    +    Qhull q(rcube,"QV0");  // good facets are adjacent to point 0
    +    QhullFacetList fs= q.facetList();
    +    QVERIFY(!fs.isSelectAll());
    +    QCOMPARE(fs.count(), 3);
    +    QCOMPARE(fs.first(), q.firstFacet());
    +    fs.selectAll();
    +    QVERIFY(fs.isSelectAll());
    +    QCOMPARE(fs.count(), 6);
    +    fs.selectGood();
    +    QVERIFY(!fs.isSelectAll());
    +    QCOMPARE(fs.count(), 3);
    +    fs.selectAll();
    +    QVERIFY(fs.isSelectAll());
    +    QCOMPARE(fs.count(), 6);
    +    QhullFacet f= fs.first();
    +    QhullFacet f2= fs.last();
    +    fs.selectAll();
    +    QVERIFY(fs.contains(f));
    +    QVERIFY(fs.contains(f2));
    +    QVERIFY(f.isGood());
    +    QVERIFY(!f2.isGood());
    +    fs.selectGood();
    +    QVERIFY(fs.contains(f));
    +    QVERIFY(!fs.contains(f2));
    +}//t_readonly
    +
    +void QhullFacetList_test::
    +t_foreach()
    +{
    +    RboxPoints rcube("c");
    +    // Spot check predicates and accessors.  See QhullLinkedList_test
    +    Qhull q(rcube,"Qt QR0");  // triangulation of rotated unit cube
    +    QhullFacetList fs= q.facetList();
    +    QVERIFY(fs.contains(q.firstFacet()));
    +    QhullFacet f= q.firstFacet().next();
    +    QVERIFY(fs.contains(f));
    +    QCOMPARE(fs.first(), *fs.begin());
    +    QCOMPARE(*(fs.end()-1), fs.last());
    +    QCOMPARE(fs.first(), q.firstFacet());
    +    QCOMPARE(*fs.begin(), q.beginFacet());
    +    QCOMPARE(*fs.end(), q.endFacet());
    +}//t_foreach
    +
    +void QhullFacetList_test::
    +t_io()
    +{
    +    RboxPoints rcube("c");
    +    {
    +        Qhull q(rcube,"QR0 QV0");   // good facets are adjacent to point 0
    +        QhullFacetList fs= q.facetList();
    +        ostringstream os;
    +        os << fs.print("Show all of FacetList\n");
    +        os << "\nFacets only\n" << fs;
    +        os << "\nVertices only\n" << fs.printVertices();
    +        cout << os.str();
    +        QString facets= QString::fromStdString(os.str());
    +        QCOMPARE(facets.count("(v"), 2*7+12*3*2);
    +        QCOMPARE(facets.count(QRegExp("f\\d")), 2*3*7 + 13*3*2);
    +    }
    +}//t_io
    +
    +}//orgQhull
    +
    +#include "moc/QhullFacetList_test.moc"
    diff --git a/xs/src/qhull/src/qhulltest/QhullFacetSet_test.cpp b/xs/src/qhull/src/qhulltest/QhullFacetSet_test.cpp
    new file mode 100644
    index 000000000..a7fe123a2
    --- /dev/null
    +++ b/xs/src/qhull/src/qhulltest/QhullFacetSet_test.cpp
    @@ -0,0 +1,153 @@
    +/****************************************************************************
    +**
    +** Copyright (c) 2008-2015 C.B. Barber. All rights reserved.
    +** $Id: //main/2015/qhull/src/qhulltest/QhullFacetSet_test.cpp#3 $$Change: 2062 $
    +** $DateTime: 2016/01/17 13:13:18 $$Author: bbarber $
    +**
    +****************************************************************************/
    +
    +//pre-compiled headers
    +#include 
    +#include "qhulltest/RoadTest.h" // QT_VERSION
    +
    +#include "libqhullcpp/QhullFacetSet.h"
    +#include "libqhullcpp/QhullError.h"
    +#include "libqhullcpp/QhullFacet.h"
    +#include "libqhullcpp/Qhull.h"
    +#include "libqhullcpp/RboxPoints.h"
    +
    +using std::cout;
    +using std::endl;
    +using std::ostringstream;
    +using std::ostream;
    +using std::string;
    +
    +namespace orgQhull {
    +
    +class QhullFacetSet_test : public RoadTest
    +{
    +    Q_OBJECT
    +
    +#//!\name Test slots
    +private slots:
    +    void cleanup();
    +    void t_construct();
    +    void t_convert();
    +    void t_readonly();
    +    void t_foreach();
    +    void t_io();
    +};//QhullFacetSet_test
    +
    +void
    +add_QhullFacetSet_test()
    +{
    +    new QhullFacetSet_test();  // RoadTest::s_testcases
    +}
    +
    +//Executed after each testcase
    +void QhullFacetSet_test::
    +cleanup()
    +{
    +    RoadTest::cleanup();
    +}
    +
    +void QhullFacetSet_test::
    +t_construct()
    +{
    +    RboxPoints rcube("c");
    +    Qhull q(rcube,"QR0");  // rotated unit cube
    +    QhullFacet f= q.firstFacet();
    +    QhullFacetSet fs2= f.neighborFacets();
    +    QVERIFY(!fs2.isEmpty());
    +    QCOMPARE(fs2.count(),4);
    +    QhullFacetSet fs4= fs2; // copy constructor
    +    QVERIFY(fs4==fs2);
    +    QhullFacetSet fs3(q, q.qh()->facet_mergeset);
    +    QVERIFY(fs3.isEmpty());
    +}//t_construct
    +
    +void QhullFacetSet_test::
    +t_convert()
    +{
    +    RboxPoints rcube("c");
    +    Qhull q2(rcube,"QR0 QV2");  // rotated unit cube
    +    QhullFacet f2= q2.firstFacet();
    +    QhullFacetSet fs2= f2.neighborFacets();
    +    QVERIFY(!fs2.isSelectAll());
    +    QCOMPARE(fs2.count(),2);
    +    std::vector fv= fs2.toStdVector();
    +    QCOMPARE(fv.size(), 2u);
    +    QList fv2= fs2.toQList();
    +    QCOMPARE(fv2.size(), 2);
    +    fs2.selectAll();
    +    QVERIFY(fs2.isSelectAll());
    +    std::vector fv3= fs2.toStdVector();
    +    QCOMPARE(fv3.size(), 4u);
    +    QList fv4= fs2.toQList();
    +    QCOMPARE(fv4.size(), 4);
    +}//t_convert
    +
    +//! Spot check properties and read-only.  See QhullSet_test
    +void QhullFacetSet_test::
    +t_readonly()
    +{
    +    RboxPoints rcube("c");
    +    Qhull q(rcube,"QV0");  // good facets are adjacent to point 0
    +    QhullFacetSet fs= q.firstFacet().neighborFacets();
    +    QVERIFY(!fs.isSelectAll());
    +    QCOMPARE(fs.count(), 2);
    +    fs.selectAll();
    +    QVERIFY(fs.isSelectAll());
    +    QCOMPARE(fs.count(), 4);
    +    fs.selectGood();
    +    QVERIFY(!fs.isSelectAll());
    +    QCOMPARE(fs.count(), 2);
    +    QhullFacet f= fs.first();
    +    QhullFacet f2= fs.last();
    +    fs.selectAll();
    +    QVERIFY(fs.contains(f));
    +    QVERIFY(fs.contains(f2));
    +    QVERIFY(f.isGood());
    +    QVERIFY(!f2.isGood());
    +    fs.selectGood();
    +    QVERIFY(fs.contains(f));
    +    QVERIFY(!fs.contains(f2));
    +}//t_readonly
    +
    +void QhullFacetSet_test::
    +t_foreach()
    +{
    +    RboxPoints rcube("c");
    +    // Spot check predicates and accessors.  See QhullLinkedList_test
    +    Qhull q(rcube,"QR0");  // rotated unit cube
    +    QhullFacetSet fs= q.firstFacet().neighborFacets();
    +    QVERIFY(!fs.contains(q.firstFacet()));
    +    QVERIFY(fs.contains(fs.first()));
    +    QhullFacet f= q.firstFacet().next();
    +    if(!fs.contains(f)){  // check if 'f' is the facet opposite firstFacet()
    +        f= f.next();
    +    }
    +    QVERIFY(fs.contains(f));
    +    QCOMPARE(fs.first(), *fs.begin());
    +    QCOMPARE(*(fs.end()-1), fs.last());
    +}//t_foreach
    +
    +void QhullFacetSet_test::
    +t_io()
    +{
    +    RboxPoints rcube("c");
    +    {
    +        Qhull q(rcube,"QR0 QV0");   // good facets are adjacent to point 0
    +        QhullFacetSet fs= q.firstFacet().neighborFacets();
    +        ostringstream os;
    +        os << fs.print("Neighbors of first facet with point 0");
    +        os << fs.printIdentifiers("\nFacet identifiers: ");
    +        cout << os.str();
    +        QString facets= QString::fromStdString(os.str());
    +        QCOMPARE(facets.count(QRegExp(" f[0-9]")), 2+13*2);
    +    }
    +}//t_io
    +
    +}//orgQhull
    +
    +#include "moc/QhullFacetSet_test.moc"
    diff --git a/xs/src/qhull/src/qhulltest/QhullFacet_test.cpp b/xs/src/qhull/src/qhulltest/QhullFacet_test.cpp
    new file mode 100644
    index 000000000..271f63753
    --- /dev/null
    +++ b/xs/src/qhull/src/qhulltest/QhullFacet_test.cpp
    @@ -0,0 +1,283 @@
    +/****************************************************************************
    +**
    +** Copyright (c) 2008-2015 C.B. Barber. All rights reserved.
    +** $Id: //main/2015/qhull/src/qhulltest/QhullFacet_test.cpp#4 $$Change: 2062 $
    +** $DateTime: 2016/01/17 13:13:18 $$Author: bbarber $
    +**
    +****************************************************************************/
    +
    +//pre-compiled headers
    +#include 
    +#include "qhulltest/RoadTest.h" // QT_VERSION
    +
    +#include "libqhullcpp/QhullFacet.h"
    +#include "libqhullcpp/QhullError.h"
    +#include "libqhullcpp/Coordinates.h"
    +#include "libqhullcpp/RboxPoints.h"
    +#include "libqhullcpp/QhullFacetList.h"
    +#include "libqhullcpp/QhullFacetSet.h"
    +#include "libqhullcpp/QhullPointSet.h"
    +#include "libqhullcpp/QhullRidge.h"
    +#include "libqhullcpp/Qhull.h"
    +
    +using std::cout;
    +using std::endl;
    +using std::ostringstream;
    +using std::ostream;
    +using std::string;
    +
    +namespace orgQhull {
    +
    +class QhullFacet_test : public RoadTest
    +{
    +    Q_OBJECT
    +
    +#//!\name Test slots
    +private slots:
    +    void cleanup();
    +    void t_construct_qh();
    +    void t_constructConvert();
    +    void t_getSet();
    +    void t_value();
    +    void t_foreach();
    +    void t_io();
    +};//QhullFacet_test
    +
    +void
    +add_QhullFacet_test()
    +{
    +    new QhullFacet_test();  // RoadTest::s_testcases
    +}
    +
    +//Executed after each testcase
    +void QhullFacet_test::
    +cleanup()
    +{
    +    RoadTest::cleanup();
    +}
    +
    +void QhullFacet_test::
    +t_construct_qh()
    +{
    +    // Qhull.runQhull() constructs QhullFacets as facetT
    +    QhullQh qh;
    +    QhullFacet f(&qh);
    +    QVERIFY(!f.isValid());
    +    QCOMPARE(f.dimension(),0);
    +}//t_construct_qh
    +
    +void QhullFacet_test::
    +t_constructConvert()
    +{
    +    // Qhull.runQhull() constructs QhullFacets as facetT
    +    Qhull q2;
    +    QhullFacet f(q2);
    +    QVERIFY(!f.isValid());
    +    QCOMPARE(f.dimension(),0);
    +    RboxPoints rcube("c");
    +    Qhull q(rcube,"Qt QR0");  // triangulation of rotated unit cube
    +    QhullFacet f2(q.beginFacet());
    +    QCOMPARE(f2.dimension(),3);
    +    f= f2; // copy assignment
    +    QVERIFY(f.isValid());
    +    QCOMPARE(f.dimension(),3);
    +    QhullFacet f5= f2;
    +    QVERIFY(f5==f2);
    +    QVERIFY(f5==f);
    +    QhullFacet f3(q, f2.getFacetT());
    +    QCOMPARE(f,f3);
    +    QhullFacet f4(q, f2.getBaseT());
    +    QCOMPARE(f,f4);
    +}//t_constructConvert
    +
    +void QhullFacet_test::
    +t_getSet()
    +{
    +    RboxPoints rcube("c");
    +    {
    +        Qhull q(rcube,"Qt QR0");  // triangulation of rotated unit cube
    +        cout << " rbox c | qhull Qt QR0 QR" << q.rotateRandom() << "   distanceEpsilon " << q.distanceEpsilon() << endl;
    +        QCOMPARE(q.facetCount(), 12);
    +        QCOMPARE(q.vertexCount(), 8);
    +        QhullFacetListIterator i(q.facetList());
    +        while(i.hasNext()){
    +            const QhullFacet f= i.next();
    +            cout << f.id() << endl;
    +            QCOMPARE(f.dimension(),3);
    +            QVERIFY(f.id()>0 && f.id()<=39);
    +            QVERIFY(f.isValid());
    +            if(i.hasNext()){
    +                QCOMPARE(f.next(), i.peekNext());
    +                QVERIFY(f.next()!=f);
    +            }
    +            QVERIFY(i.hasPrevious());
    +            QCOMPARE(f, i.peekPrevious());
    +        }
    +
    +        // test tricoplanarOwner
    +        QhullFacet facet = q.beginFacet();
    +        QhullFacet tricoplanarOwner = facet.tricoplanarOwner();
    +        int tricoplanarCount= 0;
    +        i.toFront();
    +        while(i.hasNext()){
    +            const QhullFacet f= i.next();
    +            if(f.tricoplanarOwner()==tricoplanarOwner){
    +                tricoplanarCount++;
    +            }
    +        }
    +        QCOMPARE(tricoplanarCount, 2);
    +        int tricoplanarCount2= 0;
    +        foreach (QhullFacet f, q.facetList()){  // Qt only
    +            QhullHyperplane h= f.hyperplane();
    +            cout << "Hyperplane: " << h;
    +            QCOMPARE(h.count(), 3);
    +            QCOMPARE(h.offset(), -0.5);
    +            double n= h.norm();
    +            QCOMPARE(n, 1.0);
    +            QhullHyperplane hi= f.innerplane();
    +            QCOMPARE(hi.count(), 3);
    +            double innerOffset= hi.offset()+0.5;
    +            cout << "InnerPlane: " << hi << "   innerOffset+0.5 " << innerOffset << endl;
    +            QVERIFY(innerOffset >= 0.0-(2*q.distanceEpsilon())); // A guessed epsilon.  It needs to account for roundoff due to rotation of the vertices
    +            QhullHyperplane ho= f.outerplane();
    +            QCOMPARE(ho.count(), 3);
    +            double outerOffset= ho.offset()+0.5;
    +            cout << "OuterPlane: " << ho << "   outerOffset+0.5 " << outerOffset << endl;
    +            QVERIFY(outerOffset <= 0.0+(2*q.distanceEpsilon())); // A guessed epsilon.  It needs to account for roundoff due to rotation of the vertices
    +            QVERIFY(outerOffset-innerOffset < 1e-7);
    +            for(int k= 0; k<3; k++){
    +                QVERIFY(ho[k]==hi[k]);
    +                QVERIFY(ho[k]==h[k]);
    +            }
    +            QhullPoint center= f.getCenter();
    +            cout << "Center: " << center;
    +            double d= f.distance(center);
    +            QVERIFY(d < innerOffset-outerOffset);
    +            QhullPoint center2= f.getCenter(qh_PRINTcentrums);
    +            QCOMPARE(center, center2);
    +            if(f.tricoplanarOwner()==tricoplanarOwner){
    +                tricoplanarCount2++;
    +            }
    +            cout << endl;
    +        }
    +        QCOMPARE(tricoplanarCount2, 2);
    +        Qhull q2(rcube,"d Qz Qt QR0");  // 3-d triangulation of Delaunay triangulation (the cube)
    +        cout << " rbox c | qhull d Qz Qt QR0 QR" << q2.rotateRandom() << "   distanceEpsilon " << q2.distanceEpsilon() << endl;
    +        QhullFacet f2= q2.firstFacet();
    +        QhullPoint center3= f2.getCenter(qh_PRINTtriangles);
    +        QCOMPARE(center3.dimension(), 3);
    +        QhullPoint center4= f2.getCenter();
    +        QCOMPARE(center4.dimension(), 4);
    +        for(int k= 0; k<3; k++){
    +            QVERIFY(center4[k]==center3[k]);
    +        }
    +        Qhull q3(rcube,"v Qz QR0");  // Voronoi diagram of a cube (one vertex)
    +        cout << " rbox c | qhull v Qz QR0 QR" << q3.rotateRandom() << "   distanceEpsilon " << q3.distanceEpsilon() << endl;
    +
    +        q3.setFactorEpsilon(400); // Voronoi vertices are not necessarily within distance episilon
    +        QhullPoint origin= q3.inputOrigin();
    +        int voronoiCount= 0;
    +        foreach(QhullFacet f, q3.facetList()){ //Qt only
    +            if(f.isGood()){
    +                ++voronoiCount;
    +                QhullPoint p= f.voronoiVertex();
    +                cout << p.print("Voronoi vertex: ")
    +                    << " Is it within " << q3.factorEpsilon() << " * distanceEpsilon (" << q3.distanceEpsilon() << ") of the origin?" << endl;
    +                QCOMPARE(p, origin);
    +            }
    +        }
    +        QCOMPARE(voronoiCount, 1);
    +    }
    +}//t_getSet
    +
    +void QhullFacet_test::
    +t_value()
    +{
    +    RboxPoints rcube("c");
    +    {
    +        Qhull q(rcube, "");
    +        coordT c[]= {0.0, 0.0, 0.0};
    +        foreach (QhullFacet f, q.facetList()){  // Qt only
    +            double d= f.distance(q.origin());
    +            QCOMPARE(d, -0.5);
    +            double d0= f.distance(c);
    +            QCOMPARE(d0, -0.5);
    +            double facetArea= f.facetArea();
    +            QCOMPARE(facetArea, 1.0);
    +            #if qh_MAXoutside
    +                double maxoutside= f.getFacetT()->maxoutside;
    +                QVERIFY(maxoutside<1e-7);
    +            #endif
    +        }
    +    }
    +}//t_value
    +
    +void QhullFacet_test::
    +t_foreach()
    +{
    +    RboxPoints rcube("c W0 300");  // cube plus 300 points on its surface
    +    {
    +        Qhull q(rcube, "QR0 Qc"); // keep coplanars, thick facet, and rotate the cube
    +        int coplanarCount= 0;
    +        foreach(const QhullFacet f, q.facetList()){
    +            QhullPointSet coplanars= f.coplanarPoints();
    +            coplanarCount += coplanars.count();
    +            QhullFacetSet neighbors= f.neighborFacets();
    +            QCOMPARE(neighbors.count(), 4);
    +            QhullPointSet outsides= f.outsidePoints();
    +            QCOMPARE(outsides.count(), 0);
    +            QhullRidgeSet ridges= f.ridges();
    +            QCOMPARE(ridges.count(), 4);
    +            QhullVertexSet vertices= f.vertices();
    +            QCOMPARE(vertices.count(), 4);
    +            int ridgeCount= 0;
    +            QhullRidge r= ridges.first();
    +            for(int r0= r.id(); ridgeCount==0 || r.id()!=r0; r= r.nextRidge3d(f)){
    +                ++ridgeCount;
    +                if(!r.hasNextRidge3d(f)){
    +                    QFAIL("Unexpected simplicial facet.  They only have ridges to non-simplicial neighbors.");
    +                }
    +            }
    +            QCOMPARE(ridgeCount, 4);
    +        }
    +        QCOMPARE(coplanarCount, 300);
    +    }
    +}//t_foreach
    +
    +void QhullFacet_test::
    +t_io()
    +{
    +    RboxPoints rcube("c");
    +    {
    +        Qhull q(rcube, "");
    +        QhullFacet f= q.beginFacet();
    +        cout << f;
    +        ostringstream os;
    +        os << f.print("\nWith a message\n");
    +        os << "\nPrint header for the same facet\n";
    +        os << f.printHeader();
    +        os << "\nPrint each component\n";
    +        os << f.printFlags("    - flags:");
    +        os << f.printCenter(qh_PRINTfacets, "    - center: ");
    +        os << f.printRidges();
    +        cout << os.str();
    +        ostringstream os2;
    +        os2 << f;
    +        QString facetString2= QString::fromStdString(os2.str());
    +        facetString2.replace(QRegExp("\\s\\s+"), " ");
    +        ostringstream os3;
    +        q.qh()->setOutputStream(&os3);
    +        q.outputQhull("f");
    +        QString facetsString= QString::fromStdString(os3.str());
    +        QString facetString3= facetsString.mid(facetsString.indexOf("- f1\n"));
    +        facetString3= facetString3.left(facetString3.indexOf("\n- f")+1);
    +        facetString3.replace(QRegExp("\\s\\s+"), " ");
    +        QCOMPARE(facetString2, facetString3);
    +    }
    +}//t_io
    +
    +// toQhullFacet is static_cast only
    +
    +}//orgQhull
    +
    +#include "moc/QhullFacet_test.moc"
    diff --git a/xs/src/qhull/src/qhulltest/QhullHyperplane_test.cpp b/xs/src/qhull/src/qhulltest/QhullHyperplane_test.cpp
    new file mode 100644
    index 000000000..d016989a9
    --- /dev/null
    +++ b/xs/src/qhull/src/qhulltest/QhullHyperplane_test.cpp
    @@ -0,0 +1,429 @@
    +/****************************************************************************
    +**
    +** Copyright (c) 2009-2015 C.B. Barber. All rights reserved.
    +** $Id: //main/2015/qhull/src/qhulltest/QhullHyperplane_test.cpp#4 $$Change: 2064 $
    +** $DateTime: 2016/01/18 12:36:08 $$Author: bbarber $
    +**
    +****************************************************************************/
    +
    +//pre-compiled headers
    +#include 
    +#include "qhulltest/RoadTest.h" // QT_VERSION
    +
    +#include "libqhullcpp/QhullHyperplane.h"
    +#include "libqhullcpp/QhullError.h"
    +#include "libqhullcpp/RboxPoints.h"
    +#include "libqhullcpp/QhullFacet.h"
    +#include "libqhullcpp/QhullFacetList.h"
    +#include "libqhullcpp/QhullFacetSet.h"
    +#include "libqhullcpp/Qhull.h"
    +
    +#include 
    +#include 
    +
    +using std::cout;
    +using std::endl;
    +using std::ostringstream;
    +using std::ostream;
    +using std::string;
    +
    +namespace orgQhull {
    +
    +class QhullHyperplane_test : public RoadTest
    +{
    +    Q_OBJECT
    +
    +#//!\name Test slots
    +private slots:
    +    void cleanup();
    +    void t_construct();
    +    void t_construct_qh();
    +    void t_convert();
    +    void t_readonly();
    +    void t_define();
    +    void t_value();
    +    void t_operator();
    +    void t_iterator();
    +    void t_const_iterator();
    +    void t_qhullHyperplane_iterator();
    +    void t_io();
    +};//QhullHyperplane_test
    +
    +void
    +add_QhullHyperplane_test()
    +{
    +    new QhullHyperplane_test();  // RoadTest::s_testcases
    +}
    +
    +//Executed after each testcase
    +void QhullHyperplane_test::
    +cleanup()
    +{
    +    RoadTest::cleanup();
    +}
    +
    +void QhullHyperplane_test::
    +t_construct()
    +{
    +    QhullHyperplane h4;
    +    QVERIFY(!h4.isValid());
    +    QCOMPARE(h4.dimension(), 0);
    +    // Qhull.runQhull() constructs QhullFacets as facetT
    +    RboxPoints rcube("c");
    +    Qhull q(rcube,"Qt QR0");  // triangulation of rotated unit cube
    +    QhullHyperplane h(q);
    +    QVERIFY(!h.isValid());
    +    QCOMPARE(h.dimension(), 0);
    +    QCOMPARE(h.coordinates(),static_cast(0));
    +    QhullFacet f= q.firstFacet();
    +    QhullHyperplane h2(f.hyperplane());
    +    QVERIFY(h2.isValid());
    +    QCOMPARE(h2.dimension(), 3);
    +    h= h2;
    +    QCOMPARE(h, h2);
    +    QhullHyperplane h3(q, h2.dimension(), h2.coordinates(), h2.offset());
    +    QCOMPARE(h2, h3);
    +    QhullHyperplane h5= h2; // copy constructor
    +    QVERIFY(h5==h2);
    +}//t_construct
    +
    +void QhullHyperplane_test::
    +t_construct_qh()
    +{
    +    // Qhull.runQhull() constructs QhullFacets as facetT
    +    RboxPoints rcube("c");
    +    Qhull q(rcube,"Qt QR0");  // triangulation of rotated unit cube
    +    QhullFacet f= q.firstFacet();
    +    QhullHyperplane h2(f.hyperplane());
    +    QVERIFY(h2.isValid());
    +    QCOMPARE(h2.dimension(), 3);
    +    // h= h2;  // copy assignment disabled, ambiguous
    +    QhullHyperplane h3(q.qh(), h2.dimension(), h2.coordinates(), h2.offset());
    +    QCOMPARE(h2, h3);
    +    QhullHyperplane h5= h2; // copy constructor
    +    QVERIFY(h5==h2);
    +}//t_construct_qh
    +
    +void QhullHyperplane_test::
    +t_convert()
    +{
    +    RboxPoints rcube("c");
    +    Qhull q(rcube,"Qt QR0");  // triangulation of rotated unit cube
    +    QhullHyperplane h= q.firstFacet().hyperplane();
    +    std::vector fs= h.toStdVector();
    +    QCOMPARE(fs.size(), 4u);
    +    double offset= fs.back();
    +    fs.pop_back();
    +    QCOMPARE(offset, -0.5);
    +
    +    double squareNorm= inner_product(fs.begin(), fs.end(), fs.begin(), 0.0);
    +    QCOMPARE(squareNorm, 1.0);
    +    QList qs= h.toQList();
    +    QCOMPARE(qs.size(), 4);
    +    double offset2= qs.takeLast();
    +    QCOMPARE(offset2, -0.5);
    +    double squareNorm2= std::inner_product(qs.begin(), qs.end(), qs.begin(), 0.0);
    +    QCOMPARE(squareNorm2, 1.0);
    +}//t_convert
    +
    +void QhullHyperplane_test::
    +t_readonly()
    +{
    +    RboxPoints rcube("c");
    +    {
    +        Qhull q(rcube,"Qt QR0");  // triangulation of rotated unit cube
    +        QhullFacetList fs= q.facetList();
    +        QhullFacetListIterator i(fs);
    +        while(i.hasNext()){
    +            QhullFacet f= i.next();
    +            QhullHyperplane h= f.hyperplane();
    +            int id= f.id();
    +            cout << "h" << id << endl;
    +            QVERIFY(h.isValid());
    +            QCOMPARE(h.dimension(),3);
    +            const coordT *c= h.coordinates();
    +            coordT *c2= h.coordinates();
    +            QCOMPARE(c, c2);
    +            const coordT *c3= h.begin();
    +            QCOMPARE(c, c3);
    +            QCOMPARE(h.offset(), -0.5);
    +            int j= h.end()-h.begin();
    +            QCOMPARE(j, 3);
    +            double squareNorm= std::inner_product(h.begin(), h.end(), h.begin(), 0.0);
    +            QCOMPARE(squareNorm, 1.0);
    +        }
    +        QhullHyperplane h2= fs.first().hyperplane();
    +        QhullHyperplane h3= fs.last().hyperplane();
    +        QVERIFY(h2!=h3);
    +        QVERIFY(h3.coordinates()!=h2.coordinates());
    +    }
    +}//t_readonly
    +
    +void QhullHyperplane_test::
    +t_define()
    +{
    +    RboxPoints rcube("c");
    +    {
    +        Qhull q(rcube,"Qt QR0");  // triangulation of rotated unit cube
    +        QhullFacetList fs= q.facetList();
    +        QhullHyperplane h= fs.first().hyperplane();
    +        QhullHyperplane h2= h;
    +        QVERIFY(h==h2);
    +        QhullHyperplane h3= fs.last().hyperplane();
    +        QVERIFY(h2!=h3);
    +
    +        QhullHyperplane h4= h3;
    +        h4.defineAs(h2);
    +        QVERIFY(h2==h4);
    +        QhullHyperplane p5= h3;
    +        p5.defineAs(h2.dimension(), h2.coordinates(), h2.offset());
    +        QVERIFY(h2==p5);
    +        QhullHyperplane h6= h3;
    +        h6.setCoordinates(h2.coordinates());
    +        QCOMPARE(h2.coordinates(), h6.coordinates());
    +        h6.setOffset(h2.offset());
    +        QCOMPARE(h2.offset(), h6.offset());
    +        QVERIFY(h2==h6);
    +        h6.setDimension(2);
    +        QCOMPARE(h6.dimension(), 2);
    +        QVERIFY(h2!=h6);
    +    }
    +}//t_define
    +
    +void QhullHyperplane_test::
    +t_value()
    +{
    +    RboxPoints rcube("c G1");
    +    Qhull q(rcube,"Qt QR0");  // triangulation of rotated unit cube
    +    QhullFacet f= q.firstFacet();
    +    QhullFacet f2= f.neighborFacets().at(0);
    +    const QhullHyperplane h= f.hyperplane();
    +    const QhullHyperplane h2= f2.hyperplane();   // At right angles
    +    double dist= h.distance(q.origin());
    +    QCOMPARE(dist, -1.0);
    +    double norm= h.norm();
    +    QCOMPARE(norm, 1.0);
    +    double angle= h.hyperplaneAngle(h2);
    +    cout << "angle " << angle << endl;
    +    QCOMPARE(angle+1.0, 1.0); // qFuzzyCompare does not work for 0.0
    +    QVERIFY(h==h);
    +    QVERIFY(h!=h2);
    +}//t_value
    +
    +void QhullHyperplane_test::
    +t_operator()
    +{
    +    RboxPoints rcube("c");
    +    Qhull q(rcube,"Qt QR0");  // triangulation of rotated unit cube
    +    const QhullHyperplane h= q.firstFacet().hyperplane();
    +    //operator== and operator!= tested elsewhere
    +    const coordT *c= h.coordinates();
    +    for(int k=h.dimension(); k--; ){
    +        QCOMPARE(c[k], h[k]);
    +    }
    +    //h[0]= 10.0; // compiler error, const
    +    QhullHyperplane h2= q.firstFacet().hyperplane();
    +    h2[0]= 10.0;  // Overwrites Hyperplane coordinate!
    +    QCOMPARE(h2[0], 10.0);
    +}//t_operator
    +
    +void QhullHyperplane_test::
    +t_iterator()
    +{
    +    RboxPoints rcube("c");
    +    {
    +        Qhull q(rcube,"QR0");  // rotated unit cube
    +        QhullHyperplane h= q.firstFacet().hyperplane();
    +        QCOMPARE(h.count(), 3);
    +        QCOMPARE(h.size(), 3u);
    +        QhullHyperplane::Iterator i= h.begin();
    +        QhullHyperplane::iterator i2= h.begin();
    +        QVERIFY(i==i2);
    +        QVERIFY(i>=i2);
    +        QVERIFY(i<=i2);
    +        i= h.begin();
    +        QVERIFY(i==i2);
    +        i2= h.end();
    +        QVERIFY(i!=i2);
    +        double d3= *i;
    +        i2--;
    +        double d2= *i2;
    +        QCOMPARE(d3, h[0]);
    +        QCOMPARE(d2, h[2]);
    +        QhullHyperplane::Iterator i3(i2);
    +        QCOMPARE(*i2, *i3);
    +
    +        (i3= i)++;
    +        QCOMPARE((*i3), h[1]);
    +        QVERIFY(i==i);
    +        QVERIFY(i!=i2);
    +        QVERIFY(ii);
    +        QVERIFY(i2>=i);
    +
    +        QhullHyperplane::ConstIterator i4= h.begin();
    +        QVERIFY(i==i4); // iterator COMP const_iterator
    +        QVERIFY(i<=i4);
    +        QVERIFY(i>=i4);
    +        QVERIFY(i4==i); // const_iterator COMP iterator
    +        QVERIFY(i4<=i);
    +        QVERIFY(i4>=i);
    +        QVERIFY(i>=i4);
    +        QVERIFY(i4<=i);
    +        QVERIFY(i2!=i4);
    +        QVERIFY(i2>i4);
    +        QVERIFY(i2>=i4);
    +        QVERIFY(i4!=i2);
    +        QVERIFY(i4i);
    +        QVERIFY(i4>=i);
    +
    +        i= h.begin();
    +        i2= h.begin();
    +        QCOMPARE(i, i2++);
    +        QCOMPARE(*i2, h[1]);
    +        QCOMPARE(++i, i2);
    +        QCOMPARE(i, i2--);
    +        QCOMPARE(i2, h.begin());
    +        QCOMPARE(--i, i2);
    +        QCOMPARE(i2 += 3, h.end());
    +        QCOMPARE(i2 -= 3, h.begin());
    +        QCOMPARE(i2+0, h.begin());
    +        QCOMPARE(i2+3, h.end());
    +        i2 += 3;
    +        i= i2-0;
    +        QCOMPARE(i, i2);
    +        i= i2-3;
    +        QCOMPARE(i, h.begin());
    +        QCOMPARE(i2-i, 3);
    +
    +        //h.begin end tested above
    +
    +        // QhullHyperplane is const-only
    +    }
    +}//t_iterator
    +
    +void QhullHyperplane_test::
    +t_const_iterator()
    +{
    +    RboxPoints rcube("c");
    +    {
    +        Qhull q(rcube,"QR0");  // rotated unit cube
    +        QhullHyperplane h= q.firstFacet().hyperplane();
    +        QhullHyperplane::ConstIterator i= h.begin();
    +        QhullHyperplane::const_iterator i2= h.begin();
    +        QVERIFY(i==i2);
    +        QVERIFY(i>=i2);
    +        QVERIFY(i<=i2);
    +        i= h.begin();
    +        QVERIFY(i==i2);
    +        i2= h.end();
    +        QVERIFY(i!=i2);
    +        double d3= *i;
    +        i2--;
    +        double d2= *i2;
    +        QCOMPARE(d3, h[0]);
    +        QCOMPARE(d2, h[2]);
    +        QhullHyperplane::ConstIterator i3(i2);
    +        QCOMPARE(*i2, *i3);
    +
    +        (i3= i)++;
    +        QCOMPARE((*i3), h[1]);
    +        QVERIFY(i==i);
    +        QVERIFY(i!=i2);
    +        QVERIFY(ii);
    +        QVERIFY(i2>=i);
    +
    +        // See t_iterator for const_iterator COMP iterator
    +
    +        i= h.begin();
    +        i2= h.constBegin();
    +        QCOMPARE(i, i2++);
    +        QCOMPARE(*i2, h[1]);
    +        QCOMPARE(++i, i2);
    +        QCOMPARE(i, i2--);
    +        QCOMPARE(i2, h.constBegin());
    +        QCOMPARE(--i, i2);
    +        QCOMPARE(i2+=3, h.constEnd());
    +        QCOMPARE(i2-=3, h.constBegin());
    +        QCOMPARE(i2+0, h.constBegin());
    +        QCOMPARE(i2+3, h.constEnd());
    +        i2 += 3;
    +        i= i2-0;
    +        QCOMPARE(i, i2);
    +        i= i2-3;
    +        QCOMPARE(i, h.constBegin());
    +        QCOMPARE(i2-i, 3);
    +
    +        // QhullHyperplane is const-only
    +    }
    +}//t_const_iterator
    +
    +void QhullHyperplane_test::
    +t_qhullHyperplane_iterator()
    +{
    +    RboxPoints rcube("c");
    +    Qhull q(rcube,"QR0");  // rotated unit cube
    +    QhullHyperplane h = q.firstFacet().hyperplane();
    +    QhullHyperplaneIterator i2(h);
    +    QCOMPARE(h.dimension(), 3);
    +    QhullHyperplaneIterator i= h;
    +    QVERIFY(i2.hasNext());
    +    QVERIFY(!i2.hasPrevious());
    +    QVERIFY(i.hasNext());
    +    QVERIFY(!i.hasPrevious());
    +    i2.toBack();
    +    i.toFront();
    +    QVERIFY(!i2.hasNext());
    +    QVERIFY(i2.hasPrevious());
    +    QVERIFY(i.hasNext());
    +    QVERIFY(!i.hasPrevious());
    +
    +    // i at front, i2 at end/back, 3 coordinates
    +    QCOMPARE(i.peekNext(), h[0]);
    +    QCOMPARE(i2.peekPrevious(), h[2]);
    +    QCOMPARE(i2.previous(), h[2]);
    +    QCOMPARE(i2.previous(), h[1]);
    +    QCOMPARE(i2.previous(), h[0]);
    +    QVERIFY(!i2.hasPrevious());
    +    QCOMPARE(i.peekNext(), h[0]);
    +    // i.peekNext()= 1.0; // compiler error, i is const
    +    QCOMPARE(i.next(), h[0]);
    +    QCOMPARE(i.peekNext(), h[1]);
    +    QCOMPARE(i.next(), h[1]);
    +    QCOMPARE(i.next(), h[2]);
    +    QVERIFY(!i.hasNext());
    +    i.toFront();
    +    QCOMPARE(i.next(), h[0]);
    +}//t_qhullHyperplane_iterator
    +
    +void QhullHyperplane_test::
    +t_io()
    +{
    +    RboxPoints rcube("c");
    +    {
    +        Qhull q(rcube, "");
    +        QhullHyperplane h= q.firstFacet().hyperplane();
    +        ostringstream os;
    +        os << "Hyperplane:\n";
    +        os << h;
    +        os << h.print("message");
    +        os << h.print(" and a message ", " offset ");
    +        cout << os.str();
    +        QString s= QString::fromStdString(os.str());
    +        QCOMPARE(s.count("1"), 3);
    +        // QCOMPARE(s.count(QRegExp("f\\d")), 3*7 + 13*3*2);
    +    }
    +}//t_io
    +
    +
    +}//orgQhull
    +
    +#include "moc/QhullHyperplane_test.moc"
    diff --git a/xs/src/qhull/src/qhulltest/QhullLinkedList_test.cpp b/xs/src/qhull/src/qhulltest/QhullLinkedList_test.cpp
    new file mode 100644
    index 000000000..e0cde4050
    --- /dev/null
    +++ b/xs/src/qhull/src/qhulltest/QhullLinkedList_test.cpp
    @@ -0,0 +1,330 @@
    +/****************************************************************************
    +**
    +** Copyright (c) 2009-2015 C.B. Barber. All rights reserved.
    +** $Id: //main/2015/qhull/src/qhulltest/QhullLinkedList_test.cpp#3 $$Change: 2062 $
    +** $DateTime: 2016/01/17 13:13:18 $$Author: bbarber $
    +**
    +****************************************************************************/
    +
    +//pre-compiled headers
    +#include 
    +#include "qhulltest/RoadTest.h"
    +
    +#include "libqhullcpp/QhullLinkedList.h"
    +#include "libqhullcpp/Qhull.h"
    +#include "libqhullcpp/QhullVertex.h"
    +#include "libqhullcpp/RboxPoints.h"
    +
    +namespace orgQhull {
    +
    +class QhullLinkedList_test : public RoadTest
    +{
    +    Q_OBJECT
    +
    +#//!\name Test slots
    +private slots:
    +    void cleanup();
    +    void t_construct();
    +    void t_convert();
    +    void t_element();
    +    void t_search();
    +    void t_iterator();
    +    void t_const_iterator();
    +    void t_QhullLinkedList_iterator();
    +    void t_io();
    +};//QhullLinkedList_test
    +
    +void
    +add_QhullLinkedList_test()
    +{
    +    new QhullLinkedList_test();  // RoadTest::s_testcases
    +}
    +
    +//Executed after each testcase
    +void QhullLinkedList_test::
    +cleanup()
    +{
    +    RoadTest::cleanup();
    +}
    +
    +void QhullLinkedList_test::
    +t_construct()
    +{
    +    // QhullLinkedList vs; //private (compiler error).  No memory allocation
    +    RboxPoints rcube("c");
    +    {
    +        Qhull q(rcube,"Qt QR0");  // triangulation of rotated unit cube
    +        QCOMPARE(q.facetCount(), 12);
    +        QhullVertexList vs = QhullVertexList(q.beginVertex(), q.endVertex());
    +        QCOMPARE(vs.count(), 8);
    +        QCOMPARE(vs.size(), 8u);
    +        QVERIFY(!vs.isEmpty());
    +        QhullVertexList vs2 = q.vertexList();
    +        QCOMPARE(vs2.count(), 8);
    +        QCOMPARE(vs2.size(),8u);
    +        QVERIFY(!vs2.isEmpty());
    +        QVERIFY(vs==vs2);
    +        // vs= vs2; // disabled.  Would not copy the vertices
    +        QhullVertexList vs3= vs2; // copy constructor
    +        QVERIFY(vs3==vs2);
    +    }
    +}//t_construct
    +
    +void QhullLinkedList_test::
    +t_convert()
    +{
    +    RboxPoints rcube("c");
    +    {
    +        Qhull q(rcube,"Qt QR0");  // triangulation of rotated unit cube
    +        QCOMPARE(q.facetCount(), 12);
    +        QhullVertexList vs = q.vertexList();
    +        QCOMPARE(vs.size(), 8u);
    +        QVERIFY(!vs.isEmpty());
    +        std::vector vs2= vs.toStdVector();
    +        QCOMPARE(vs2.size(), vs.size());
    +        QhullVertexList::Iterator i= vs.begin();
    +        for(int k= 0; k<(int)vs2.size(); k++){
    +            QCOMPARE(vs2[k], *i++);
    +        }
    +        QList vs3= vs.toQList();
    +        QCOMPARE(vs3.count(), vs.count());
    +        i= vs.begin();
    +        for(int k= 0; k
    +#include "RoadTest.h" // QT_VERSION
    +
    +#include "libqhullcpp/QhullPointSet.h"
    +#include "libqhullcpp/RboxPoints.h"
    +#include "libqhullcpp/QhullPoint.h"
    +#include "libqhullcpp/QhullFacet.h"
    +#include "libqhullcpp/QhullFacetList.h"
    +#include "libqhullcpp/Qhull.h"
    +
    +using std::cout;
    +using std::endl;
    +using std::ostringstream;
    +
    +namespace orgQhull {
    +
    +class QhullPointSet_test : public RoadTest
    +{
    +    Q_OBJECT
    +
    +#//!\name Test slots
    +private slots:
    +    void cleanup();
    +    void t_construct();
    +    void t_convert();
    +    void t_element();
    +    void t_iterator();
    +    void t_const_iterator();
    +    void t_search();
    +    void t_pointset_iterator();
    +    void t_io();
    +};//QhullPointSet_test
    +
    +void
    +add_QhullPointSet_test()
    +{
    +    new QhullPointSet_test();  // RoadTest::s_testcases
    +}
    +
    +//Executed after each testcase
    +void QhullPointSet_test::
    +cleanup()
    +{
    +    RoadTest::cleanup();
    +}
    +
    +void QhullPointSet_test::
    +t_construct()
    +{
    +    // Default constructor is disallowed (i.e., private)
    +    RboxPoints rcube("c W0 1000");
    +    Qhull q(rcube,"Qc");  // cube with 1000 coplanar points
    +    int coplanarCount= 0;
    +    foreach(QhullFacet f, q.facetList()){
    +        QhullPointSet ps(q, f.getFacetT()->outsideset);
    +        QVERIFY(ps.isEmpty());
    +        QCOMPARE(ps.count(), 0);
    +        QCOMPARE(ps.size(), 0u);
    +        QhullPointSet ps2(q.qh(), f.getFacetT()->coplanarset);
    +        QVERIFY(!ps2.isEmpty());
    +        coplanarCount += ps2.count();
    +        QCOMPARE(ps2.count(), (int)ps2.size());
    +        QhullPointSet ps3(ps2);
    +        QVERIFY(!ps3.isEmpty());
    +        QCOMPARE(ps3.count(), ps2.count());
    +        QVERIFY(ps3==ps2);
    +        QVERIFY(ps3!=ps);
    +        QhullPointSet ps4= ps3;
    +        QVERIFY(ps4==ps2);
    +    }
    +    QCOMPARE(coplanarCount, 1000);
    +}//t_construct
    +
    +void QhullPointSet_test::
    +t_convert()
    +{
    +    RboxPoints rcube("c W0 1000");
    +    Qhull q(rcube,"Qc");  // cube with 1000 coplanar points
    +    QhullFacet f= q.firstFacet();
    +    QhullPointSet ps= f.coplanarPoints();
    +    QVERIFY(ps.count()>=1);   // Sometimes no coplanar points
    +    std::vector vs= ps.toStdVector();
    +    QCOMPARE(vs.size(), ps.size());
    +    QhullPoint p= ps[0];
    +    QhullPoint p2= vs[0];
    +    QCOMPARE(p, p2);
    +    QList qs= ps.toQList();
    +    QCOMPARE(qs.size(), static_cast(ps.size()));
    +    QhullPoint p3= qs[0];
    +    QCOMPARE(p3, p);
    +}//t_convert
    +
    +// readonly tested in t_construct
    +//   empty, isEmpty, ==, !=, size
    +
    +void QhullPointSet_test::
    +t_element()
    +{
    +    RboxPoints rcube("c W0 1000");
    +    Qhull q(rcube,"Qc");  // cube with 1000 coplanar points
    +    QhullFacet f= q.firstFacet();
    +    QhullPointSet ps= f.coplanarPoints();
    +    QVERIFY(ps.count()>=3);  // Sometimes no coplanar points
    +    QhullPoint p= ps[0];
    +    QCOMPARE(p, ps[0]);
    +    QhullPoint p2= ps[ps.count()-1];
    +    QCOMPARE(ps.at(1), ps[1]);
    +    QCOMPARE(ps.second(), ps[1]);
    +    QCOMPARE(ps.first(), p);
    +    QCOMPARE(ps.front(), ps.first());
    +    QCOMPARE(ps.last(), p2);
    +    QCOMPARE(ps.back(), ps.last());
    +    QhullPoint p8(q);
    +    QCOMPARE(ps.value(2), ps[2]);
    +    QCOMPARE(ps.value(-1), p8);
    +    QCOMPARE(ps.value(ps.count()), p8);
    +    QCOMPARE(ps.value(ps.count(), p), p);
    +    QVERIFY(ps.value(1, p)!=p);
    +    QhullPointSet ps8= f.coplanarPoints();
    +    QhullPointSet::Iterator i= ps8.begin();
    +    foreach(QhullPoint p9, ps){  // Qt only
    +        QCOMPARE(p9.dimension(), 3);
    +        QCOMPARE(p9, *i++);
    +    }
    +}//t_element
    +
    +void QhullPointSet_test::
    +t_iterator()
    +{
    +    RboxPoints rcube("c W0 1000");
    +    Qhull q(rcube,"Qc");  // cube with 1000 coplanar points
    +    QhullFacet f= q.firstFacet();
    +    QhullPointSet ps= f.coplanarPoints();
    +    QVERIFY(ps.count()>=3);  // Sometimes no coplanar points
    +    QhullPointSet::Iterator i= ps.begin();
    +    QhullPointSet::iterator i2= ps.begin();
    +    QVERIFY(i==i2);
    +    QVERIFY(i>=i2);
    +    QVERIFY(i<=i2);
    +    i= ps.begin();
    +    QVERIFY(i==i2);
    +    i2= ps.end();
    +    QVERIFY(i!=i2);
    +    QhullPoint p= *i;
    +    QCOMPARE(p.dimension(), q.dimension());
    +    QCOMPARE(p, ps[0]);
    +    i2--;
    +    QhullPoint p2= *i2;
    +    QCOMPARE(p2.dimension(), q.dimension());
    +    QCOMPARE(p2, ps.last());
    +    QhullPointSet::Iterator i5(i2);
    +    QCOMPARE(*i2, *i5);
    +    QhullPointSet::Iterator i3= i+1;
    +    QVERIFY(i!=i3);
    +    QCOMPARE(i[1], *i3);
    +    (i3= i)++;
    +    QCOMPARE((*i3)[0], ps[1][0]);
    +    QCOMPARE((*i3).dimension(), 3);
    +
    +    QVERIFY(i==i);
    +    QVERIFY(i!=i3);
    +    QVERIFY(ii);
    +    QVERIFY(i3>=i);
    +
    +    QhullPointSet::ConstIterator i4= ps.begin();
    +    QVERIFY(i==i4); // iterator COMP const_iterator
    +    QVERIFY(i<=i4);
    +    QVERIFY(i>=i4);
    +    QVERIFY(i4==i); // const_iterator COMP iterator
    +    QVERIFY(i4<=i);
    +    QVERIFY(i4>=i);
    +    QVERIFY(i>=i4);
    +    QVERIFY(i4<=i);
    +    QVERIFY(i2!=i4);
    +    QVERIFY(i2>i4);
    +    QVERIFY(i2>=i4);
    +    QVERIFY(i4!=i2);
    +    QVERIFY(i4i);
    +    QVERIFY(i4>=i);
    +    i4= ps.constBegin();
    +    QVERIFY(i==i4); // iterator COMP const_iterator
    +    QCOMPARE(i4+ps.count(), ps.constEnd());
    +
    +    i= ps.begin();
    +    i2= ps.begin();
    +    QCOMPARE(i, i2++);
    +    QCOMPARE(*i2, ps[1]);
    +    QCOMPARE(++i, i2);
    +    QCOMPARE(i, i2--);
    +    QCOMPARE(i2, ps.begin());
    +    QCOMPARE(--i, i2);
    +    QCOMPARE(i2+=ps.count(), ps.end());
    +    QCOMPARE(i2-=ps.count(), ps.begin());
    +    QCOMPARE(i2+0, ps.begin());
    +    QCOMPARE(i2+ps.count(), ps.end());
    +    i2 += ps.count();
    +    i= i2-0;
    +    QCOMPARE(i, i2);
    +    i= i2-ps.count();
    +    QCOMPARE(i, ps.begin());
    +    QCOMPARE(i2-i, ps.count());
    +
    +    //ps.begin end tested above
    +
    +    // QhullPointSet is const-only
    +}//t_iterator
    +
    +void QhullPointSet_test::
    +t_const_iterator()
    +{
    +    RboxPoints rcube("c W0 1000");
    +    Qhull q(rcube,"Qc");  // cube with 1000 coplanar points
    +    QhullFacet f= q.firstFacet();
    +    QhullPointSet ps= f.coplanarPoints();
    +    QVERIFY(ps.count()>=3);  // Sometimes no coplanar points
    +    QhullPointSet::ConstIterator i= ps.begin();
    +    QhullPointSet::const_iterator i2= ps.begin();
    +    QVERIFY(i==i2);
    +    QVERIFY(i>=i2);
    +    QVERIFY(i<=i2);
    +
    +    // See t_iterator for const_iterator COMP iterator
    +
    +    i= ps.begin();
    +    QVERIFY(i==i2);
    +    i2= ps.end();
    +    QVERIFY(i!=i2);
    +    QhullPoint p= *i; // QhullPoint is the base class for QhullPointSet::iterator
    +    QCOMPARE(p.dimension(), q.dimension());
    +    QCOMPARE(p, ps[0]);
    +    i2--;
    +    QhullPoint p2= *i2;
    +    QCOMPARE(p2.dimension(), q.dimension());
    +    QCOMPARE(p2, ps.last());
    +    QhullPointSet::ConstIterator i5(i2);
    +    QCOMPARE(*i2, *i5);
    +
    +
    +    QhullPointSet::ConstIterator i3= i+1;
    +    QVERIFY(i!=i3);
    +    QCOMPARE(i[1], *i3);
    +
    +    QVERIFY(i==i);
    +    QVERIFY(i!=i3);
    +    QVERIFY(ii);
    +    QVERIFY(i3>=i);
    +
    +    // QhullPointSet is const-only
    +}//t_const_iterator
    +
    +
    +void QhullPointSet_test::
    +t_search()
    +{
    +    RboxPoints rcube("c W0 1000");
    +    Qhull q(rcube,"Qc");  // cube with 1000 coplanar points
    +    QhullFacet f= q.firstFacet();
    +    QhullPointSet ps= f.coplanarPoints();
    +    QVERIFY(ps.count()>=3);  // Sometimes no coplanar points
    +    QhullPoint p= ps.first();
    +    QhullPoint p2= ps.last();
    +    QVERIFY(ps.contains(p));
    +    QVERIFY(ps.contains(p2));
    +    QVERIFY(p!=p2);
    +    QhullPoint p3= ps[2];
    +    QVERIFY(ps.contains(p3));
    +    QVERIFY(p!=p3);
    +    QCOMPARE(ps.indexOf(p), 0);
    +    QCOMPARE(ps.indexOf(p2), ps.count()-1);
    +    QCOMPARE(ps.indexOf(p3), 2);
    +    QhullPoint p4(q);
    +    QCOMPARE(ps.indexOf(p4), -1);
    +    QCOMPARE(ps.lastIndexOf(p), 0);
    +    QCOMPARE(ps.lastIndexOf(p2), ps.count()-1);
    +    QCOMPARE(ps.lastIndexOf(p3), 2);
    +    QCOMPARE(ps.lastIndexOf(p4), -1);
    +}//t_search
    +
    +void QhullPointSet_test::
    +t_pointset_iterator()
    +{
    +    RboxPoints rcube("c W0 1000");
    +    Qhull q(rcube,"Qc");  // cube with 1000 coplanar points
    +    QhullFacet f= q.firstFacet();
    +    QhullPointSet ps2= f.outsidePoints();
    +    QVERIFY(ps2.count()==0); // No outside points after constructing the convex hull
    +    QhullPointSetIterator i2= ps2;
    +    QCOMPARE(i2.countRemaining(), 0);
    +    QVERIFY(!i2.hasNext());
    +    QVERIFY(!i2.hasPrevious());
    +    i2.toBack();
    +    QVERIFY(!i2.hasNext());
    +    QVERIFY(!i2.hasPrevious());
    +
    +    QhullPointSet ps= f.coplanarPoints();
    +    QVERIFY(ps.count()>=3);  // Sometimes no coplanar points
    +    QhullPointSetIterator i(ps);
    +    i2= ps;
    +    QCOMPARE(i2.countRemaining(), ps.count());
    +    QVERIFY(i2.hasNext());
    +    QVERIFY(!i2.hasPrevious());
    +    QVERIFY(i.hasNext());
    +    QVERIFY(!i.hasPrevious());
    +    i2.toBack();
    +    QCOMPARE(i2.countRemaining(), 0);
    +    i.toFront();
    +    QCOMPARE(i.countRemaining(), ps.count());
    +    QCOMPARE(i2.countRemaining(), 0);
    +    QVERIFY(!i2.hasNext());
    +    QVERIFY(i2.hasPrevious());
    +    QVERIFY(i.hasNext());
    +    QVERIFY(!i.hasPrevious());
    +
    +    QhullPoint p= ps[0];
    +    QhullPoint p2(ps[0]);
    +    QCOMPARE(p, p2);
    +    QVERIFY(p==p2);
    +    QhullPoint p3(ps.last());
    + // p2[0]= 0.0;
    +    QVERIFY(p==p2);
    +    QCOMPARE(i2.peekPrevious(), p3);
    +    QCOMPARE(i2.previous(), p3);
    +    QCOMPARE(i2.previous(), ps[ps.count()-2]);
    +    QVERIFY(i2.hasPrevious());
    +    QCOMPARE(i.peekNext(), p);
    +    // i.peekNext()= 1.0; // compiler error
    +    QCOMPARE(i.next(), p);
    +    QCOMPARE(i.countRemaining(), ps.count()-1);
    +    QhullPoint p4= i.peekNext();
    +    QVERIFY(p4!=p3);
    +    QCOMPARE(i.next(), p4);
    +    QVERIFY(i.hasNext());
    +    i.toFront();
    +    QCOMPARE(i.next(), p);
    +}//t_pointset_iterator
    +
    +void QhullPointSet_test::
    +t_io()
    +{
    +    ostringstream os;
    +    RboxPoints rcube("c W0 120");
    +    Qhull q(rcube,"Qc");  // cube with 100 coplanar points
    +    QhullFacet f= q.firstFacet();
    +    QhullPointSet ps= f.coplanarPoints();
    +    QVERIFY(ps.count()>=3);  // Sometimes no coplanar points
    +    os << "QhullPointSet from coplanarPoints\n" << ps << endl;
    +    os << ps.print("\nWith message\n");
    +    os << ps.printIdentifiers("\nCoplanar points: ");
    +    os << "\nAs a point set:\n";
    +    os << ps;
    +    cout << os.str();
    +    QString s= QString::fromStdString(os.str());
    +    QCOMPARE(s.count(" 0.5\n"), 3*ps.count());
    +    QCOMPARE(s.count("p"), ps.count()+4);
    +}//t_io
    +
    +}//orgQhull
    +
    +#include "moc/QhullPointSet_test.moc"
    diff --git a/xs/src/qhull/src/qhulltest/QhullPoint_test.cpp b/xs/src/qhull/src/qhulltest/QhullPoint_test.cpp
    new file mode 100644
    index 000000000..1528086a1
    --- /dev/null
    +++ b/xs/src/qhull/src/qhulltest/QhullPoint_test.cpp
    @@ -0,0 +1,437 @@
    +/****************************************************************************
    +**
    +** Copyright (c) 2008-2015 C.B. Barber. All rights reserved.
    +** $Id: //main/2015/qhull/src/qhulltest/QhullPoint_test.cpp#3 $$Change: 2062 $
    +** $DateTime: 2016/01/17 13:13:18 $$Author: bbarber $
    +**
    +****************************************************************************/
    +
    +//pre-compiled headers
    +#include 
    +#include "RoadTest.h" // QT_VERSION
    +
    +#include "libqhullcpp/QhullPoint.h"
    +#include "libqhullcpp/Coordinates.h"
    +#include "libqhullcpp/RboxPoints.h"
    +#include "libqhullcpp/QhullError.h"
    +#include "libqhullcpp/QhullFacet.h"
    +#include "libqhullcpp/QhullPoint.h"
    +#include "libqhullcpp/Qhull.h"
    +
    +#include 
    +
    +using std::cout;
    +using std::endl;
    +using std::ostringstream;
    +using std::ostream;
    +using std::string;
    +
    +namespace orgQhull {
    +
    +class QhullPoint_test : public RoadTest
    +{
    +    Q_OBJECT
    +
    +#//!\name Test slots
    +private slots:
    +    void cleanup();
    +    void t_construct();
    +    void t_convert();
    +    void t_readonly();
    +    void t_define();
    +    void t_operator();
    +    void t_iterator();
    +    void t_const_iterator();
    +    void t_qhullpoint_iterator();
    +    void t_method();
    +    void t_io();
    +};//QhullPoint_test
    +
    +void
    +add_QhullPoint_test()
    +{
    +    new QhullPoint_test();  // RoadTest::s_testcases
    +}
    +
    +//Executed after each test
    +void QhullPoint_test::
    +cleanup()
    +{
    +    RoadTest::cleanup();
    +}
    +
    +void QhullPoint_test::
    +t_construct()
    +{
    +    QhullPoint p12;
    +    QVERIFY(!p12.isValid());
    +    QCOMPARE(p12.coordinates(), (coordT *)0);
    +    QCOMPARE(p12.dimension(), 0);
    +    QCOMPARE(p12.qh(), (QhullQh *)0);
    +    QCOMPARE(p12.id(), -3);
    +    QCOMPARE(p12.begin(), p12.end());
    +    QCOMPARE(p12.constBegin(), p12.constEnd());
    +
    +    RboxPoints rcube("c");
    +    Qhull q(rcube, "Qt QR0");  // triangulation of rotated unit cube
    +    QhullPoint p(q);
    +    QVERIFY(!p.isValid());
    +    QCOMPARE(p.dimension(),3);
    +    QCOMPARE(p.coordinates(),static_cast(0));
    +    QhullPoint p7(q.qh());
    +    QCOMPARE(p, p7);
    +
    +    // copy constructor and copy assignment
    +    QhullVertex v2(q.beginVertex());
    +    QhullPoint p2(v2.point());
    +    QVERIFY(p2.isValid());
    +    QCOMPARE(p2.dimension(),3);
    +    QVERIFY(p2!=p12);
    +    p= p2;
    +    QCOMPARE(p, p2);
    +
    +    QhullPoint p3(q, p2.dimension(), p2.coordinates());
    +    QCOMPARE(p3, p2);
    +    QhullPoint p8(q, p2.coordinates()); // Qhull defines dimension
    +    QCOMPARE(p8, p2);
    +    QhullPoint p9(q.qh(), p2.dimension(), p2.coordinates());
    +    QCOMPARE(p9, p2);
    +    QhullPoint p10(q.qh(), p2.coordinates()); // Qhull defines dimension
    +    QCOMPARE(p10, p2);
    +
    +    Coordinates c;
    +    c << 0.0 << 0.0 << 0.0;
    +    QhullPoint p6(q, c);
    +    QCOMPARE(p6, q.origin());
    +    QhullPoint p11(q.qh(), c);
    +    QCOMPARE(p11, q.origin());
    +
    +    QhullPoint p5= p2; // copy constructor
    +    QVERIFY(p5==p2);
    +}//t_construct
    +
    +void QhullPoint_test::
    +t_convert()
    +{
    +    RboxPoints rcube("c");
    +    Qhull q(rcube,"Qt QR0");  // triangulation of rotated unit cube
    +    QhullVertex v= q.firstVertex();
    +    QhullPoint p= v.point();
    +    std::vector vs= p.toStdVector();
    +    QCOMPARE(vs.size(), 3u);
    +    for(int k=3; k--; ){
    +        QCOMPARE(vs[k], p[k]);
    +    }
    +    QList qs= p.toQList();
    +    QCOMPARE(qs.size(), 3);
    +    for(int k=3; k--; ){
    +        QCOMPARE(qs[k], p[k]);
    +    }
    +}//t_convert
    +
    +void QhullPoint_test::
    +t_readonly()
    +{
    +    RboxPoints rcube("c");
    +    {
    +        Qhull q(rcube,"Qt QR0");  // triangulation of rotated unit cube
    +        QhullVertexList vs= q.vertexList();
    +        cout << "Point ids in 'rbox c'\n";
    +        QhullVertexListIterator i(vs);
    +        while(i.hasNext()){
    +            QhullPoint p= i.next().point();
    +            int id= p.id();
    +            cout << "p" << id << endl;
    +            QVERIFY(p.isValid());
    +            QCOMPARE(p.dimension(),3);
    +            QCOMPARE(id, p.id());
    +            QVERIFY(p.id()>=0 && p.id()<9);
    +            const coordT *c= p.coordinates();
    +            coordT *c2= p.coordinates();
    +            QCOMPARE(c, c2);
    +            QCOMPARE(p.dimension(), 3);
    +            QCOMPARE(q.qh(), p.qh());
    +        }
    +        QhullPoint p2= vs.first().point();
    +        QhullPoint p3= vs.last().point();
    +        QVERIFY(p2!=p3);
    +        QVERIFY(p3.coordinates()!=p2.coordinates());
    +    }
    +}//t_readonly
    +
    +void QhullPoint_test::
    +t_define()
    +{
    +    RboxPoints rcube("c");
    +    {
    +        Qhull q(rcube,"Qt QR0");  // triangulation of rotated unit cube
    +        QhullVertexList vs= q.vertexList();
    +        QhullPoint p= vs.first().point();
    +        QhullPoint p2= p;
    +        QVERIFY(p==p2);
    +        QhullPoint p3= vs.last().point();
    +        QVERIFY(p2!=p3);
    +        int idx= (p3.coordinates()-p2.coordinates())/p2.dimension();
    +        QVERIFY(idx>-8 && idx<8);
    +        p2.advancePoint(idx);
    +        QVERIFY(p2==p3);
    +        p2.advancePoint(-idx);
    +        QVERIFY(p2==p);
    +        p2.advancePoint(0);
    +        QVERIFY(p2==p);
    +
    +        QhullPoint p4= p3;
    +        QVERIFY(p4==p3);
    +        p4.defineAs(p2);
    +        QVERIFY(p2==p4);
    +        QhullPoint p5= p3;
    +        p5.defineAs(p2.dimension(), p2.coordinates());
    +        QVERIFY(p2==p5);
    +        QhullPoint p6= p3;
    +        p6.setCoordinates(p2.coordinates());
    +        QCOMPARE(p2.coordinates(), p6.coordinates());
    +        QVERIFY(p2==p6);
    +        p6.setDimension(2);
    +        QCOMPARE(p6.dimension(), 2);
    +        QVERIFY(p2!=p6);
    +    }
    +}//t_define
    +
    +void QhullPoint_test::
    +t_operator()
    +{
    +    RboxPoints rcube("c");
    +    Qhull q(rcube,"Qt QR0");  // triangulation of rotated unit cube
    +    const QhullPoint p= q.firstVertex().point();
    +    //operator== and operator!= tested elsewhere
    +    const coordT *c= p.coordinates();
    +    for(int k=p.dimension(); k--; ){
    +        QCOMPARE(c[k], p[k]);
    +    }
    +    //p[0]= 10.0; // compiler error, const
    +    QhullPoint p2= q.firstVertex().point();
    +    p2[0]= 10.0;  // Overwrites point coordinate
    +    QCOMPARE(p2[0], 10.0);
    +}//t_operator
    +
    +void QhullPoint_test::
    +t_iterator()
    +{
    +    RboxPoints rcube("c");
    +    {
    +        Qhull q(rcube,"QR0");  // rotated unit cube
    +        QhullPoint p2(q);
    +        QCOMPARE(p2.begin(), p2.end());
    +
    +        QhullPoint p= q.firstVertex().point();
    +        QhullPoint::Iterator i= p.begin();
    +        QhullPoint::iterator i2= p.begin();
    +        QVERIFY(i==i2);
    +        QVERIFY(i>=i2);
    +        QVERIFY(i<=i2);
    +        i= p.begin();
    +        QVERIFY(i==i2);
    +        i2= p.end();
    +        QVERIFY(i!=i2);
    +        double d3= *i;
    +        i2--;
    +        double d2= *i2;
    +        QCOMPARE(d3, p[0]);
    +        QCOMPARE(d2, p[2]);
    +        QhullPoint::Iterator i3(i2);
    +        QCOMPARE(*i2, *i3);
    +
    +        (i3= i)++;
    +        QCOMPARE((*i3), p[1]);
    +        QVERIFY(i==i);
    +        QVERIFY(i!=i2);
    +        QVERIFY(ii);
    +        QVERIFY(i2>=i);
    +
    +        QhullPoint::ConstIterator i4= p.begin();
    +        QVERIFY(i==i4); // iterator COMP const_iterator
    +        QVERIFY(i<=i4);
    +        QVERIFY(i>=i4);
    +        QVERIFY(i4==i); // const_iterator COMP iterator
    +        QVERIFY(i4<=i);
    +        QVERIFY(i4>=i);
    +        QVERIFY(i>=i4);
    +        QVERIFY(i4<=i);
    +        QVERIFY(i2!=i4);
    +        QVERIFY(i2>i4);
    +        QVERIFY(i2>=i4);
    +        QVERIFY(i4!=i2);
    +        QVERIFY(i4i);
    +        QVERIFY(i4>=i);
    +
    +        i= p.begin();
    +        i2= p.begin();
    +        QCOMPARE(i, i2++);
    +        QCOMPARE(*i2, p[1]);
    +        QCOMPARE(++i, i2);
    +        QCOMPARE(i, i2--);
    +        QCOMPARE(i2, p.begin());
    +        QCOMPARE(--i, i2);
    +        QCOMPARE(i2 += 3, p.end());
    +        QCOMPARE(i2 -= 3, p.begin());
    +        QCOMPARE(i2+0, p.begin());
    +        QCOMPARE(i2+3, p.end());
    +        i2 += 3;
    +        i= i2-0;
    +        QCOMPARE(i, i2);
    +        i= i2-3;
    +        QCOMPARE(i, p.begin());
    +        QCOMPARE(i2-i, 3);
    +
    +        //p.begin end tested above
    +
    +        // QhullPoint is const-only
    +    }
    +}//t_iterator
    +
    +void QhullPoint_test::
    +t_const_iterator()
    +{
    +    RboxPoints rcube("c");
    +    {
    +        Qhull q(rcube,"QR0");  // rotated unit cube
    +        QhullPoint p= q.firstVertex().point();
    +        QhullPoint::ConstIterator i= p.begin();
    +        QhullPoint::const_iterator i2= p.begin();
    +        QVERIFY(i==i2);
    +        QVERIFY(i>=i2);
    +        QVERIFY(i<=i2);
    +        i= p.begin();
    +        QVERIFY(i==i2);
    +        i2= p.end();
    +        QVERIFY(i!=i2);
    +        double d3= *i;
    +        i2--;
    +        double d2= *i2;
    +        QCOMPARE(d3, p[0]);
    +        QCOMPARE(d2, p[2]);
    +        QhullPoint::ConstIterator i3(i2);
    +        QCOMPARE(*i2, *i3);
    +
    +        (i3= i)++;
    +        QCOMPARE((*i3), p[1]);
    +        QVERIFY(i==i);
    +        QVERIFY(i!=i2);
    +        QVERIFY(ii);
    +        QVERIFY(i2>=i);
    +
    +        // See t_iterator for const_iterator COMP iterator
    +
    +        i= p.begin();
    +        i2= p.constBegin();
    +        QCOMPARE(i, i2++);
    +        QCOMPARE(*i2, p[1]);
    +        QCOMPARE(++i, i2);
    +        QCOMPARE(i, i2--);
    +        QCOMPARE(i2, p.constBegin());
    +        QCOMPARE(--i, i2);
    +        QCOMPARE(i2+=3, p.constEnd());
    +        QCOMPARE(i2-=3, p.constBegin());
    +        QCOMPARE(i2+0, p.constBegin());
    +        QCOMPARE(i2+3, p.constEnd());
    +        i2 += 3;
    +        i= i2-0;
    +        QCOMPARE(i, i2);
    +        i= i2-3;
    +        QCOMPARE(i, p.constBegin());
    +        QCOMPARE(i2-i, 3);
    +
    +        // QhullPoint is const-only
    +    }
    +}//t_const_iterator
    +
    +void QhullPoint_test::
    +t_qhullpoint_iterator()
    +{
    +    RboxPoints rcube("c");
    +    Qhull q(rcube,"QR0");  // rotated unit cube
    +
    +    QhullPoint p2(q);
    +    QhullPointIterator i= p2;
    +    QCOMPARE(p2.dimension(), 3);
    +    QVERIFY(!i.hasNext());
    +    QVERIFY(!i.hasPrevious());
    +    i.toBack();
    +    QVERIFY(!i.hasNext());
    +    QVERIFY(!i.hasPrevious());
    +
    +    QhullPoint p = q.firstVertex().point();
    +    QhullPointIterator i2(p);
    +    QCOMPARE(p.dimension(), 3);
    +    i= p;
    +    QVERIFY(i2.hasNext());
    +    QVERIFY(!i2.hasPrevious());
    +    QVERIFY(i.hasNext());
    +    QVERIFY(!i.hasPrevious());
    +    i2.toBack();
    +    i.toFront();
    +    QVERIFY(!i2.hasNext());
    +    QVERIFY(i2.hasPrevious());
    +    QVERIFY(i.hasNext());
    +    QVERIFY(!i.hasPrevious());
    +
    +    // i at front, i2 at end/back, 3 coordinates
    +    QCOMPARE(i.peekNext(), p[0]);
    +    QCOMPARE(i2.peekPrevious(), p[2]);
    +    QCOMPARE(i2.previous(), p[2]);
    +    QCOMPARE(i2.previous(), p[1]);
    +    QCOMPARE(i2.previous(), p[0]);
    +    QVERIFY(!i2.hasPrevious());
    +    QCOMPARE(i.peekNext(), p[0]);
    +    // i.peekNext()= 1.0; // compiler error, i is const
    +    QCOMPARE(i.next(), p[0]);
    +    QCOMPARE(i.peekNext(), p[1]);
    +    QCOMPARE(i.next(), p[1]);
    +    QCOMPARE(i.next(), p[2]);
    +    QVERIFY(!i.hasNext());
    +    i.toFront();
    +    QCOMPARE(i.next(), p[0]);
    +}//t_qhullpoint_iterator
    +
    +void QhullPoint_test::
    +t_method()
    +{
    +    // advancePoint tested above
    +    RboxPoints rcube("c");
    +    Qhull q(rcube, "");
    +    QhullPoint p = q.firstVertex().point();
    +    double dist= p.distance(q.origin());
    +    QCOMPARE(dist, sqrt(double(2.0+1.0))/2); // half diagonal of unit cube
    +}//t_qhullpoint_iterator
    +
    +void QhullPoint_test::
    +t_io()
    +{
    +    RboxPoints rcube("c");
    +    {
    +        Qhull q(rcube, "");
    +        QhullPoint p= q.beginVertex().point();
    +        ostringstream os;
    +        os << "Point:\n";
    +        os << p;
    +        os << "Point w/ print:\n";
    +        os << p.print(" message ");
    +        os << p.printWithIdentifier(" Point with id and a message ");
    +        cout << os.str();
    +        QString s= QString::fromStdString(os.str());
    +        QCOMPARE(s.count("p"), 2);
    +    }
    +}//t_io
    +
    +}//orgQhull
    +
    +#include "moc/QhullPoint_test.moc"
    diff --git a/xs/src/qhull/src/qhulltest/QhullPoints_test.cpp b/xs/src/qhull/src/qhulltest/QhullPoints_test.cpp
    new file mode 100644
    index 000000000..c2d8347e2
    --- /dev/null
    +++ b/xs/src/qhull/src/qhulltest/QhullPoints_test.cpp
    @@ -0,0 +1,561 @@
    +/****************************************************************************
    +**
    +** Copyright (p) 2009-2015 C.B. Barber. All rights reserved.
    +** $Id: //main/2015/qhull/src/qhulltest/QhullPoints_test.cpp#3 $$Change: 2062 $
    +** $DateTime: 2016/01/17 13:13:18 $$Author: bbarber $
    +**
    +****************************************************************************/
    +
    +//pre-compiled header
    +#include 
    +#include "qhulltest/RoadTest.h" // QT_VERSION
    +
    +#include "libqhullcpp/QhullPoints.h"
    +#include "libqhullcpp/RboxPoints.h"
    +#include "libqhullcpp/Qhull.h"
    +
    +using std::cout;
    +using std::endl;
    +using std::ostringstream;
    +
    +namespace orgQhull {
    +
    +class QhullPoints_test : public RoadTest
    +{
    +    Q_OBJECT
    +
    +#//!\name Test slots
    +private slots:
    +    void cleanup();
    +    void t_construct_q();
    +    void t_construct_qh();
    +    void t_convert();
    +    void t_getset();
    +    void t_element();
    +    void t_iterator();
    +    void t_const_iterator();
    +    void t_search();
    +    void t_points_iterator();
    +    void t_io();
    +};//QhullPoints_test
    +
    +void
    +add_QhullPoints_test()
    +{
    +    new QhullPoints_test();  // RoadTest::s_testcases
    +}
    +
    +//Executed after each testcase
    +void QhullPoints_test::
    +cleanup()
    +{
    +    RoadTest::cleanup();
    +}
    +
    +void QhullPoints_test::
    +t_construct_q()
    +{
    +    Qhull q;
    +    QhullPoints ps(q);
    +    QCOMPARE(ps.dimension(), 0);
    +    QVERIFY(ps.isEmpty());
    +    QCOMPARE(ps.count(), 0);
    +    QCOMPARE(ps.size(), 0u);
    +    QCOMPARE(ps.coordinateCount(), 0);
    +    coordT c[]= {0.0, 1.0, 2.0, 3.0, 4.0, 5.0};
    +    QhullPoints ps2(q);
    +    ps2.defineAs(2, 6, c);
    +    QCOMPARE(ps2.dimension(), 2);
    +    QVERIFY(!ps2.isEmpty());
    +    QCOMPARE(ps2.count(), 3);
    +    QCOMPARE(ps2.size(), 3u);
    +    QCOMPARE(ps2.coordinates(), c);
    +    QhullPoints ps3(q, 2, 6, c);
    +    QCOMPARE(ps3.dimension(), 2);
    +    QVERIFY(!ps3.isEmpty());
    +    QCOMPARE(ps3.coordinates(), ps2.coordinates());
    +    QVERIFY(ps3==ps2);
    +    QVERIFY(ps3!=ps);
    +    QhullPoints ps4= ps3;
    +    QVERIFY(ps4==ps3);
    +    // ps4= ps3; //compiler error
    +    QhullPoints ps5(ps4);
    +    QVERIFY(ps5==ps4);
    +    QVERIFY(!(ps5!=ps4));
    +    coordT c2[]= {0.0, 1.0, 2.0, 3.0, 4.0, 5.0};
    +    QhullPoints ps6(q, 2, 6, c2);
    +    QVERIFY(ps6==ps2);
    +
    +    RboxPoints rbox("c D2");
    +    Qhull q2(rbox, "");
    +    QhullPoints ps8(q2);
    +    QCOMPARE(ps8.dimension(), 2);
    +    QCOMPARE(ps8.count(), 0);
    +    QCOMPARE(ps8.size(), 0u);
    +    QCOMPARE(ps8.coordinateCount(), 0);
    +    coordT c3[]= {0.0, 1.0, 2.0, 3.0, 4.0, 5.0};
    +    QhullPoints ps9(q2, 6, c3);
    +    QCOMPARE(ps9.dimension(), 2);
    +    QCOMPARE(ps9.coordinateCount(), 6);
    +    QCOMPARE(ps9.count(), 3);
    +    QCOMPARE(ps9.coordinates(), c3);
    +    QCOMPARE(ps9, ps2);  // DISTround
    +    c3[1]= 1.0+1e-17;
    +    QCOMPARE(ps9, ps2);  // DISTround
    +    c3[1]= 1.0+1e-15;
    +    QVERIFY(ps9!=ps2);  // DISTround
    +
    +    ps9.defineAs(6, c2);
    +    QCOMPARE(ps9.dimension(), 2);
    +    QCOMPARE(ps9.coordinateCount(), 6);
    +    QCOMPARE(ps9.count(), 3);
    +    QCOMPARE(ps9.coordinates(), c2);
    +}//t_construct_q
    +
    +void QhullPoints_test::
    +t_construct_qh()
    +{
    +    Qhull q;
    +    QhullQh *qh= q.qh();
    +    QhullPoints ps(qh);
    +    QCOMPARE(ps.dimension(), 0);
    +    QVERIFY(ps.isEmpty());
    +    QCOMPARE(ps.count(), 0);
    +    QCOMPARE(ps.size(), 0u);
    +    QCOMPARE(ps.coordinateCount(), 0);
    +    coordT c[]= {0.0, 1.0, 2.0, 3.0, 4.0, 5.0};
    +    QhullPoints ps2(qh);
    +    ps2.defineAs(2, 6, c);
    +    QCOMPARE(ps2.dimension(), 2);
    +    QVERIFY(!ps2.isEmpty());
    +    QCOMPARE(ps2.count(), 3);
    +    QCOMPARE(ps2.size(), 3u);
    +    QCOMPARE(ps2.coordinates(), c);
    +    QhullPoints ps3(qh, 2, 6, c);
    +    QCOMPARE(ps3.dimension(), 2);
    +    QVERIFY(!ps3.isEmpty());
    +    QCOMPARE(ps3.coordinates(), ps2.coordinates());
    +    QVERIFY(ps3==ps2);
    +    QVERIFY(ps3!=ps);
    +    QhullPoints ps4= ps3;
    +    QVERIFY(ps4==ps3);
    +    // ps4= ps3; //compiler error
    +    QhullPoints ps5(ps4);
    +    QVERIFY(ps5==ps4);
    +    QVERIFY(!(ps5!=ps4));
    +    coordT c2[]= {0.0, 1.0, 2.0, 3.0, 4.0, 5.0};
    +    QhullPoints ps6(qh, 2, 6, c2);
    +    QVERIFY(ps6==ps2);
    +
    +    RboxPoints rbox("c D2");
    +    Qhull q2(rbox, "");
    +    QhullPoints ps8(q2.qh());
    +    QCOMPARE(ps8.dimension(), 2);
    +    QCOMPARE(ps8.count(), 0);
    +    QCOMPARE(ps8.size(), 0u);
    +    QCOMPARE(ps8.coordinateCount(), 0);
    +    coordT c3[]= {10.0, 11.0, 12.0, 13.0, 14.0, 15.0};
    +    QhullPoints ps9(q2.qh(), 6, c3);
    +    QCOMPARE(ps9.dimension(), 2);
    +    QCOMPARE(ps9.coordinateCount(), 6);
    +    QCOMPARE(ps9.count(), 3);
    +    QCOMPARE(ps9.coordinates(), c3);
    +    ps9.defineAs(6, c2);
    +    QCOMPARE(ps9.dimension(), 2);
    +    QCOMPARE(ps9.coordinateCount(), 6);
    +    QCOMPARE(ps9.count(), 3);
    +    QCOMPARE(ps9.coordinates(), c2);
    +}//t_construct_qh
    +
    +void QhullPoints_test::
    +t_convert()
    +{
    +    Qhull q;
    +    //defineAs tested above
    +    coordT c[]= {0.0, 1.0, 2.0, 3.0, 4.0, 5.0};
    +    QhullPoints ps(q, 3, 6, c);
    +    QCOMPARE(ps.dimension(), 3);
    +    QCOMPARE(ps.size(), 2u);
    +    const coordT *c2= ps.constData();
    +    QCOMPARE(c, c2);
    +    const coordT *c3= ps.data();
    +    QCOMPARE(c, c3);
    +    coordT *c4= ps.data();
    +    QCOMPARE(c, c4);
    +    std::vector vs= ps.toStdVector();
    +    QCOMPARE(vs.size(), 2u);
    +    QhullPoint p= vs[1];
    +    QCOMPARE(p[2], 5.0);
    +    QList qs= ps.toQList();
    +    QCOMPARE(qs.size(), 2);
    +    QhullPoint p2= qs[1];
    +    QCOMPARE(p2[2], 5.0);
    +}//t_convert
    +
    +void QhullPoints_test::
    +t_getset()
    +{
    +    Qhull q;
    +    //See t_construct for coordinates, count, defineAs, dimension, isempty, ==, !=, size
    +    coordT c[]= {0.0, 1.0, 2.0, 3.0, 4.0, 5.0};
    +    QhullPoints ps(q, 3, 6, c);
    +    QhullPoints ps2(q, 3, 6, c);
    +    QCOMPARE(ps2.dimension(), 3);
    +    QCOMPARE(ps2.coordinates(), c);
    +    QCOMPARE(ps2.count(), 2);
    +    QCOMPARE(ps2.coordinateCount(), 6);
    +    coordT c2[]= {-1.0, -2.0, -3.0, -4.0, -5.0, -6.0};
    +    ps2.defineAs(6, c2);
    +    QCOMPARE(ps2.coordinates(), c2);
    +    QCOMPARE(ps2.count(), 2);
    +    QCOMPARE(ps2.size(), 2u);
    +    QCOMPARE(ps2.dimension(), 3);
    +    QVERIFY(!ps2.isEmpty());
    +    QVERIFY(ps!=ps2);
    +    // ps2= ps; // assignment not available, compiler error
    +    ps2.defineAs(ps);
    +    QVERIFY(ps==ps2);
    +    ps2.setDimension(2);
    +    QCOMPARE(ps2.dimension(), 2);
    +    QCOMPARE(ps2.coordinates(), c);
    +    QVERIFY(!ps2.isEmpty());
    +    QCOMPARE(ps2.count(), 3);
    +    QCOMPARE(ps2.size(), 3u);
    +    QVERIFY(ps!=ps2);
    +    QhullPoints ps3(ps2);
    +    ps3.setDimension(3);
    +    ps3.defineAs(5, c2);
    +    QCOMPARE(ps3.count(), 1);
    +    QCOMPARE(ps3.extraCoordinatesCount(), 2);
    +    QCOMPARE(ps3.extraCoordinates()[0], -4.0);
    +    QVERIFY(ps3.includesCoordinates(ps3.data()));
    +    QVERIFY(ps3.includesCoordinates(ps3.data()+ps3.count()-1));
    +    QVERIFY(!ps3.includesCoordinates(ps3.data()-1));
    +    QVERIFY(!ps3.includesCoordinates(ps3.data()+ps3.coordinateCount()));
    +}//t_getset
    +
    +
    +void QhullPoints_test::
    +t_element()
    +{
    +    Qhull q;
    +    coordT c[]= {0.0, 1.0, 2.0, 3.0, 4.0, 5.0};
    +    QhullPoints ps(q, 2, 6, c);
    +    QCOMPARE(ps.count(), 3);
    +    QhullPoint p(q, 2, c);
    +    QCOMPARE(ps[0], p);
    +    QCOMPARE(ps.at(1), ps[1]);
    +    QCOMPARE(ps.first(), p);
    +    QCOMPARE(ps.front(), ps.first());
    +    QCOMPARE(ps.last(), ps.at(2));
    +    QCOMPARE(ps.back(), ps.last());
    +    QhullPoints ps2= ps.mid(2);
    +    QCOMPARE(ps2.count(), 1);
    +    QhullPoints ps3= ps.mid(3);
    +    QVERIFY(ps3.isEmpty());
    +    QhullPoints ps4= ps.mid(10);
    +    QVERIFY(ps4.isEmpty());
    +    QhullPoints ps5= ps.mid(-1);
    +    QVERIFY(ps5.isEmpty());
    +    QhullPoints ps6= ps.mid(1, 1);
    +    QCOMPARE(ps6.count(), 1);
    +    QCOMPARE(ps6[0], ps[1]);
    +    QhullPoints ps7= ps.mid(1, 10);
    +    QCOMPARE(ps7.count(), 2);
    +    QCOMPARE(ps7[1], ps[2]);
    +    QhullPoint p8(q);
    +    QCOMPARE(ps.value(2), ps[2]);
    +    QCOMPARE(ps.value(-1), p8);
    +    QCOMPARE(ps.value(3), p8);
    +    QCOMPARE(ps.value(3, p), p);
    +    QVERIFY(ps.value(1, p)!=p);
    +    foreach(QhullPoint p9, ps){  // Qt only
    +        QCOMPARE(p9.dimension(), 2);
    +        QVERIFY(p9[0]==0.0 || p9[0]==2.0 || p9[0]==4.0);
    +    }
    +}//t_element
    +
    +void QhullPoints_test::
    +t_iterator()
    +{
    +    Qhull q;
    +    coordT c[]= {0.0, 1.0, 2.0};
    +    QhullPoints ps(q, 1, 3, c);
    +    QCOMPARE(ps.dimension(), 1);
    +    QhullPoints::Iterator i(ps);
    +    QhullPoints::iterator i2= ps.begin();
    +    QVERIFY(i==i2);
    +    QVERIFY(i>=i2);
    +    QVERIFY(i<=i2);
    +    i= ps.begin();
    +    QVERIFY(i==i2);
    +    i2= ps.end();
    +    QVERIFY(i!=i2);
    +    QhullPoint p(i); // QhullPoint is the base class for QhullPoints::iterator
    +    QCOMPARE(p.dimension(), ps.dimension());
    +    QCOMPARE(p.coordinates(), ps.coordinates());
    +    i2--;
    +    QhullPoint p2= *i2;
    +    QCOMPARE(p[0], 0.0);
    +    QCOMPARE(p2[0], 2.0);
    +    QhullPoints::Iterator i5(i2);
    +    QCOMPARE(*i2, *i5);
    +    coordT c3[]= {0.0, -1.0, -2.0};
    +    QhullPoints::Iterator i3(q, 1, c3);
    +    QVERIFY(i!=i3);
    +    QCOMPARE(*i, *i3);
    +
    +    (i3= i)++;
    +    QCOMPARE((*i3)[0], 1.0);
    +    QCOMPARE(i3->dimension(), 1);
    +    QCOMPARE(i3[0][0], 1.0);
    +    QCOMPARE(i3[0], ps[1]);
    +
    +    QVERIFY(i==i);
    +    QVERIFY(i!=i2);
    +    QVERIFY(ii);
    +    QVERIFY(i2>=i);
    +
    +    QhullPoints::ConstIterator i4(q, 1, c);
    +    QVERIFY(i==i4); // iterator COMP const_iterator
    +    QVERIFY(i<=i4);
    +    QVERIFY(i>=i4);
    +    QVERIFY(i4==i); // const_iterator COMP iterator
    +    QVERIFY(i4<=i);
    +    QVERIFY(i4>=i);
    +    QVERIFY(i>=i4);
    +    QVERIFY(i4<=i);
    +    QVERIFY(i2!=i4);
    +    QVERIFY(i2>i4);
    +    QVERIFY(i2>=i4);
    +    QVERIFY(i4!=i2);
    +    QVERIFY(i4i);
    +    QVERIFY(i4>=i);
    +
    +    i= ps.begin();
    +    i2= ps.begin();
    +    QCOMPARE(i, i2++);
    +    QCOMPARE(*i2, ps[1]);
    +    QCOMPARE(++i, i2);
    +    QCOMPARE(i, i2--);
    +    QCOMPARE(i2, ps.begin());
    +    QCOMPARE(--i, i2);
    +    QCOMPARE(i2+=3, ps.end());
    +    QCOMPARE(i2-=3, ps.begin());
    +    QCOMPARE(i2+0, ps.begin());
    +    QCOMPARE(i2+3, ps.end());
    +    i2 += 3;
    +    i= i2-0;
    +    QCOMPARE(i, i2);
    +    i= i2-3;
    +    QCOMPARE(i, ps.begin());
    +    QCOMPARE(i2-i, 3);
    +
    +    //ps.begin end tested above
    +
    +    // QhullPoints is const-only
    +}//t_iterator
    +
    +void QhullPoints_test::
    +t_const_iterator()
    +{
    +    Qhull q;
    +    coordT c[]= {0.0, 1.0, 2.0};
    +    const QhullPoints ps(q, 1, 3, c);
    +    QhullPoints::ConstIterator i(ps);
    +    QhullPoints::const_iterator i2= ps.begin();
    +    QVERIFY(i==i2);
    +    QVERIFY(i>=i2);
    +    QVERIFY(i<=i2);
    +    i= ps.begin();
    +    QVERIFY(i==i2);
    +    i2= ps.end();
    +    QVERIFY(i!=i2);
    +    QhullPoint p(i);
    +    QCOMPARE(p.dimension(), ps.dimension());
    +    QCOMPARE(p.coordinates(), ps.coordinates());
    +    i2--;
    +    QhullPoint p2= *i2;
    +    QCOMPARE(p[0], 0.0);
    +    QCOMPARE(p2[0], 2.0);
    +    QhullPoints::ConstIterator i5(i2);
    +    QCOMPARE(*i2, *i5);
    +    coordT c3[]= {0.0, -1.0, -2.0};
    +    QhullPoints::ConstIterator i3(q, 1, c3);
    +    QVERIFY(i!=i3);
    +    QCOMPARE(*i, *i3);
    +
    +    (i3= i)++;
    +    QCOMPARE((*i3)[0], 1.0);
    +    QCOMPARE(i3->dimension(), 1);
    +    QCOMPARE(i3[0][0], 1.0);
    +    QCOMPARE(i3[0][0], 1.0);
    +    QCOMPARE(i3[0], ps[1]);
    +
    +    QVERIFY(i==i);
    +    QVERIFY(i!=i2);
    +    QVERIFY(ii);
    +    QVERIFY(i2>=i);
    +
    +    // See t_iterator for const_iterator COMP iterator
    +
    +    i= ps.begin();
    +    i2= ps.constBegin();
    +    QCOMPARE(i, i2++);
    +    QCOMPARE(*i2, ps[1]);
    +    QCOMPARE(++i, i2);
    +    QCOMPARE(i, i2--);
    +    QCOMPARE(i2, ps.constBegin());
    +    QCOMPARE(--i, i2);
    +    QCOMPARE(i2+=3, ps.constEnd());
    +    QCOMPARE(i2-=3, ps.constBegin());
    +    QCOMPARE(i2+0, ps.constBegin());
    +    QCOMPARE(i2+3, ps.constEnd());
    +    i2 += 3;
    +    i= i2-0;
    +    QCOMPARE(i, i2);
    +    i= i2-3;
    +    QCOMPARE(i, ps.constBegin());
    +    QCOMPARE(i2-i, 3);
    +
    +    // QhullPoints is const-only
    +}//t_const_iterator
    +
    +
    +void QhullPoints_test::
    +t_search()
    +{
    +    Qhull q;
    +    coordT c[]= {0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 0, 1};
    +    QhullPoints ps(q, 2, 8, c); //2-d array of 4 points
    +    QhullPoint p= ps.first();
    +    QhullPoint p2= ps.last();
    +    QVERIFY(ps.contains(p));
    +    QVERIFY(ps.contains(p2));
    +    QVERIFY(p==p2);
    +    QhullPoint p5= ps[2];
    +    QVERIFY(p!=p5);
    +    QVERIFY(ps.contains(p5));
    +    coordT c2[]= {0.0, 1.0, 2.0, 3.0};
    +    QhullPoint p3(q, 2, c2); //2-d point
    +    QVERIFY(ps.contains(p3));
    +    QhullPoint p4(q, 3, c2); //3-d point
    +    QVERIFY(!ps.contains(p4));
    +    p4.defineAs(2, c); //2-d point
    +    QVERIFY(ps.contains(p4));
    +    p4.defineAs(2, c+1); //2-d point
    +    QVERIFY(!ps.contains(p4));
    +    QhullPoint p6(q, 2, c2+2); //2-d point
    +    QCOMPARE(ps.count(p), 2);
    +    QCOMPARE(ps.count(p2), 2);
    +    QCOMPARE(ps.count(p3), 2);
    +    QCOMPARE(ps.count(p4), 0);
    +    QCOMPARE(ps.count(p6), 1);
    +    QCOMPARE(ps.indexOf(&ps[0][0]), 0);
    +    //QCOMPARE(ps.indexOf(ps.end()), -1); //ps.end() is a QhullPoint which may match
    +    QCOMPARE(ps.indexOf(0), -1);
    +    QCOMPARE(ps.indexOf(&ps[3][0]), 3);
    +    QCOMPARE(ps.indexOf(&ps[3][1], QhullError::NOthrow), 3);
    +    QCOMPARE(ps.indexOf(ps.data()+ps.coordinateCount(), QhullError::NOthrow), -1);
    +    QCOMPARE(ps.indexOf(p), 0);
    +    QCOMPARE(ps.indexOf(p2), 0);
    +    QCOMPARE(ps.indexOf(p3), 0);
    +    QCOMPARE(ps.indexOf(p4), -1);
    +    QCOMPARE(ps.indexOf(p5), 2);
    +    QCOMPARE(ps.indexOf(p6), 1);
    +    QCOMPARE(ps.lastIndexOf(p), 3);
    +    QCOMPARE(ps.lastIndexOf(p4), -1);
    +    QCOMPARE(ps.lastIndexOf(p6), 1);
    +    QhullPoints ps3(q);
    +    QCOMPARE(ps3.indexOf(ps3.data()), -1);
    +    QCOMPARE(ps3.indexOf(ps3.data()+1, QhullError::NOthrow), -1);
    +    QCOMPARE(ps3.indexOf(p), -1);
    +    QCOMPARE(ps3.lastIndexOf(p), -1);
    +    QhullPoints ps4(q, 2, 0, c);
    +    QCOMPARE(ps4.indexOf(p), -1);
    +    QCOMPARE(ps4.lastIndexOf(p), -1);
    +}//t_search
    +
    +void QhullPoints_test::
    +t_points_iterator()
    +{
    +    Qhull q;
    +    coordT c2[]= {0.0};
    +    QhullPoints ps2(q, 0, 0, c2); // 0-dimensional
    +    QhullPointsIterator i2= ps2;
    +    QVERIFY(!i2.hasNext());
    +    QVERIFY(!i2.hasPrevious());
    +    i2.toBack();
    +    QVERIFY(!i2.hasNext());
    +    QVERIFY(!i2.hasPrevious());
    +
    +    coordT c[]= {0.0, 1.0, 2.0, 3.0, 4.0, 5.0};
    +    QhullPoints ps(q, 3, 6, c); // 3-dimensional
    +    QhullPointsIterator i(ps);
    +    i2= ps;
    +    QVERIFY(i2.hasNext());
    +    QVERIFY(!i2.hasPrevious());
    +    QVERIFY(i.hasNext());
    +    QVERIFY(!i.hasPrevious());
    +    i2.toBack();
    +    i.toFront();
    +    QVERIFY(!i2.hasNext());
    +    QVERIFY(i2.hasPrevious());
    +    QVERIFY(i.hasNext());
    +    QVERIFY(!i.hasPrevious());
    +
    +    QhullPoint p= ps[0];
    +    QhullPoint p2(ps[0]);
    +    QCOMPARE(p, p2);
    +    QVERIFY(p==p2);
    +    QhullPoint p3(ps[1]);
    + // p2[0]= 0.0;
    +    QVERIFY(p==p2);
    +    QCOMPARE(i2.peekPrevious(), p3);
    +    QCOMPARE(i2.previous(), p3);
    +    QCOMPARE(i2.previous(), p);
    +    QVERIFY(!i2.hasPrevious());
    +    QCOMPARE(i.peekNext(), p);
    +    // i.peekNext()= 1.0; // compiler error
    +    QCOMPARE(i.next(), p);
    +    QCOMPARE(i.peekNext(), p3);
    +    QCOMPARE(i.next(), p3);
    +    QVERIFY(!i.hasNext());
    +    i.toFront();
    +    QCOMPARE(i.next(), p);
    +}//t_points_iterator
    +
    +void QhullPoints_test::
    +t_io()
    +{
    +    Qhull q;
    +    QhullPoints ps(q);
    +    ostringstream os;
    +    os << "Empty QhullPoints\n" << ps << endl;
    +    coordT c[]= {0.0, 1.0, 2.0, 3.0, 4.0, 5.0};
    +    QhullPoints ps2(q, 3, 6, c); // 3-dimensional explicit
    +    os << "QhullPoints from c[]\n" << ps2 << endl;
    +
    +    RboxPoints rcube("c");
    +    Qhull q2(rcube,"Qt QR0");  // triangulation of rotated unit cube
    +    QhullPoints ps3= q2.points();
    +    os << "QhullPoints\n" << ps3;
    +    os << ps3.print("message\n");
    +    os << ps3.printWithIdentifier("w/ identifiers\n");
    +    cout << os.str();
    +    QString s= QString::fromStdString(os.str());
    +    QCOMPARE(s.count("p"), 8+1);
    +}//t_io
    +
    +}//orgQhull
    +
    +#include "moc/QhullPoints_test.moc"
    diff --git a/xs/src/qhull/src/qhulltest/QhullRidge_test.cpp b/xs/src/qhull/src/qhulltest/QhullRidge_test.cpp
    new file mode 100644
    index 000000000..420a7f06d
    --- /dev/null
    +++ b/xs/src/qhull/src/qhulltest/QhullRidge_test.cpp
    @@ -0,0 +1,159 @@
    +/****************************************************************************
    +**
    +** Copyright (c) 2008-2015 C.B. Barber. All rights reserved.
    +** $Id: //main/2015/qhull/src/qhulltest/QhullRidge_test.cpp#3 $$Change: 2062 $
    +** $DateTime: 2016/01/17 13:13:18 $$Author: bbarber $
    +**
    +****************************************************************************/
    +
    +//pre-compiled headers
    +#include 
    +#include "RoadTest.h" // QT_VERSION
    +
    +#include "libqhullcpp/QhullRidge.h"
    +#include "libqhullcpp/QhullError.h"
    +#include "libqhullcpp/RboxPoints.h"
    +#include "libqhullcpp/QhullFacet.h"
    +#include "libqhullcpp/Qhull.h"
    +
    +using std::cout;
    +using std::endl;
    +using std::ostringstream;
    +using std::ostream;
    +using std::string;
    +
    +namespace orgQhull {
    +
    +class QhullRidge_test : public RoadTest
    +{
    +    Q_OBJECT
    +
    +#//!\name Test slots
    +private slots:
    +    void cleanup();
    +    void t_construct();
    +    void t_getSet();
    +    void t_foreach();
    +    void t_io();
    +};//QhullRidge_test
    +
    +void
    +add_QhullRidge_test()
    +{
    +    new QhullRidge_test();  // RoadTest::s_testcases
    +}
    +
    +//Executed after each testcase
    +void QhullRidge_test::
    +cleanup()
    +{
    +    RoadTest::cleanup();
    +}
    +
    +void QhullRidge_test::
    +t_construct()
    +{
    +    // Qhull.runQhull() constructs QhullFacets as facetT
    +    RboxPoints rcube("c");
    +    Qhull q(rcube,"QR0");  // triangulation of rotated unit cube
    +    QhullRidge r(q);
    +    QVERIFY(!r.isValid());
    +    QCOMPARE(r.dimension(),2);
    +    QhullFacet f(q.firstFacet());
    +    QhullRidgeSet rs(f.ridges());
    +    QVERIFY(!rs.isEmpty()); // Simplicial facets do not have ridges()
    +    QhullRidge r2(rs.first());
    +    QCOMPARE(r2.dimension(), 2); // One dimension lower than the facet
    +    r= r2;
    +    QVERIFY(r.isValid());
    +    QCOMPARE(r.dimension(), 2);
    +    QhullRidge r3(q, r2.getRidgeT());
    +    QCOMPARE(r,r3);
    +    QhullRidge r4(q, r2.getBaseT());
    +    QCOMPARE(r,r4);
    +    QhullRidge r5= r2; // copy constructor
    +    QVERIFY(r5==r2);
    +    QVERIFY(r5==r);
    +}//t_construct
    +
    +void QhullRidge_test::
    +t_getSet()
    +{
    +    RboxPoints rcube("c");
    +    {
    +        Qhull q(rcube,"QR0");  // triangulation of rotated unit cube
    +        QCOMPARE(q.facetCount(), 6);
    +        QCOMPARE(q.vertexCount(), 8);
    +        QhullFacet f(q.firstFacet());
    +        QhullRidgeSet rs= f.ridges();
    +        QhullRidgeSetIterator i(rs);
    +        while(i.hasNext()){
    +            const QhullRidge r= i.next();
    +            cout << r.id() << endl;
    +            QVERIFY(r.bottomFacet()!=r.topFacet());
    +            QCOMPARE(r.dimension(), 2); // Ridge one-dimension less than facet
    +            QVERIFY(r.id()>=0 && r.id()<9*27);
    +            QVERIFY(r.isValid());
    +            QVERIFY(r==r);
    +            QVERIFY(r==i.peekPrevious());
    +            QCOMPARE(r.otherFacet(r.bottomFacet()),r.topFacet());
    +            QCOMPARE(r.otherFacet(r.topFacet()),r.bottomFacet());
    +        }
    +    }
    +}//t_getSet
    +
    +void QhullRidge_test::
    +t_foreach()
    +{
    +    RboxPoints rcube("c");  // cube
    +    {
    +        Qhull q(rcube, "QR0"); // rotated cube
    +        QhullFacet f(q.firstFacet());
    +        foreach (const QhullRidge &r, f.ridges()){  // Qt only
    +            QhullVertexSet vs= r.vertices();
    +            QCOMPARE(vs.count(), 2);
    +            foreach (const QhullVertex &v, vs){  // Qt only
    +                QVERIFY(f.vertices().contains(v));
    +            }
    +        }
    +        QhullRidgeSet rs= f.ridges();
    +        QhullRidge r= rs.first();
    +        QhullRidge r2= r;
    +        QList vs;
    +        int count= 0;
    +        while(!count || r2!=r){
    +            ++count;
    +            QhullVertex v(q);
    +            QVERIFY2(r2.hasNextRidge3d(f),"A cube should only have non-simplicial facets.");
    +            QhullRidge r3= r2.nextRidge3d(f, &v);
    +            QVERIFY(!vs.contains(v));
    +            vs << v;
    +            r2= r2.nextRidge3d(f);
    +            QCOMPARE(r3, r2);
    +        }
    +        QCOMPARE(vs.count(), rs.count());
    +        QCOMPARE(count, rs.count());
    +    }
    +}//t_foreach
    +
    +void QhullRidge_test::
    +t_io()
    +{
    +    RboxPoints rcube("c");
    +    {
    +        Qhull q(rcube, "");
    +        QhullFacet f(q.firstFacet());
    +        QhullRidgeSet rs= f.ridges();
    +        QhullRidge r= rs.first();
    +        ostringstream os;
    +        os << "Ridges\n" << rs << "Ridge\n" << r;
    +        os << r.print("\nRidge with message");
    +        cout << os.str();
    +        QString s= QString::fromStdString(os.str());
    +        QCOMPARE(s.count(" r"), 6);
    +    }
    +}//t_io
    +
    +}//orgQhull
    +
    +#include "moc/QhullRidge_test.moc"
    diff --git a/xs/src/qhull/src/qhulltest/QhullSet_test.cpp b/xs/src/qhull/src/qhulltest/QhullSet_test.cpp
    new file mode 100644
    index 000000000..87fcf4acf
    --- /dev/null
    +++ b/xs/src/qhull/src/qhulltest/QhullSet_test.cpp
    @@ -0,0 +1,434 @@
    +/****************************************************************************
    +**
    +** Copyright (c) 2009-2015 C.B. Barber. All rights reserved.
    +** $Id: //main/2015/qhull/src/qhulltest/QhullSet_test.cpp#3 $$Change: 2062 $
    +** $DateTime: 2016/01/17 13:13:18 $$Author: bbarber $
    +**
    +****************************************************************************/
    +
    +//pre-compiled headers
    +#include 
    +#include "RoadTest.h" // QT_VERSION
    +
    +#include "libqhullcpp/QhullRidge.h"
    +#include "libqhullcpp/QhullFacetSet.h"
    +#include "libqhullcpp/Qhull.h"
    +#include "libqhullcpp/RboxPoints.h"
    +
    +#include 
    +
    +using std::cout;
    +using std::endl;
    +
    +namespace orgQhull {
    +
    +class QhullSet_test : public RoadTest
    +{
    +    Q_OBJECT
    +
    +#//!\name Test slots
    +private slots:
    +    void cleanup();
    +    void t_qhullsetbase();
    +    void t_convert();
    +    void t_element();
    +    void t_search();
    +    void t_iterator();
    +    void t_const_iterator();
    +    void t_qhullset_iterator();
    +    void t_io();
    +};//QhullSet_test
    +
    +void
    +add_QhullSet_test()
    +{
    +    new QhullSet_test();  // RoadTest::s_testcases
    +}
    +
    +//Executed after each testcase
    +void QhullSet_test::
    +cleanup()
    +{
    +    RoadTest::cleanup();
    +}
    +
    +// Test QhullFacetSet and QhullSet.
    +// Use QhullRidgeSet to test methods overloaded by QhullFacetSet
    +
    +void QhullSet_test::
    +t_qhullsetbase()
    +{
    +    RboxPoints rcube("c");
    +    {
    +        Qhull q(rcube,"QR0");  // triangulation of rotated unit cube
    +        // Fake an empty set.  Default constructor not defined.  No memory allocation.
    +        QhullFacet f4 = q.beginFacet();
    +        QhullFacetSet fs = f4.neighborFacets();
    +        fs.defineAs(q.qh()->other_points); // Force an empty set
    +        QVERIFY(fs.isEmpty());
    +        QCOMPARE(fs.count(), 0);
    +        QCOMPARE(fs.size(), 0u);
    +        QCOMPARE(fs.begin(), fs.end()); // beginPointer(), endPointer()
    +        QVERIFY(QhullSetBase::isEmpty(fs.getSetT()));
    +
    +        QhullRidgeSet rs = f4.ridges();
    +        QVERIFY(!rs.isEmpty());
    +        QCOMPARE(rs.count(), 4);
    +        QCOMPARE(rs.size(), 4u);
    +        QVERIFY(rs.begin()!=rs.end());
    +        QVERIFY(!QhullSetBase::isEmpty(rs.getSetT()));
    +        QhullRidgeSet rs2= rs; // copy constructor
    +        // rs= rs2; // disabled.  Would not copy ridges
    +        QCOMPARE(rs2, rs);
    +
    +        QCOMPARE(q.facetCount(), 6);
    +        QhullFacet f = q.beginFacet();
    +        QhullFacetSet fs2 = f.neighborFacets();
    +        QCOMPARE(fs2.count(), 4);
    +        QCOMPARE(fs2.size(), 4u);
    +        QVERIFY(!fs2.isEmpty());
    +        QVERIFY(!QhullSetBase::isEmpty(fs2.getSetT()));
    +        QVERIFY(fs!=fs2);
    +        setT *s= fs2.getSetT();
    +        fs.defineAs(s);
    +        QVERIFY(fs==fs2);
    +        QCOMPARE(fs[1], fs2[1]); // elementPointer
    +        QhullFacetSet fs3(fs2);
    +        QVERIFY(fs3==fs);
    +        // fs= fs2; // disabled.  Would not copy facets
    +        QhullFacetSet fs4= fs2; // copy constructor
    +        QVERIFY(fs4==fs2);
    +    }
    +}//t_qhullsetbase
    +
    +// constructors tested by t_qhullsetbase
    +
    +void QhullSet_test::
    +t_convert()
    +{
    +    RboxPoints rcube("c");
    +    {
    +        Qhull q(rcube,"QR0");  // rotated unit cube
    +        QhullFacet f= q.firstFacet();
    +        f= f.next();
    +        QhullRidgeSet rs= f.ridges();
    +        QCOMPARE(rs.count(),4);
    +        std::vector rv= rs.toStdVector();
    +        QCOMPARE(rv.size(), 4u);
    +        QList rv2= rs.toQList();
    +        QCOMPARE(rv2.size(), 4);
    +        std::vector::iterator i= rv.begin();
    +        foreach(QhullRidge r, rv2){  // Qt only
    +            QhullRidge r2= *i++;
    +            QCOMPARE(r, r2);
    +        }
    +
    +        Qhull q2(rcube,"Qt QR0");  // triangulation of rotated unit cube
    +        QCOMPARE(q2.facetCount(), 12);
    +        QhullFacet f2 = q2.beginFacet();
    +        QhullFacetSet fs = f2.neighborFacets();
    +        QCOMPARE(fs.size(), 3U);
    +        std::vector vs= fs.toStdVector();
    +        QCOMPARE(vs.size(), fs.size());
    +        for(int k= fs.count(); k--; ){
    +            QCOMPARE(vs[k], fs[k]);
    +        }
    +        QList qv= fs.toQList();
    +        QCOMPARE(qv.count(), fs.count());
    +        for(int k= fs.count(); k--; ){
    +            QCOMPARE(qv[k], fs[k]);
    +        }
    +    }
    +}//t_convert
    +
    +//ReadOnly (count, isEmpty) tested by t_convert
    +//  operator== tested by t_search
    +
    +void QhullSet_test::
    +t_element()
    +{
    +    RboxPoints rcube("c");
    +    Qhull q(rcube,"QR0");  // rotated unit cube
    +    QhullFacet f = q.beginFacet();
    +    QhullFacetSet fs = f.neighborFacets();
    +
    +    QCOMPARE(fs.at(1), fs[1]);
    +    QCOMPARE(fs.first(), fs[0]);
    +    QCOMPARE(fs.front(), fs.first());
    +    QCOMPARE(fs.last(), fs.at(3));
    +    QCOMPARE(fs.back(), fs.last());
    +    facetT **d = fs.data();
    +    facetT * const *d2= fs.data();
    +    facetT * const *d3= fs.constData();
    +    QVERIFY(d==d2);
    +    QVERIFY(d2==d3);
    +    QCOMPARE(QhullFacet(q, *d), fs.first());
    +    QhullFacetSet::iterator i(q.qh(), d+4);
    +    QCOMPARE(i, fs.end());
    +    QCOMPARE(d[4], static_cast(0));
    +    QhullFacet f4(q, d[4]);
    +    QVERIFY(!f4.isValid());
    +    QCOMPARE(fs.second(), fs[1]);
    +    const QhullFacet f2= fs.second();
    +    QVERIFY(f2==fs[1]);
    +    const QhullFacet f3= fs[1];
    +    QCOMPARE(f2, f3);
    +
    +    QCOMPARE(fs.value(2), fs[2]);
    +    QCOMPARE(fs.value(-1), QhullFacet());
    +    QCOMPARE(fs.value(10), QhullFacet());
    +    QCOMPARE(fs.value(2, f), fs[2]);
    +    QCOMPARE(fs.value(4, f), f);
    +    // mid() not available (read-only)
    +}//t_element
    +
    +void QhullSet_test::
    +t_search()
    +{
    +    RboxPoints rcube("c");
    +    Qhull q(rcube,"QR0");  // rotated unit cube
    +    QhullFacet f = q.beginFacet();
    +    QhullFacetSet fs = f.neighborFacets();
    +    QhullFacet f2= *fs.begin();
    +    QhullFacet f3= fs.last();
    +    QVERIFY(fs.contains(f2));
    +    QVERIFY(fs.contains(f3));
    +    QVERIFY(!fs.contains(f));
    +
    +    QhullFacetSet fs2= f2.neighborFacets();
    +    QVERIFY(fs==fs);
    +    QVERIFY(fs!=fs2);
    +    QCOMPARE(fs.count(f2), 1);
    +    QCOMPARE(fs.count(f3), 1);
    +    QCOMPARE(fs.count(f), 0);
    +    QCOMPARE(fs.indexOf(f2), 0);
    +    QCOMPARE(fs.indexOf(f3), 3);
    +    QCOMPARE(fs.indexOf(f), -1);
    +    QCOMPARE(fs.lastIndexOf(f2), 0);
    +    QCOMPARE(fs.lastIndexOf(f3), 3);
    +    QCOMPARE(fs.lastIndexOf(f), -1);
    +}//t_search
    +
    +void QhullSet_test::
    +t_iterator()
    +{
    +    RboxPoints rcube("c");
    +    {
    +        Qhull q(rcube,"QR0");  // rotated unit cube
    +        QhullFacet f = q.beginFacet();
    +        QhullFacetSet fs = f.neighborFacets();
    +        QhullFacetSet::Iterator i= fs.begin();
    +        QhullFacetSet::iterator i2= fs.begin();
    +        QVERIFY(i==i2);
    +        QVERIFY(i>=i2);
    +        QVERIFY(i<=i2);
    +        i= fs.begin();
    +        QVERIFY(i==i2);
    +        i2= fs.end();
    +        QVERIFY(i!=i2);
    +        QhullFacet f3(*i);
    +        i2--;
    +        QhullFacet f2= *i2;
    +        QCOMPARE(f3.id(), fs[0].id());
    +        QCOMPARE(f2.id(), fs[3].id());
    +        QhullFacetSet::Iterator i3(i2);
    +        QCOMPARE(*i2, *i3);
    +
    +        (i3= i)++;
    +        QCOMPARE((*i3).id(), fs[1].id());
    +        QVERIFY(i==i);
    +        QVERIFY(i!=i2);
    +        QVERIFY(ii);
    +        QVERIFY(i2>=i);
    +
    +        QhullFacetSet::ConstIterator i4= fs.begin();
    +        QVERIFY(i==i4); // iterator COMP const_iterator
    +        QVERIFY(i<=i4);
    +        QVERIFY(i>=i4);
    +        QVERIFY(i4==i); // const_iterator COMP iterator
    +        QVERIFY(i4<=i);
    +        QVERIFY(i4>=i);
    +        QVERIFY(i>=i4);
    +        QVERIFY(i4<=i);
    +        QVERIFY(i2!=i4);
    +        QVERIFY(i2>i4);
    +        QVERIFY(i2>=i4);
    +        QVERIFY(i4!=i2);
    +        QVERIFY(i4i);
    +        QVERIFY(i4>=i);
    +
    +        i= fs.begin();
    +        i2= fs.begin();
    +        QCOMPARE(i, i2++);
    +        QCOMPARE(*i2, fs[1]);
    +        QCOMPARE(++i, i2);
    +        QCOMPARE(i, i2--);
    +        QCOMPARE(i2, fs.begin());
    +        QCOMPARE(--i, i2);
    +        QCOMPARE(i2 += 4, fs.end());
    +        QCOMPARE(i2 -= 4, fs.begin());
    +        QCOMPARE(i2+0, fs.begin());
    +        QCOMPARE(i2+4, fs.end());
    +        i2 += 4;
    +        i= i2-0;
    +        QCOMPARE(i, i2);
    +        i= i2-4;
    +        QCOMPARE(i, fs.begin());
    +        QCOMPARE(i2-i, 4);
    +
    +        //fs.begin end tested above
    +
    +        // QhullFacetSet is const-only
    +    }
    +}//t_iterator
    +
    +void QhullSet_test::
    +t_const_iterator()
    +{
    +    RboxPoints rcube("c");
    +    {
    +        Qhull q(rcube,"QR0");  // rotated unit cube
    +        QhullFacet f = q.beginFacet();
    +        QhullFacetSet fs = f.neighborFacets();
    +        QhullFacetSet::ConstIterator i= fs.begin();
    +        QhullFacetSet::const_iterator i2= fs.begin();
    +        QVERIFY(i==i2);
    +        QVERIFY(i>=i2);
    +        QVERIFY(i<=i2);
    +        i= fs.begin();
    +        QVERIFY(i==i2);
    +        i2= fs.end();
    +        QVERIFY(i!=i2);
    +        QhullFacet f3(*i);
    +        i2--;
    +        QhullFacet f2= *i2;
    +        QCOMPARE(f3.id(), fs[0].id());
    +        QCOMPARE(f2.id(), fs[3].id());
    +        QhullFacetSet::ConstIterator i3(i2);
    +        QCOMPARE(*i2, *i3);
    +
    +        (i3= i)++;
    +        QCOMPARE((*i3).id(), fs[1].id());
    +        QVERIFY(i==i);
    +        QVERIFY(i!=i2);
    +        QVERIFY(ii);
    +        QVERIFY(i2>=i);
    +
    +        // See t_iterator for const_iterator COMP iterator
    +
    +        i= fs.begin();
    +        i2= fs.constBegin();
    +        QCOMPARE(i, i2++);
    +        QCOMPARE(*i2, fs[1]);
    +        QCOMPARE(++i, i2);
    +        QCOMPARE(i, i2--);
    +        QCOMPARE(i2, fs.constBegin());
    +        QCOMPARE(--i, i2);
    +        QCOMPARE(i2+=4, fs.constEnd());
    +        QCOMPARE(i2-=4, fs.constBegin());
    +        QCOMPARE(i2+0, fs.constBegin());
    +        QCOMPARE(i2+4, fs.constEnd());
    +        i2 += 4;
    +        i= i2-0;
    +        QCOMPARE(i, i2);
    +        i= i2-4;
    +        QCOMPARE(i, fs.constBegin());
    +        QCOMPARE(i2-i, 4);
    +
    +        // QhullFacetSet is const-only
    +    }
    +}//t_const_iterator
    +
    +void QhullSet_test::
    +t_qhullset_iterator()
    +{
    +    RboxPoints rcube("c");
    +    Qhull q(rcube,"QR0");  // rotated unit cube
    +    // Fake an empty set.  Default constructor not defined.  No memory allocation.
    +    QhullFacet f = q.beginFacet();
    +    QhullFacetSet fs = f.neighborFacets();
    +    fs.defineAs(q.qh()->other_points);
    +    QhullFacetSetIterator i(fs);
    +    QCOMPARE(fs.count(), 0);
    +    QVERIFY(!i.hasNext());
    +    QVERIFY(!i.hasPrevious());
    +    i.toBack();
    +    QVERIFY(!i.hasNext());
    +    QVERIFY(!i.hasPrevious());
    +
    +    QhullFacet f2 = q.beginFacet();
    +    QhullFacetSet fs2 = f2.neighborFacets();
    +    QhullFacetSetIterator i2(fs2);
    +    QCOMPARE(fs2.count(), 4);
    +    i= fs2;
    +    QVERIFY(i2.hasNext());
    +    QVERIFY(!i2.hasPrevious());
    +    QVERIFY(i.hasNext());
    +    QVERIFY(!i.hasPrevious());
    +    i2.toBack();
    +    i.toFront();
    +    QVERIFY(!i2.hasNext());
    +    QVERIFY(i2.hasPrevious());
    +    QVERIFY(i.hasNext());
    +    QVERIFY(!i.hasPrevious());
    +
    +    // i at front, i2 at end/back, 4 neighbors
    +    QhullFacetSet fs3 = f2.neighborFacets(); // same as fs2
    +    QhullFacet f3(fs2[0]);
    +    QhullFacet f4= fs3[0];
    +    QCOMPARE(f3, f4);
    +    QVERIFY(f3==f4);
    +    QhullFacet f5(fs3[1]);
    +    QVERIFY(f4!=f5);
    +    QhullFacet f6(fs3[2]);
    +    QhullFacet f7(fs3[3]);
    +    QCOMPARE(i2.peekPrevious(), f7);
    +    QCOMPARE(i2.previous(), f7);
    +    QCOMPARE(i2.previous(), f6);
    +    QCOMPARE(i2.previous(), f5);
    +    QCOMPARE(i2.previous(), f4);
    +    QVERIFY(!i2.hasPrevious());
    +    QCOMPARE(i.peekNext(), f4);
    +    // i.peekNext()= 1.0; // compiler error
    +    QCOMPARE(i.next(), f4);
    +    QCOMPARE(i.peekNext(), f5);
    +    QCOMPARE(i.next(), f5);
    +    QCOMPARE(i.next(), f6);
    +    QCOMPARE(i.next(), f7);
    +    QVERIFY(!i.hasNext());
    +    i.toFront();
    +    QCOMPARE(i.next(), f4);
    +}//t_qhullset_iterator
    +
    +void QhullSet_test::
    +t_io()
    +{
    +    RboxPoints rcube("c");
    +    Qhull q(rcube,"QR0");  // rotated unit cube
    +    // Fake an empty set.  Default constructor not defined.  No memory allocation.
    +    QhullFacet f= q.beginFacet();
    +    QhullFacetSet fs= f.neighborFacets();
    +    fs.defineAs(q.qh()->other_points);
    +    cout << "INFO:     empty set" << fs << std::endl;
    +    QhullFacet f2= q.beginFacet();
    +    QhullFacetSet fs2= f2.neighborFacets();
    +    cout << "INFO:   Neighboring facets\n";
    +    cout << fs2 << std::endl;
    +
    +    QhullRidgeSet rs= f.ridges();
    +    cout << "INFO:   Ridges for a facet\n";
    +    cout << rs << std::endl;
    +}//t_io
    +
    +}//namespace orgQhull
    +
    +#include "moc/QhullSet_test.moc"
    diff --git a/xs/src/qhull/src/qhulltest/QhullVertexSet_test.cpp b/xs/src/qhull/src/qhulltest/QhullVertexSet_test.cpp
    new file mode 100644
    index 000000000..41caacd29
    --- /dev/null
    +++ b/xs/src/qhull/src/qhulltest/QhullVertexSet_test.cpp
    @@ -0,0 +1,152 @@
    +/****************************************************************************
    +**
    +** Copyright (c) 2008-2015 C.B. Barber. All rights reserved.
    +** $Id: //main/2015/qhull/src/qhulltest/QhullVertexSet_test.cpp#3 $$Change: 2062 $
    +** $DateTime: 2016/01/17 13:13:18 $$Author: bbarber $
    +**
    +****************************************************************************/
    +
    +//pre-compiled headers
    +#include 
    +#include "qhulltest/RoadTest.h" // QT_VERSION
    +
    +#include "libqhullcpp/QhullVertexSet.h"
    +#include "libqhullcpp/QhullVertex.h"
    +#include "libqhullcpp/Qhull.h"
    +#include "libqhullcpp/QhullError.h"
    +#include "libqhullcpp/QhullFacet.h"
    +#include "libqhullcpp/RboxPoints.h"
    +
    +using std::cout;
    +using std::endl;
    +using std::ostringstream;
    +using std::ostream;
    +using std::string;
    +
    +namespace orgQhull {
    +
    +class QhullVertexSet_test : public RoadTest
    +{
    +    Q_OBJECT
    +
    +#//!\name Test slots
    +private slots:
    +    void cleanup();
    +    void t_construct();
    +    void t_convert();
    +    void t_readonly();
    +    void t_foreach();
    +    void t_io();
    +};//QhullVertexSet_test
    +
    +void
    +add_QhullVertexSet_test()
    +{
    +    new QhullVertexSet_test();  // RoadTest::s_testcases
    +}
    +
    +//Executed after each testcase
    +void QhullVertexSet_test::
    +cleanup()
    +{
    +    RoadTest::cleanup();
    +}
    +
    +void QhullVertexSet_test::
    +t_construct()
    +{
    +    RboxPoints rcube("c");
    +    Qhull q(rcube,"QR0");  // rotated unit cube
    +    cout << "INFO   : Cube rotated by QR" << q.rotateRandom() << std::endl;
    +    QhullFacet f= q.firstFacet();
    +    QhullVertexSet vs= f.vertices();
    +    QVERIFY(!vs.isEmpty());
    +    QCOMPARE(vs.count(),4);
    +    QhullVertexSet vs4= vs; // copy constructor
    +    QVERIFY(vs4==vs);
    +    QhullVertexSet vs3(q, q.qh()->del_vertices);
    +    QVERIFY(vs3.isEmpty());
    +}//t_construct
    +
    +void QhullVertexSet_test::
    +t_convert()
    +{
    +    RboxPoints rcube("c");
    +    Qhull q(rcube,"QR0 QV2");  // rotated unit cube with "good" facets adjacent to point 0
    +    cout << "INFO   : Cube rotated by QR" << q.rotateRandom() << std::endl;
    +    QhullFacet f= q.firstFacet();
    +    QhullVertexSet vs2= f.vertices();
    +    QCOMPARE(vs2.count(),4);
    +    std::vector fv= vs2.toStdVector();
    +    QCOMPARE(fv.size(), 4u);
    +    QList fv2= vs2.toQList();
    +    QCOMPARE(fv2.size(), 4);
    +    std::vector fv3= vs2.toStdVector();
    +    QCOMPARE(fv3.size(), 4u);
    +    QList fv4= vs2.toQList();
    +    QCOMPARE(fv4.size(), 4);
    +}//t_convert
    +
    +//! Spot check properties and read-only.  See QhullSet_test
    +void QhullVertexSet_test::
    +t_readonly()
    +{
    +    RboxPoints rcube("c");
    +    Qhull q(rcube,"QV0");  // good facets are adjacent to point 0
    +    QhullVertexSet vs= q.firstFacet().vertices();
    +    QCOMPARE(vs.count(), 4);
    +    QCOMPARE(vs.count(), 4);
    +    QhullVertex v= vs.first();
    +    QhullVertex v2= vs.last();
    +    QVERIFY(vs.contains(v));
    +    QVERIFY(vs.contains(v2));
    +}//t_readonly
    +
    +void QhullVertexSet_test::
    +t_foreach()
    +{
    +    RboxPoints rcube("c");
    +    // Spot check predicates and accessors.  See QhullLinkedList_test
    +    Qhull q(rcube,"QR0");  // rotated unit cube
    +    cout << "INFO   : Cube rotated by QR" << q.rotateRandom() << std::endl;
    +    QhullVertexSet vs= q.firstFacet().vertices();
    +    QVERIFY(vs.contains(vs.first()));
    +    QVERIFY(vs.contains(vs.last()));
    +    QCOMPARE(vs.first(), *vs.begin());
    +    QCOMPARE(*(vs.end()-1), vs.last());
    +}//t_foreach
    +
    +void QhullVertexSet_test::
    +t_io()
    +{
    +    RboxPoints rcube("c");
    +    {
    +        Qhull q(rcube,"QR0 QV0");   // good facets are adjacent to point 0
    +        cout << "INFO   : Cube rotated by QR" << q.rotateRandom() << std::endl;
    +        QhullVertexSet vs= q.firstFacet().vertices();
    +        ostringstream os;
    +        os << vs.print("Vertices of first facet with point 0");
    +        os << vs.printIdentifiers("\nVertex identifiers: ");
    +        cout<< os.str();
    +        QString vertices= QString::fromStdString(os.str());
    +        QCOMPARE(vertices.count(QRegExp(" v[0-9]")), 4);
    +    }
    +}//t_io
    +
    +#ifdef QHULL_USES_QT
    +QList QhullVertexSet::
    +toQList() const
    +{
    +    QhullSetIterator i(*this);
    +    QList vs;
    +    while(i.hasNext()){
    +        QhullVertex v= i.next();
    +        vs.append(v);
    +    }
    +    return vs;
    +}//toQList
    +#endif //QHULL_USES_QT
    +
    +}//orgQhull
    +
    +#include "moc/QhullVertexSet_test.moc"
    diff --git a/xs/src/qhull/src/qhulltest/QhullVertex_test.cpp b/xs/src/qhull/src/qhulltest/QhullVertex_test.cpp
    new file mode 100644
    index 000000000..fb6ec9640
    --- /dev/null
    +++ b/xs/src/qhull/src/qhulltest/QhullVertex_test.cpp
    @@ -0,0 +1,184 @@
    +/****************************************************************************
    +**
    +** Copyright (c) 2008-2015 C.B. Barber. All rights reserved.
    +** $Id: //main/2015/qhull/src/qhulltest/QhullVertex_test.cpp#3 $$Change: 2062 $
    +** $DateTime: 2016/01/17 13:13:18 $$Author: bbarber $
    +**
    +****************************************************************************/
    +
    +//pre-compiled headers
    +#include 
    +#include "RoadTest.h" // QT_VERSION
    +
    +#include "libqhullcpp/QhullVertex.h"
    +#include "libqhullcpp/Coordinates.h"
    +#include "libqhullcpp/QhullError.h"
    +#include "libqhullcpp/RboxPoints.h"
    +#include "libqhullcpp/QhullFacet.h"
    +#include "libqhullcpp/QhullFacetSet.h"
    +#include "libqhullcpp/QhullVertexSet.h"
    +#include "libqhullcpp/Qhull.h"
    +
    +using std::cout;
    +using std::endl;
    +using std::ostringstream;
    +using std::ostream;
    +using std::string;
    +
    +namespace orgQhull {
    +
    +class QhullVertex_test : public RoadTest
    +{
    +    Q_OBJECT
    +
    +#//!\name Test slots
    +private slots:
    +    void cleanup();
    +    void t_constructConvert();
    +    void t_getSet();
    +    void t_foreach();
    +    void t_io();
    +};//QhullVertex_test
    +
    +void
    +add_QhullVertex_test()
    +{
    +    new QhullVertex_test();  // RoadTest::s_testcases
    +}
    +
    +//Executed after each testcase
    +void QhullVertex_test::
    +cleanup()
    +{
    +    RoadTest::cleanup();
    +}
    +
    +void QhullVertex_test::
    +t_constructConvert()
    +{
    +    QhullVertex v6;
    +    QVERIFY(!v6.isValid());
    +    QCOMPARE(v6.dimension(),0);
    +    // Qhull.runQhull() constructs QhullFacets as facetT
    +    RboxPoints rcube("c");
    +    Qhull q(rcube,"Qt QR0");  // triangulation of rotated unit cube
    +    QhullVertex v(q);
    +    QVERIFY(!v.isValid());
    +    QCOMPARE(v.dimension(),3);
    +    QhullVertex v2(q.beginVertex());
    +    QCOMPARE(v2.dimension(),3);
    +    v= v2;  // copy assignment
    +    QVERIFY(v.isValid());
    +    QCOMPARE(v.dimension(),3);
    +    QhullVertex v5= v2; // copy constructor
    +    QVERIFY(v5==v2);
    +    QVERIFY(v5==v);
    +    QhullVertex v3(q, v2.getVertexT());
    +    QCOMPARE(v,v3);
    +    QhullVertex v4(q, v2.getBaseT());
    +    QCOMPARE(v,v4);
    +}//t_constructConvert
    +
    +void QhullVertex_test::
    +t_getSet()
    +{
    +    RboxPoints rcube("c");
    +    {
    +        Qhull q(rcube,"Qt QR0");  // triangulation of rotated unit cube
    +        QCOMPARE(q.facetCount(), 12);
    +        QCOMPARE(q.vertexCount(), 8);
    +
    +        // Also spot-test QhullVertexList.  See QhullLinkedList_test.cpp
    +        QhullVertexList vs= q.vertexList();
    +        QhullVertexListIterator i(vs);
    +        while(i.hasNext()){
    +            const QhullVertex v= i.next();
    +            cout << v.id() << endl;
    +            QCOMPARE(v.dimension(),3);
    +            QVERIFY(v.id()>=0 && v.id()<9);
    +            QVERIFY(v.isValid());
    +            if(i.hasNext()){
    +                QCOMPARE(v.next(), i.peekNext());
    +                QVERIFY(v.next()!=v);
    +                QVERIFY(v.next().previous()==v);
    +            }
    +            QVERIFY(i.hasPrevious());
    +            QCOMPARE(v, i.peekPrevious());
    +        }
    +
    +        // test point()
    +        foreach (QhullVertex v, q.vertexList()){  // Qt only
    +            QhullPoint p= v.point();
    +            int j= p.id();
    +            cout << "Point " << j << ":\n" << p << endl;
    +            QVERIFY(j>=0 && j<8);
    +        }
    +    }
    +}//t_getSet
    +
    +void QhullVertex_test::
    +t_foreach()
    +{
    +    RboxPoints rcube("c W0 300");  // 300 points on surface of cube
    +    {
    +        Qhull q(rcube, "QR0 Qc"); // keep coplanars, thick facet, and rotate the cube
    +        foreach (QhullVertex v, q.vertexList()){  // Qt only
    +            QhullFacetSet fs= v.neighborFacets();
    +            QCOMPARE(fs.count(), 3);
    +            foreach (QhullFacet f, fs){  // Qt only
    +                QVERIFY(f.vertices().contains(v));
    +            }
    +        }
    +    }
    +}//t_foreach
    +
    +void QhullVertex_test::
    +t_io()
    +{
    +    RboxPoints rcube("c");
    +    {
    +        Qhull q(rcube, "");
    +        QhullVertex v= q.beginVertex();
    +        ostringstream os;
    +        os << "Vertex and vertices:\n";
    +        os << v;
    +        QhullVertexSet vs= q.firstFacet().vertices();
    +        os << vs;
    +        os << "\nVertex and vertices with message:\n";
    +        os << v.print("Vertex");
    +        os << vs.print("\nVertices:");
    +        cout << os.str();
    +        QString s= QString::fromStdString(os.str());
    +        QCOMPARE(s.count("(v"), 10);
    +        QCOMPARE(s.count(": f"), 2);
    +    }
    +    RboxPoints r10("10 D3");  // Without QhullVertex::facetNeighbors
    +    {
    +        Qhull q(r10, "");
    +        QhullVertex v= q.beginVertex();
    +        ostringstream os;
    +        os << "\nTry again with simplicial facets.  No neighboring facets listed for vertices.\n";
    +        os << "Vertex and vertices:\n";
    +        os << v;
    +        q.defineVertexNeighborFacets();
    +        os << "This time with neighborFacets() defined for all vertices:\n";
    +        os << v;
    +        cout << os.str();
    +        QString s= QString::fromStdString(os.str());
    +        QCOMPARE(s.count(": f"), 1);
    +
    +        Qhull q2(r10, "v"); // Voronoi diagram
    +        QhullVertex v2= q2.beginVertex();
    +        ostringstream os2;
    +        os2 << "\nTry again with Voronoi diagram of simplicial facets.  Neighboring facets automatically defined for vertices.\n";
    +        os2 << "Vertex and vertices:\n";
    +        os2 << v2;
    +        cout << os2.str();
    +        QString s2= QString::fromStdString(os2.str());
    +        QCOMPARE(s2.count(": f"), 1);
    +    }
    +}//t_io
    +
    +}//orgQhull
    +
    +#include "moc/QhullVertex_test.moc"
    diff --git a/xs/src/qhull/src/qhulltest/Qhull_test.cpp b/xs/src/qhull/src/qhulltest/Qhull_test.cpp
    new file mode 100644
    index 000000000..cc3918a05
    --- /dev/null
    +++ b/xs/src/qhull/src/qhulltest/Qhull_test.cpp
    @@ -0,0 +1,360 @@
    +/****************************************************************************
    +**
    +** Copyright (c) 2008-2015 C.B. Barber. All rights reserved.
    +** $Id: //main/2015/qhull/src/qhulltest/Qhull_test.cpp#4 $$Change: 2062 $
    +** $DateTime: 2016/01/17 13:13:18 $$Author: bbarber $
    +**
    +****************************************************************************/
    +
    +//pre-compiled headers
    +#include 
    +#include "qhulltest/RoadTest.h" // QT_VERSION
    +
    +#include "libqhullcpp/Qhull.h"
    +#include "libqhullcpp/QhullError.h"
    +#include "libqhullcpp/RboxPoints.h"
    +#include "libqhullcpp/QhullFacetList.h"
    +
    +using std::cout;
    +using std::endl;
    +using std::string;
    +
    +namespace orgQhull {
    +
    +//! Test C++ interface to Qhull
    +//! See eg/q_test for tests of Qhull commands
    +class Qhull_test : public RoadTest
    +{
    +    Q_OBJECT
    +
    +#//!\name Test slots
    +private slots:
    +    void cleanup();
    +    void t_construct();
    +    void t_attribute();
    +    void t_message();
    +    void t_getSet();
    +    void t_getQh();
    +    void t_getValue();
    +    void t_foreach();
    +    void t_modify();
    +};//Qhull_test
    +
    +void
    +add_Qhull_test()
    +{
    +    new Qhull_test();  // RoadTest::s_testcases
    +}
    +
    +//Executed after each testcase
    +void Qhull_test::
    +cleanup()
    +{
    +    RoadTest::cleanup();
    +}
    +
    +void Qhull_test::
    +t_construct()
    +{
    +    {
    +        Qhull q;
    +        QCOMPARE(q.dimension(),0);
    +        QVERIFY(q.qh()!=0);
    +        QCOMPARE(QString(q.qhullCommand()),QString(""));
    +        QCOMPARE(QString(q.rboxCommand()),QString(""));
    +        try{
    +            QCOMPARE(q.area(),0.0);
    +            QFAIL("area() did not fail.");
    +        }catch (const std::exception &e) {
    +            cout << "INFO   : Caught " << e.what();
    +        }
    +    }
    +    {
    +        RboxPoints rbox("10000");
    +        Qhull q(rbox, "QR0"); // Random points in a randomly rotated cube.
    +        QCOMPARE(q.dimension(),3);
    +        QVERIFY(q.volume() < 1.0);
    +        QVERIFY(q.volume() > 0.99);
    +    }
    +    {
    +        double points[] = {
    +            0, 0,
    +            1, 0,
    +            1, 1
    +        };
    +        Qhull q("triangle", 2, 3, points, "");
    +        QCOMPARE(q.dimension(),2);
    +        QCOMPARE(q.facetCount(),3);
    +        QCOMPARE(q.vertexCount(),3);
    +        QCOMPARE(q.dimension(),2);
    +        QCOMPARE(q.area(), 2.0+sqrt(2.0)); // length of boundary
    +        QCOMPARE(q.volume(), 0.5);        // the 2-d area
    +    }
    +}//t_construct
    +
    +void Qhull_test::
    +t_attribute()
    +{
    +    RboxPoints rcube("c");
    +    {
    +        double normals[] = {
    +            0,  -1, -0.5,
    +           -1,   0, -0.5,
    +            1,   0, -0.5,
    +            0,   1, -0.5
    +        };
    +        Qhull q;
    +        Coordinates feasible;
    +        feasible << 0.0 << 0.0;
    +        q.setFeasiblePoint(feasible);
    +        Coordinates c(std::vector(2, 0.0));
    +        QVERIFY(q.feasiblePoint()==c);
    +        q.setOutputStream(&cout);
    +        q.runQhull("normals of square", 3, 4, normals, "H"); // halfspace intersect
    +        QVERIFY(q.feasiblePoint()==c); // from qh.feasible_point after runQhull()
    +        QCOMPARE(q.facetList().count(), 4); // Vertices of square
    +        cout << "Expecting summary of halfspace intersection\n";
    +        q.outputQhull();
    +        q.qh()->disableOutputStream();  // Same as q.disableOutputStream()
    +        cout << "Expecting no output from qh_fprintf() in Qhull.cpp\n";
    +        q.outputQhull();
    +    }
    +}//t_attribute
    +
    +//! No QhullMessage for errors outside of qhull
    +void Qhull_test::
    +t_message()
    +{
    +    RboxPoints rcube("c");
    +    {
    +        Qhull q;
    +        QCOMPARE(q.qhullMessage(), string(""));
    +        QCOMPARE(q.qhullStatus(), qh_ERRnone);
    +        QVERIFY(!q.hasQhullMessage());
    +        try{
    +            q.runQhull(rcube, "Fd");
    +            QFAIL("runQhull Fd did not fail.");
    +        }catch (const std::exception &e) {
    +            const char *s= e.what();
    +            cout << "INFO   : Caught " << s;
    +            QCOMPARE(QString::fromStdString(s).left(6), QString("QH6029"));
    +            // FIXUP QH11025 -- review decision to clearQhullMessage at QhullError()            // Cleared when copied to QhullError
    +            QVERIFY(!q.hasQhullMessage());
    +            // QCOMPARE(q.qhullMessage(), QString::fromStdString(s).remove(0, 7));
    +            // QCOMPARE(q.qhullStatus(), 6029);
    +            q.clearQhullMessage();
    +            QVERIFY(!q.hasQhullMessage());
    +        }
    +        q.appendQhullMessage("Append 1");
    +        QVERIFY(q.hasQhullMessage());
    +        QCOMPARE(QString::fromStdString(q.qhullMessage()), QString("Append 1"));
    +        q.appendQhullMessage("\nAppend 2\n");
    +        QCOMPARE(QString::fromStdString(q.qhullMessage()), QString("Append 1\nAppend 2\n"));
    +        q.clearQhullMessage();
    +        QVERIFY(!q.hasQhullMessage());
    +        QCOMPARE(QString::fromStdString(q.qhullMessage()), QString(""));
    +    }
    +    {
    +        cout << "INFO   : Error stream without output stream\n";
    +        Qhull q;
    +        q.setErrorStream(&cout);
    +        q.setOutputStream(0);
    +        try{
    +            q.runQhull(rcube, "Fd");
    +            QFAIL("runQhull Fd did not fail.");
    +        }catch (const QhullError &e) {
    +            cout << "INFO   : Caught " << e;
    +            QCOMPARE(e.errorCode(), 6029);
    +        }
    +        //FIXUP QH11025 Qhullmessage cleared when QhullError thrown.  Switched to e
    +        //QVERIFY(q.hasQhullMessage());
    +        //QCOMPARE(QString::fromStdString(q.qhullMessage()).left(6), QString("QH6029"));
    +        q.clearQhullMessage();
    +        QVERIFY(!q.hasQhullMessage());
    +    }
    +    {
    +        cout << "INFO   : Error output sent to output stream without error stream\n";
    +        Qhull q;
    +        q.setErrorStream(0);
    +        q.setOutputStream(&cout);
    +        try{
    +            q.runQhull(rcube, "Tz H0");
    +            QFAIL("runQhull TZ did not fail.");
    +        }catch (const std::exception &e) {
    +            const char *s= e.what();
    +            cout << "INFO   : Caught " << s;
    +            QCOMPARE(QString::fromLatin1(s).left(6), QString("QH6023"));
    +        }
    +        //FIXUP QH11025 Qhullmessage cleared when QhullError thrown.  Switched to e
    +        //QVERIFY(q.hasQhullMessage());
    +        //QCOMPARE(QString::fromStdString(q.qhullMessage()).left(17), QString("qhull: no message"));
    +        //QCOMPARE(q.qhullStatus(), 6023);
    +        q.clearQhullMessage();
    +        QVERIFY(!q.hasQhullMessage());
    +    }
    +    {
    +        cout << "INFO   : No error stream or output stream\n";
    +        Qhull q;
    +        q.setErrorStream(0);
    +        q.setOutputStream(0);
    +        try{
    +            q.runQhull(rcube, "Fd");
    +            QFAIL("outputQhull did not fail.");
    +        }catch (const std::exception &e) {
    +            const char *s= e.what();
    +            cout << "INFO   : Caught " << s;
    +            QCOMPARE(QString::fromLatin1(s).left(6), QString("QH6029"));
    +        }
    +        //FIXUP QH11025 Qhullmessage cleared when QhullError thrown.  Switched to e
    +        //QVERIFY(q.hasQhullMessage());
    +        //QCOMPARE(QString::fromStdString(q.qhullMessage()).left(9), QString("qhull err"));
    +        //QCOMPARE(q.qhullStatus(), 6029);
    +        q.clearQhullMessage();
    +        QVERIFY(!q.hasQhullMessage());
    +    }
    +}//t_message
    +
    +void Qhull_test::
    +t_getSet()
    +{
    +    RboxPoints rcube("c");
    +    {
    +        Qhull q;
    +        QVERIFY(!q.initialized());
    +        q.runQhull(rcube, "s");
    +        QVERIFY(q.initialized());
    +        QCOMPARE(q.dimension(), 3);
    +        QhullPoint p= q.origin();
    +        QCOMPARE(p.dimension(), 3);
    +        QCOMPARE(p[0]+p[1]+p[2], 0.0);
    +        q.setErrorStream(&cout);
    +        q.outputQhull();
    +    }
    +    {
    +        Qhull q;
    +        q.runQhull(rcube, "");
    +        q.setOutputStream(&cout);
    +        q.outputQhull();
    +    }
    +}//t_getSet
    +
    +void Qhull_test::
    +t_getQh()
    +{
    +    RboxPoints rcube("c");
    +    {
    +        Qhull q;
    +        q.runQhull(rcube, "s");
    +        QCOMPARE(QString(q.qhullCommand()), QString("qhull s"));
    +        QCOMPARE(QString(q.rboxCommand()), QString("rbox \"c\""));
    +        QCOMPARE(q.facetCount(), 6);
    +        QCOMPARE(q.vertexCount(), 8);
    +        // Sample fields from Qhull's qhT [libqhull.h]
    +        QCOMPARE(q.qh()->ALLpoints, 0u);
    +        QCOMPARE(q.qh()->GOODpoint, 0);
    +        QCOMPARE(q.qh()->IStracing, 0);
    +        QCOMPARE(q.qh()->MAXcoplanar+1.0, 1.0); // fuzzy compare
    +        QCOMPARE(q.qh()->MERGING, 1u);
    +        QCOMPARE(q.qh()->input_dim, 3);
    +        QCOMPARE(QString(q.qh()->qhull_options).left(8), QString("  run-id"));
    +        QCOMPARE(q.qh()->num_facets, 6);
    +        QCOMPARE(q.qh()->hasTriangulation, 0u);
    +        QCOMPARE(q.qh()->max_outside - q.qh()->min_vertex + 1.0, 1.0); // fuzzy compare
    +        QCOMPARE(*q.qh()->gm_matrix+1.0, 1.0); // fuzzy compare
    +    }
    +}//t_getQh
    +
    +void Qhull_test::
    +t_getValue()
    +{
    +    RboxPoints rcube("c");
    +    {
    +        Qhull q;
    +        q.runQhull(rcube, "");
    +        QCOMPARE(q.area(), 6.0);
    +        QCOMPARE(q.volume(), 1.0);
    +    }
    +}//t_getValue
    +
    +void Qhull_test::
    +t_foreach()
    +{
    +    RboxPoints rcube("c");
    +    {
    +        Qhull q;
    +        QCOMPARE(q.beginFacet(),q.endFacet());
    +        QCOMPARE(q.beginVertex(),q.endVertex());
    +        q.runQhull(rcube, "");
    +        QCOMPARE(q.facetList().count(), 6);
    +
    +        // defineVertexNeighborFacets() tested in QhullVertex_test::t_io()
    +
    +        QhullFacetList facets(q.beginFacet(), q.endFacet());
    +        QCOMPARE(facets.count(), 6);
    +        QCOMPARE(q.firstFacet(), q.beginFacet());
    +        QhullVertexList vertices(q.beginVertex(), q.endVertex());
    +        QCOMPARE(vertices.count(), 8);
    +        QCOMPARE(q.firstVertex(), q.beginVertex());
    +        QhullPoints ps= q.points();
    +        QCOMPARE(ps.count(), 8);
    +        QhullPointSet ps2= q.otherPoints();
    +        QCOMPARE(ps2.count(), 0);
    +        // ps2= q.otherPoints(); //disabled, would not copy the points
    +        QCOMPARE(q.facetCount(), 6);
    +        QCOMPARE(q.vertexCount(), 8);
    +        coordT *c= q.pointCoordinateBegin(); // of q.points()
    +        QVERIFY(*c==0.5 || *c==-0.5);
    +        coordT *c3= q.pointCoordinateEnd();
    +        QVERIFY(c3[-1]==0.5 || c3[-1]==-0.5);
    +        QCOMPARE(c3-c, 8*3);
    +        QCOMPARE(q.vertexList().count(), 8);
    +    }
    +}//t_foreach
    +
    +void Qhull_test::
    +t_modify()
    +{
    +    //addPoint() tested in t_foreach
    +    RboxPoints diamond("d");
    +    Qhull q(diamond, "o");
    +    q.setOutputStream(&cout);
    +    cout << "Expecting vertexList and facetList of a 3-d diamond.\n";
    +    q.outputQhull();
    +    cout << "Expecting normals of a 3-d diamond.\n";
    +    q.outputQhull("n");
    +    // runQhull tested in t_attribute(), t_message(), etc.
    +}//t_modify
    +
    +}//orgQhull
    +
    +// Redefine Qhull's usermem_r.c in order to report erroneous calls to qh_exit
    +void qh_exit(int exitcode) {
    +    cout << "FAIL!  : Qhull called qh_exit().  Qhull's error handling not available.\n.. See the corresponding Qhull:qhull_message or setErrorStream().\n";
    +    exit(exitcode);
    +}
    +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 */
    +void qh_free(void *mem) {
    +    free(mem);
    +}
    +void *qh_malloc(size_t size) {
    +    return malloc(size);
    +}
    +
    +#if 0
    +template<> char * QTest::
    +toString(const std::string &s)
    +{
    +    QByteArray ba = s.c_str();
    +    return qstrdup(ba.data());
    +}
    +#endif
    +
    +#include "moc/Qhull_test.moc"
    diff --git a/xs/src/qhull/src/qhulltest/RboxPoints_test.cpp b/xs/src/qhull/src/qhulltest/RboxPoints_test.cpp
    new file mode 100644
    index 000000000..4f4ea984f
    --- /dev/null
    +++ b/xs/src/qhull/src/qhulltest/RboxPoints_test.cpp
    @@ -0,0 +1,215 @@
    +/****************************************************************************
    +**
    +** Copyright (c) 2006-2015 C.B. Barber. All rights reserved.
    +** $Id: //main/2015/qhull/src/qhulltest/RboxPoints_test.cpp#2 $$Change: 2062 $
    +** $DateTime: 2016/01/17 13:13:18 $$Author: bbarber $
    +**
    +****************************************************************************/
    +
    +//pre-compiled headers
    +#include 
    +#include "RoadTest.h" // QT_VERSION
    +
    +#include "libqhullcpp/RboxPoints.h"
    +#include "libqhullcpp/QhullError.h"
    +
    +using std::cout;
    +using std::endl;
    +using std::ostringstream;
    +using std::string;
    +using std::stringstream;
    +
    +namespace orgQhull {
    +
    +//! Test C++ interface to Rbox
    +//! See eg/q_test for tests of rbox commands
    +class RboxPoints_test : public RoadTest
    +{
    +    Q_OBJECT
    +
    +#//!\name Test slots
    +private slots:
    +    void t_construct();
    +    void t_error();
    +    void t_test();
    +    void t_getSet();
    +    void t_foreach();
    +    void t_change();
    +    void t_ostream();
    +};
    +
    +void
    +add_RboxPoints_test()
    +{
    +    new RboxPoints_test();  // RoadTest::s_testcases
    +}
    +
    +void RboxPoints_test::
    +t_construct()
    +{
    +    RboxPoints rp;
    +    QCOMPARE(rp.dimension(), 0);
    +    QCOMPARE(rp.count(), 0);
    +    QVERIFY(QString::fromStdString(rp.comment()) != QString(""));
    +    QVERIFY(rp.isEmpty());
    +    QVERIFY(!rp.hasRboxMessage());
    +    QCOMPARE(rp.rboxStatus(), qh_ERRnone);
    +    QCOMPARE(QString::fromStdString(rp.rboxMessage()), QString("rbox warning: no points generated\n"));
    +
    +    RboxPoints rp2("c"); // 3-d cube
    +    QCOMPARE(rp2.dimension(), 3);
    +    QCOMPARE(rp2.count(), 8);
    +    QCOMPARE(QString::fromStdString(rp2.comment()), QString("rbox \"c\""));
    +    QVERIFY(!rp2.isEmpty());
    +    QVERIFY(!rp2.hasRboxMessage());
    +    QCOMPARE(rp2.rboxStatus(), qh_ERRnone);
    +    QCOMPARE(QString::fromStdString(rp2.rboxMessage()), QString("rbox: OK\n"));
    +}//t_construct
    +
    +void RboxPoints_test::
    +t_error()
    +{
    +    RboxPoints rp;
    +    try{
    +        rp.appendPoints("D0 c");
    +        QFAIL("'D0 c' did not fail.");
    +    }catch (const std::exception &e) {
    +        const char *s= e.what();
    +        cout << "INFO   : Caught " << s;
    +        QCOMPARE(QString(s).left(6), QString("QH6189"));
    +        QVERIFY(rp.hasRboxMessage());
    +        QCOMPARE(QString::fromStdString(rp.rboxMessage()).left(8), QString("rbox err"));
    +        QCOMPARE(rp.rboxStatus(), 6189);
    +        rp.clearRboxMessage();
    +        QVERIFY(!rp.hasRboxMessage());
    +    }
    +    try{
    +        RboxPoints rp2;
    +        rp2.setDimension(-1);
    +        QFAIL("setDimension(-1) did not fail.");
    +    }catch (const RoadError &e) {
    +        const char *s= e.what();
    +        cout << "INFO   : Caught " << s;
    +        QCOMPARE(QString(s).left(7), QString("QH10062"));
    +        QCOMPARE(e.errorCode(), 10062);
    +        QCOMPARE(QString::fromStdString(e.what()), QString(s));
    +        RoadLogEvent logEvent= e.roadLogEvent();
    +        QCOMPARE(logEvent.int1(), -1);
    +    }
    +}//t_error
    +
    +void RboxPoints_test::
    +t_test()
    +{
    +    // isEmpty -- t_construct
    +}//t_test
    +
    +void RboxPoints_test::
    +t_getSet()
    +{
    +    // comment -- t_construct
    +    // count -- t_construct
    +    // dimension -- t_construct
    +
    +    RboxPoints rp;
    +    QCOMPARE(rp.dimension(), 0);
    +    rp.setDimension(2);
    +    QCOMPARE(rp.dimension(), 2);
    +    try{
    +        rp.setDimension(102);
    +        QFAIL("setDimension(102) did not fail.");
    +    }catch (const std::exception &e) {
    +        cout << "INFO   : Caught " << e.what();
    +    }
    +    QCOMPARE(rp.newCount(), 0);
    +    rp.appendPoints("D2 P1 P2");
    +    QCOMPARE(rp.count(), 2);
    +    QCOMPARE(rp.newCount(), 2); // From previous appendPoints();
    +    PointCoordinates pc(rp.qh(), 2, "Test qh() and <<");
    +    pc << 1.0 << 0.0 << 2.0 << 0.0;
    +    QCOMPARE(pc.dimension(), 2);
    +    QCOMPARE(pc.count(), 2);
    +    QVERIFY(rp==pc);
    +    rp.setNewCount(10);  // Normally only used by appendPoints for rbox processing
    +    QCOMPARE(rp.newCount(), 10);
    +    rp.reservePoints();
    +    QVERIFY(rp==pc);
    +}//t_getSet
    +
    +void RboxPoints_test::
    +t_foreach()
    +{
    +    RboxPoints rp("c");
    +    Coordinates::ConstIterator cci= rp.beginCoordinates();
    +    orgQhull::Coordinates::Iterator ci= rp.beginCoordinates();
    +    QCOMPARE(*cci, -0.5);
    +    QCOMPARE(*ci, *cci);
    +    int i=1;
    +    while(++cci
    +#include "RoadTest.h" // QT_VERSION
    +
    +#include 
    +
    +using std::cout;
    +using std::endl;
    +
    +namespace orgQhull {
    +
    +#//!\name class variable
    +
    +QList RoadTest::
    +s_testcases;
    +
    +int RoadTest::
    +s_test_count= 0;
    +
    +int RoadTest::
    +s_test_fail= 0;
    +
    +QStringList RoadTest::
    +s_failed_tests;
    +
    +#//!\name Slot
    +
    +//! Executed after each test
    +void RoadTest::
    +cleanup()
    +{
    +    s_test_count++;
    +    if(QTest::currentTestFailed()){
    +        recordFailedTest();
    +    }
    +}//cleanup
    +
    +#//!\name Helper
    +
    +void RoadTest::
    +recordFailedTest()
    +{
    +    s_test_fail++;
    +    QString className= metaObject()->className();
    +    s_failed_tests << className + "::" + QTest::currentTestFunction();
    +}
    +
    +#//!\name class function
    +
    +void RoadTest::
    +deleteTests()
    +{
    +    foreach(RoadTest *testcase, s_testcases){
    +        delete testcase;
    +    }
    +    s_failed_tests.clear();
    +}
    +
    +int RoadTest::
    +runTests(QStringList arguments)
    +{
    +    int result= 0; // assume success
    +
    +    foreach(RoadTest *testcase, s_testcases){
    +        try{
    +            result += QTest::qExec(testcase, arguments);
    +        }catch(const std::exception &e){
    +            cout << "FAIL!  : Threw error ";
    +            cout << e.what() << endl;
    +    s_test_count++;
    +            testcase->recordFailedTest();
    +            // Qt 4.5.2 OK.  In Qt 4.3.3, qtestcase did not clear currentTestObject
    +        }
    +    }
    +    if(s_test_fail){
    +        cout << "Failed " << s_test_fail << " of " << s_test_count << " tests.\n";
    +        cout << s_failed_tests.join("\n").toLocal8Bit().constData() << std::endl;
    +    }else{
    +        cout << "Passed " << s_test_count << " tests.\n";
    +    }
    +    return result;
    +}//runTests
    +
    +}//orgQhull
    +
    +#include "moc/moc_RoadTest.cpp"
    diff --git a/xs/src/qhull/src/qhulltest/RoadTest.h b/xs/src/qhull/src/qhulltest/RoadTest.h
    new file mode 100644
    index 000000000..adfe0bf8c
    --- /dev/null
    +++ b/xs/src/qhull/src/qhulltest/RoadTest.h
    @@ -0,0 +1,102 @@
    +/****************************************************************************
    +**
    +** Copyright (c) 2008-2015 C.B. Barber. All rights reserved.
    +** $Id: //main/2015/qhull/src/qhulltest/RoadTest.h#2 $$Change: 2062 $
    +** $Date: 2016/01/17 $$Author: bbarber $
    +**
    +****************************************************************************/
    +
    +#ifndef ROADTEST_H
    +#define ROADTEST_H
    +
    +//pre-compiled with RoadTest.h
    +#include     // Qt C++ Framework
    +#include 
    +
    +#define QHULL_USES_QT 1
    +
    +namespace orgQhull {
    +
    +#//!\name Defined here
    +
    +    //! RoadTest -- Generic test for Qt's QTest
    +    class RoadTest;
    +    //! TESTadd_(t) -- Add a RoadTest
    +
    +/** Test Name objects using Qt's QTestLib
    +
    +Template:
    +
    +class Name_test : public RoadTest
    +{
    +    Q_OBJECT
    +#//!\name Test slot
    +private slots:
    +    void t_name();
    +    //Executed before any test
    +    void initTestCase();
    +    void init();          // Each test
    +    //Executed after each test
    +    void cleanup(); //RoadTest::cleanup();
    +    // Executed after last test
    +    void cleanupTestCase();
    +};
    +
    +void
    +add_Name_test()
    +{
    +    new Name_test();  // RoadTest::s_testcases
    +}
    +
    +Send additional output to cout
    +*/
    +
    +class RoadTest : public QObject
    +{
    +    Q_OBJECT
    +
    +#//!\name Class globals
    +protected:
    +    static QList
    +                        s_testcases; ///! List of testcases to execute.  Initialized via add_...()
    +    static int          s_test_count; ///! Total number of tests executed
    +    static int          s_test_fail; ///! Number of failed tests
    +    static QStringList  s_failed_tests; ///! List of failed tests
    +
    +#//!\name Test slots
    +public slots:
    +    void cleanup();
    +
    +public:
    +#//!\name Constructors, etc.
    +                        RoadTest()  { s_testcases.append(this); }
    +    virtual             ~RoadTest() {} // Derived from QObject
    +
    +#//!\name Helper
    +    void                recordFailedTest();
    +
    +
    +#//!\name Class functions
    +    static void         deleteTests();
    +    static int          runTests(QStringList arguments);
    +
    +};//RoadTest
    +
    +#define TESTadd_(t) extern void t(); t();
    +
    +
    +}//orgQhull
    +
    +namespace QTest{
    +
    +template<>
    +inline char *
    +toString(const std::string &s)
    +{
    +    return qstrdup(s.c_str());
    +}
    +
    +}//namespace QTest
    +
    +#endif //ROADTEST_H
    +
    diff --git a/xs/src/qhull/src/qhulltest/qhulltest.cpp b/xs/src/qhull/src/qhulltest/qhulltest.cpp
    new file mode 100644
    index 000000000..5bfe16e9c
    --- /dev/null
    +++ b/xs/src/qhull/src/qhulltest/qhulltest.cpp
    @@ -0,0 +1,94 @@
    +/****************************************************************************
    +**
    +** Copyright (c) 2008-2015 C.B. Barber. All rights reserved.
    +** $Id: //main/2015/qhull/src/qhulltest/qhulltest.cpp#5 $$Change: 2079 $
    +** $DateTime: 2016/02/07 17:43:34 $$Author: bbarber $
    +**
    +****************************************************************************/
    +
    +//pre-compiled headers
    +#include "libqhull_r/user_r.h"
    +
    +#include 
    +#include "RoadTest.h" // QT_VERSION
    +
    +#include "libqhullcpp/RoadError.h"
    +#include "libqhull_r/qhull_ra.h"
    +
    +#include 
    +#include 
    +#include 
    +
    +using std::cout;
    +using std::endl;
    +
    +namespace orgQhull {
    +
    +void addQhullTests(QStringList &args)
    +{
    +    TESTadd_(add_Qhull_test);
    +
    +    if(args.contains("--all")){
    +        args.removeAll("--all");
    +        // up-to-date
    +        TESTadd_(add_Coordinates_test);
    +        TESTadd_(add_PointCoordinates_test);
    +        TESTadd_(add_QhullFacet_test);
    +        TESTadd_(add_QhullFacetList_test);
    +        TESTadd_(add_QhullFacetSet_test);
    +        TESTadd_(add_QhullHyperplane_test);
    +        TESTadd_(add_QhullLinkedList_test);
    +        TESTadd_(add_QhullPoint_test);
    +        TESTadd_(add_QhullPoints_test);
    +        TESTadd_(add_QhullPointSet_test);
    +        TESTadd_(add_QhullRidge_test);
    +        TESTadd_(add_QhullSet_test);
    +        TESTadd_(add_QhullVertex_test);
    +        TESTadd_(add_QhullVertexSet_test);
    +        TESTadd_(add_RboxPoints_test);
    +        // qhullStat
    +        TESTadd_(add_Qhull_test);
    +    }//--all
    +}//addQhullTests
    +
    +int main(int argc, char *argv[])
    +{
    +
    +    QCoreApplication app(argc, argv);
    +    QStringList args= app.arguments();
    +    bool isAll= args.contains("--all");
    +
    +    QHULL_LIB_CHECK /* Check for compatible library */
    +
    +    addQhullTests(args);
    +    int status=1010;
    +    try{
    +        status= RoadTest::runTests(args);
    +    }catch(const std::exception &e){
    +        cout << "FAIL!  : runTests() did not catch error\n";
    +        cout << e.what() << endl;
    +        if(!RoadError::emptyGlobalLog()){
    +            cout << RoadError::stringGlobalLog() << endl;
    +            RoadError::clearGlobalLog();
    +        }
    +    }
    +    if(!RoadError::emptyGlobalLog()){
    +        cout << RoadError::stringGlobalLog() << endl;
    +        RoadError::clearGlobalLog();
    +    }
    +    if(isAll){
    +        cout << "Finished test of libqhullcpp.  Test libqhull_r with eg/q_test after building libqhull_r/Makefile" << endl;
    +    }else{
    +        cout << "Finished test of one class.  Test all classes with 'qhulltest --all'" << endl;
    +    }
    +    RoadTest::deleteTests();
    +    return status;
    +}
    +
    +}//orgQhull
    +
    +int main(int argc, char *argv[])
    +{
    +    return orgQhull::main(argc, argv); // Needs RoadTest:: for TESTadd_() linkage
    +}
    +
    diff --git a/xs/src/qhull/src/qhulltest/qhulltest.pro b/xs/src/qhull/src/qhulltest/qhulltest.pro
    new file mode 100644
    index 000000000..0da34d375
    --- /dev/null
    +++ b/xs/src/qhull/src/qhulltest/qhulltest.pro
    @@ -0,0 +1,36 @@
    +# -------------------------------------------------
    +# qhulltest.pro -- Qt project for qhulltest.exe (QTestLib)
    +# cd $qh/build/qhulltest && qmake -tp vc -r ../../src/qhulltest/qhulltest.pro
    +# -------------------------------------------------
    +
    +include(../qhull-app-cpp.pri)
    +
    +TARGET = qhulltest
    +QT += testlib
    +MOC_DIR = moc
    +INCLUDEPATH += ..  # for MOC_DIR
    +
    +PRECOMPILED_HEADER = RoadTest.h
    +
    +HEADERS += RoadTest.h
    +
    +SOURCES += ../libqhullcpp/qt-qhull.cpp
    +SOURCES += Coordinates_test.cpp
    +SOURCES += PointCoordinates_test.cpp
    +SOURCES += Qhull_test.cpp
    +SOURCES += QhullFacet_test.cpp
    +SOURCES += QhullFacetList_test.cpp
    +SOURCES += QhullFacetSet_test.cpp
    +SOURCES += QhullHyperplane_test.cpp
    +SOURCES += QhullLinkedList_test.cpp
    +SOURCES += QhullPoint_test.cpp
    +SOURCES += QhullPoints_test.cpp
    +SOURCES += QhullPointSet_test.cpp
    +SOURCES += QhullRidge_test.cpp
    +SOURCES += QhullSet_test.cpp
    +SOURCES += qhulltest.cpp
    +SOURCES += QhullVertex_test.cpp
    +SOURCES += QhullVertexSet_test.cpp
    +SOURCES += RboxPoints_test.cpp
    +SOURCES += RoadTest.cpp
    +
    diff --git a/xs/src/qhull/src/qvoronoi/qvoronoi.c b/xs/src/qhull/src/qvoronoi/qvoronoi.c
    new file mode 100644
    index 000000000..b93d23711
    --- /dev/null
    +++ b/xs/src/qhull/src/qvoronoi/qvoronoi.c
    @@ -0,0 +1,303 @@
    +/*
      ---------------------------------
    +
    +   qvoronoi.c
    +     compute Voronoi diagrams and furthest-point Voronoi
    +     diagrams using qhull
    +
    +   see unix.c for full interface
    +
    +   Copyright (c) 1993-2015, The Geometry Center
    +*/
    +
    +#include "libqhull/libqhull.h"
    +
    +#include 
    +#include 
    +#include 
    +#include 
    +#include 
    +
    +#if __cplusplus
    +extern "C" {
    +  int isatty(int);
    +}
    +
    +#elif _MSC_VER
    +#include 
    +#define isatty _isatty
    +/* int _isatty(int); */
    +
    +#else
    +int isatty(int);  /* returns 1 if stdin is a tty
    +                   if "Undefined symbol" this can be deleted along with call in main() */
    +#endif
    +
    +/*---------------------------------
    +
    +  qh_prompt
    +    long prompt for qhull
    +
    +  notes:
    +    restricted version of libqhull.c
    +
    +  see:
    +    concise prompt below
    +*/
    +
    +/* duplicated in qvoron_f.htm and qvoronoi.htm
    +   QJ and Qt are deprecated, but allowed for backwards compatibility
    +*/
    +char hidden_options[]=" d n m v H U Qb QB Qc Qf Qg Qi Qm Qr QR Qv Qx TR E V Fa FA FC Fp FS Ft FV Pv Gt Q0 Q1 Q2 Q3 Q4 Q5 Q6 Q7 Q8 Q9 ";
    +
    +char qh_prompta[]= "\n\
    +qvoronoi- compute the Voronoi diagram\n\
    +    http://www.qhull.org  %s\n\
    +\n\
    +input (stdin):\n\
    +    first lines: dimension and number of points (or vice-versa).\n\
    +    other lines: point coordinates, best if one point per line\n\
    +    comments:    start with a non-numeric character\n\
    +\n\
    +options:\n\
    +    Qu   - compute furthest-site Voronoi diagram\n\
    +\n\
    +Qhull control options:\n\
    +    Qz   - add point-at-infinity to Voronoi diagram\n\
    +%s%s%s%s";  /* split up qh_prompt for Visual C++ */
    +char qh_promptb[]= "\
    +    Qs   - search all points for the initial simplex\n\
    +    QGn  - Voronoi vertices if visible from point n, -n if not\n\
    +    QVn  - Voronoi vertices for input point n, -n if not\n\
    +\n\
    +";
    +char qh_promptc[]= "\
    +Trace options:\n\
    +    T4   - trace at level n, 4=all, 5=mem/gauss, -1= events\n\
    +    Tc   - check frequently during execution\n\
    +    Ts   - statistics\n\
    +    Tv   - verify result: structure, convexity, and in-circle test\n\
    +    Tz   - send all output to stdout\n\
    +    TFn  - report summary when n or more facets created\n\
    +    TI file - input data from file, no spaces or single quotes\n\
    +    TO file - output results to file, may be enclosed in single quotes\n\
    +    TPn  - turn on tracing when point n added to hull\n\
    +     TMn - turn on tracing at merge n\n\
    +     TWn - trace merge facets when width > n\n\
    +    TVn  - stop qhull after adding point n, -n for before (see TCn)\n\
    +     TCn - stop qhull after building cone for point n (see TVn)\n\
    +\n\
    +Precision options:\n\
    +    Cn   - radius of centrum (roundoff added).  Merge facets if non-convex\n\
    +     An  - cosine of maximum angle.  Merge facets if cosine > n or non-convex\n\
    +           C-0 roundoff, A-0.99/C-0.01 pre-merge, A0.99/C0.01 post-merge\n\
    +    Rn   - randomly perturb computations by a factor of [1-n,1+n]\n\
    +    Wn   - min facet width for non-coincident point (before roundoff)\n\
    +\n\
    +Output formats (may be combined; if none, produces a summary to stdout):\n\
    +    s    - summary to stderr\n\
    +    p    - Voronoi vertices\n\
    +    o    - OFF format (dim, Voronoi vertices, and Voronoi regions)\n\
    +    i    - Delaunay regions (use 'Pp' to avoid warning)\n\
    +    f    - facet dump\n\
    +\n\
    +";
    +char qh_promptd[]= "\
    +More formats:\n\
    +    Fc   - count plus coincident points (by Voronoi vertex)\n\
    +    Fd   - use cdd format for input (homogeneous with offset first)\n\
    +    FD   - use cdd format for output (offset first)\n\
    +    FF   - facet dump without ridges\n\
    +    Fi   - separating hyperplanes for bounded Voronoi regions\n\
    +    FI   - ID for each Voronoi vertex\n\
    +    Fm   - merge count for each Voronoi vertex (511 max)\n\
    +    Fn   - count plus neighboring Voronoi vertices for each Voronoi vertex\n\
    +    FN   - count and Voronoi vertices for each Voronoi region\n\
    +    Fo   - separating hyperplanes for unbounded Voronoi regions\n\
    +    FO   - options and precision constants\n\
    +    FP   - nearest point and distance for each coincident point\n\
    +    FQ   - command used for qvoronoi\n\
    +    Fs   - summary: #int (8), dimension, #points, tot vertices, tot facets,\n\
    +                    for output: #Voronoi regions, #Voronoi vertices,\n\
    +                                #coincident points, #non-simplicial regions\n\
    +                    #real (2), max outer plane and min vertex\n\
    +    Fv   - Voronoi diagram as Voronoi vertices between adjacent input sites\n\
    +    Fx   - extreme points of Delaunay triangulation (on convex hull)\n\
    +\n\
    +";
    +char qh_prompte[]= "\
    +Geomview options (2-d only)\n\
    +    Ga   - all points as dots\n\
    +     Gp  -  coplanar points and vertices as radii\n\
    +     Gv  -  vertices as spheres\n\
    +    Gi   - inner planes only\n\
    +     Gn  -  no planes\n\
    +     Go  -  outer planes only\n\
    +    Gc   - centrums\n\
    +    Gh   - hyperplane intersections\n\
    +    Gr   - ridges\n\
    +    GDn  - drop dimension n in 3-d and 4-d output\n\
    +\n\
    +Print options:\n\
    +    PAn  - keep n largest Voronoi vertices by 'area'\n\
    +    Pdk:n - drop facet if normal[k] <= n (default 0.0)\n\
    +    PDk:n - drop facet if normal[k] >= n\n\
    +    Pg   - print good Voronoi vertices (needs 'QGn' or 'QVn')\n\
    +    PFn  - keep Voronoi vertices whose 'area' is at least n\n\
    +    PG   - print neighbors of good Voronoi vertices\n\
    +    PMn  - keep n Voronoi vertices with most merges\n\
    +    Po   - force output.  If error, output neighborhood of facet\n\
    +    Pp   - do not report precision problems\n\
    +\n\
    +    .    - list of all options\n\
    +    -    - one line descriptions of all options\n\
    +    -V   - version\n\
    +";
    +/* for opts, don't assign 'e' or 'E' to a flag (already used for exponent) */
    +
    +/*---------------------------------
    +
    +  qh_prompt2
    +    synopsis for qhull
    +*/
    +char qh_prompt2[]= "\n\
    +qvoronoi- compute the Voronoi diagram.  Qhull %s\n\
    +    input (stdin): dimension, number of points, point coordinates\n\
    +    comments start with a non-numeric character\n\
    +\n\
    +options (qvoronoi.htm):\n\
    +    Qu   - compute furthest-site Voronoi diagram\n\
    +    Tv   - verify result: structure, convexity, and in-circle test\n\
    +    .    - concise list of all options\n\
    +    -    - one-line description of all options\n\
    +    -V   - version\n\
    +\n\
    +output options (subset):\n\
    +    s    - summary of results (default)\n\
    +    p    - Voronoi vertices\n\
    +    o    - OFF file format (dim, Voronoi vertices, and Voronoi regions)\n\
    +    FN   - count and Voronoi vertices for each Voronoi region\n\
    +    Fv   - Voronoi diagram as Voronoi vertices between adjacent input sites\n\
    +    Fi   - separating hyperplanes for bounded regions, 'Fo' for unbounded\n\
    +    G    - Geomview output (2-d only)\n\
    +    QVn  - Voronoi vertices for input point n, -n if not\n\
    +    TO file- output results to file, may be enclosed in single quotes\n\
    +\n\
    +examples:\n\
    +rbox c P0 D2 | qvoronoi s o         rbox c P0 D2 | qvoronoi Fi\n\
    +rbox c P0 D2 | qvoronoi Fo          rbox c P0 D2 | qvoronoi Fv\n\
    +rbox c P0 D2 | qvoronoi s Qu Fv     rbox c P0 D2 | qvoronoi Qu Fo\n\
    +rbox c G1 d D2 | qvoronoi s p       rbox c P0 D2 | qvoronoi s Fv QV0\n\
    +\n\
    +";
    +/* for opts, don't assign 'e' or 'E' to a flag (already used for exponent) */
    +
    +/*---------------------------------
    +
    +  qh_prompt3
    +    concise prompt for qhull
    +*/
    +char qh_prompt3[]= "\n\
    +Qhull %s.\n\
    +Except for 'F.' and 'PG', upper-case options take an argument.\n\
    +\n\
    + OFF_format     p_vertices     i_delaunay     summary        facet_dump\n\
    +\n\
    + Fcoincident    Fd_cdd_in      FD_cdd_out     FF-dump-xridge Fi_bounded\n\
    + Fxtremes       Fmerges        Fneighbors     FNeigh_region  FOptions\n\
    + Fo_unbounded   FPoint_near    FQvoronoi      Fsummary       Fvoronoi\n\
    + FIDs\n\
    +\n\
    + Gvertices      Gpoints        Gall_points    Gno_planes     Ginner\n\
    + Gcentrums      Ghyperplanes   Gridges        Gouter         GDrop_dim\n\
    +\n\
    + PArea_keep     Pdrop d0:0D0   Pgood          PFacet_area_keep\n\
    + PGood_neighbors PMerge_keep   Poutput_forced Pprecision_not\n\
    +\n\
    + QG_vertex_good Qsearch_1st    Qupper_voronoi QV_point_good  Qzinfinite\n\
    + T4_trace       Tcheck_often   Tstatistics    Tverify        Tz_stdout\n\
    + TFacet_log     TInput_file    TPoint_trace   TMerge_trace   TOutput_file\n\
    + TWide_trace    TVertex_stop   TCone_stop\n\
    +\n\
    + Angle_max      Centrum_size   Random_dist    Wide_outside\n\
    +";
    +
    +/*---------------------------------
    +
    +  main( argc, argv )
    +    processes the command line, calls qhull() to do the work, and exits
    +
    +  design:
    +    initializes data structures
    +    reads points
    +    finishes initialization
    +    computes convex hull and other structures
    +    checks the result
    +    writes the output
    +    frees memory
    +*/
    +int main(int argc, char *argv[]) {
    +  int curlong, totlong; /* used !qh_NOmem */
    +  int exitcode, numpoints, dim;
    +  coordT *points;
    +  boolT ismalloc;
    +
    +  QHULL_LIB_CHECK /* Check for compatible library */
    +
    +  if ((argc == 1) && isatty( 0 /*stdin*/)) {
    +    fprintf(stdout, qh_prompt2, qh_version);
    +    exit(qh_ERRnone);
    +  }
    +  if (argc > 1 && *argv[1] == '-' && !*(argv[1]+1)) {
    +    fprintf(stdout, qh_prompta, qh_version,
    +                qh_promptb, qh_promptc, qh_promptd, qh_prompte);
    +    exit(qh_ERRnone);
    +  }
    +  if (argc > 1 && *argv[1] == '.' && !*(argv[1]+1)) {
    +    fprintf(stdout, qh_prompt3, qh_version);
    +    exit(qh_ERRnone);
    +  }
    +  if (argc > 1 && *argv[1] == '-' && *(argv[1]+1)=='V') {
    +      fprintf(stdout, "%s\n", qh_version2);
    +      exit(qh_ERRnone);
    +  }
    +  qh_init_A(stdin, stdout, stderr, argc, argv);  /* sets qh qhull_command */
    +  exitcode= setjmp(qh errexit); /* simple statement for CRAY J916 */
    +  if (!exitcode) {
    +    qh NOerrexit= False;
    +    qh_option("voronoi  _bbound-last  _coplanar-keep", NULL, NULL);
    +    qh DELAUNAY= True;     /* 'v'   */
    +    qh VORONOI= True;
    +    qh SCALElast= True;    /* 'Qbb' */
    +    qh_checkflags(qh qhull_command, hidden_options);
    +    qh_initflags(qh qhull_command);
    +    points= qh_readpoints(&numpoints, &dim, &ismalloc);
    +    if (dim >= 5) {
    +      qh_option("_merge-exact", NULL, NULL);
    +      qh MERGEexact= True; /* 'Qx' always */
    +    }
    +    qh_init_B(points, numpoints, dim, ismalloc);
    +    qh_qhull();
    +    qh_check_output();
    +    qh_produce_output();
    +    if (qh VERIFYoutput && !qh FORCEoutput && !qh STOPpoint && !qh STOPcone)
    +      qh_check_points();
    +    exitcode= qh_ERRnone;
    +  }
    +  qh NOerrexit= True;  /* no more setjmp */
    +#ifdef qh_NOmem
    +  qh_freeqhull(qh_ALL);
    +#else
    +  qh_freeqhull(!qh_ALL);
    +  qh_memfreeshort(&curlong, &totlong);
    +  if (curlong || totlong)
    +    qh_fprintf_stderr(6263, "qhull internal warning (main): did not free %d bytes of long memory(%d pieces)\n",
    +       totlong, curlong);
    +#endif
    +  return exitcode;
    +} /* main */
    +
    diff --git a/xs/src/qhull/src/qvoronoi/qvoronoi.pro b/xs/src/qhull/src/qvoronoi/qvoronoi.pro
    new file mode 100644
    index 000000000..4646c8447
    --- /dev/null
    +++ b/xs/src/qhull/src/qvoronoi/qvoronoi.pro
    @@ -0,0 +1,9 @@
    +# -------------------------------------------------
    +# qvoronoi.pro -- Qt project file for qvoronoi.exe
    +# -------------------------------------------------
    +
    +include(../qhull-app-c.pri)
    +
    +TARGET = qvoronoi
    +
    +SOURCES += qvoronoi.c
    diff --git a/xs/src/qhull/src/qvoronoi/qvoronoi_r.c b/xs/src/qhull/src/qvoronoi/qvoronoi_r.c
    new file mode 100644
    index 000000000..6323c8b49
    --- /dev/null
    +++ b/xs/src/qhull/src/qvoronoi/qvoronoi_r.c
    @@ -0,0 +1,305 @@
    +/*
      ---------------------------------
    +
    +   qvoronoi.c
    +     compute Voronoi diagrams and furthest-point Voronoi
    +     diagrams using qhull
    +
    +   see unix.c for full interface
    +
    +   Copyright (c) 1993-2015, The Geometry Center
    +*/
    +
    +#include "libqhull_r/libqhull_r.h"
    +
    +#include 
    +#include 
    +#include 
    +#include 
    +#include 
    +
    +#if __cplusplus
    +extern "C" {
    +  int isatty(int);
    +}
    +
    +#elif _MSC_VER
    +#include 
    +#define isatty _isatty
    +/* int _isatty(int); */
    +
    +#else
    +int isatty(int);  /* returns 1 if stdin is a tty
    +                   if "Undefined symbol" this can be deleted along with call in main() */
    +#endif
    +
    +/*---------------------------------
    +
    +  qh_prompt
    +    long prompt for qhull
    +
    +  notes:
    +    restricted version of libqhull.c
    +
    +  see:
    +    concise prompt below
    +*/
    +
    +/* duplicated in qvoron_f.htm and qvoronoi.htm
    +   QJ and Qt are deprecated, but allowed for backwards compatibility
    +*/
    +char hidden_options[]=" d n m v H U Qb QB Qc Qf Qg Qi Qm Qr QR Qv Qx TR E V Fa FA FC Fp FS Ft FV Pv Gt Q0 Q1 Q2 Q3 Q4 Q5 Q6 Q7 Q8 Q9 ";
    +
    +char qh_prompta[]= "\n\
    +qvoronoi- compute the Voronoi diagram\n\
    +    http://www.qhull.org  %s\n\
    +\n\
    +input (stdin):\n\
    +    first lines: dimension and number of points (or vice-versa).\n\
    +    other lines: point coordinates, best if one point per line\n\
    +    comments:    start with a non-numeric character\n\
    +\n\
    +options:\n\
    +    Qu   - compute furthest-site Voronoi diagram\n\
    +\n\
    +Qhull control options:\n\
    +    Qz   - add point-at-infinity to Voronoi diagram\n\
    +%s%s%s%s";  /* split up qh_prompt for Visual C++ */
    +char qh_promptb[]= "\
    +    Qs   - search all points for the initial simplex\n\
    +    QGn  - Voronoi vertices if visible from point n, -n if not\n\
    +    QVn  - Voronoi vertices for input point n, -n if not\n\
    +\n\
    +";
    +char qh_promptc[]= "\
    +Trace options:\n\
    +    T4   - trace at level n, 4=all, 5=mem/gauss, -1= events\n\
    +    Tc   - check frequently during execution\n\
    +    Ts   - statistics\n\
    +    Tv   - verify result: structure, convexity, and in-circle test\n\
    +    Tz   - send all output to stdout\n\
    +    TFn  - report summary when n or more facets created\n\
    +    TI file - input data from file, no spaces or single quotes\n\
    +    TO file - output results to file, may be enclosed in single quotes\n\
    +    TPn  - turn on tracing when point n added to hull\n\
    +     TMn - turn on tracing at merge n\n\
    +     TWn - trace merge facets when width > n\n\
    +    TVn  - stop qhull after adding point n, -n for before (see TCn)\n\
    +     TCn - stop qhull after building cone for point n (see TVn)\n\
    +\n\
    +Precision options:\n\
    +    Cn   - radius of centrum (roundoff added).  Merge facets if non-convex\n\
    +     An  - cosine of maximum angle.  Merge facets if cosine > n or non-convex\n\
    +           C-0 roundoff, A-0.99/C-0.01 pre-merge, A0.99/C0.01 post-merge\n\
    +    Rn   - randomly perturb computations by a factor of [1-n,1+n]\n\
    +    Wn   - min facet width for non-coincident point (before roundoff)\n\
    +\n\
    +Output formats (may be combined; if none, produces a summary to stdout):\n\
    +    s    - summary to stderr\n\
    +    p    - Voronoi vertices\n\
    +    o    - OFF format (dim, Voronoi vertices, and Voronoi regions)\n\
    +    i    - Delaunay regions (use 'Pp' to avoid warning)\n\
    +    f    - facet dump\n\
    +\n\
    +";
    +char qh_promptd[]= "\
    +More formats:\n\
    +    Fc   - count plus coincident points (by Voronoi vertex)\n\
    +    Fd   - use cdd format for input (homogeneous with offset first)\n\
    +    FD   - use cdd format for output (offset first)\n\
    +    FF   - facet dump without ridges\n\
    +    Fi   - separating hyperplanes for bounded Voronoi regions\n\
    +    FI   - ID for each Voronoi vertex\n\
    +    Fm   - merge count for each Voronoi vertex (511 max)\n\
    +    Fn   - count plus neighboring Voronoi vertices for each Voronoi vertex\n\
    +    FN   - count and Voronoi vertices for each Voronoi region\n\
    +    Fo   - separating hyperplanes for unbounded Voronoi regions\n\
    +    FO   - options and precision constants\n\
    +    FP   - nearest point and distance for each coincident point\n\
    +    FQ   - command used for qvoronoi\n\
    +    Fs   - summary: #int (8), dimension, #points, tot vertices, tot facets,\n\
    +                    for output: #Voronoi regions, #Voronoi vertices,\n\
    +                                #coincident points, #non-simplicial regions\n\
    +                    #real (2), max outer plane and min vertex\n\
    +    Fv   - Voronoi diagram as Voronoi vertices between adjacent input sites\n\
    +    Fx   - extreme points of Delaunay triangulation (on convex hull)\n\
    +\n\
    +";
    +char qh_prompte[]= "\
    +Geomview options (2-d only)\n\
    +    Ga   - all points as dots\n\
    +     Gp  -  coplanar points and vertices as radii\n\
    +     Gv  -  vertices as spheres\n\
    +    Gi   - inner planes only\n\
    +     Gn  -  no planes\n\
    +     Go  -  outer planes only\n\
    +    Gc   - centrums\n\
    +    Gh   - hyperplane intersections\n\
    +    Gr   - ridges\n\
    +    GDn  - drop dimension n in 3-d and 4-d output\n\
    +\n\
    +Print options:\n\
    +    PAn  - keep n largest Voronoi vertices by 'area'\n\
    +    Pdk:n - drop facet if normal[k] <= n (default 0.0)\n\
    +    PDk:n - drop facet if normal[k] >= n\n\
    +    Pg   - print good Voronoi vertices (needs 'QGn' or 'QVn')\n\
    +    PFn  - keep Voronoi vertices whose 'area' is at least n\n\
    +    PG   - print neighbors of good Voronoi vertices\n\
    +    PMn  - keep n Voronoi vertices with most merges\n\
    +    Po   - force output.  If error, output neighborhood of facet\n\
    +    Pp   - do not report precision problems\n\
    +\n\
    +    .    - list of all options\n\
    +    -    - one line descriptions of all options\n\
    +    -V   - version\n\
    +";
    +/* for opts, don't assign 'e' or 'E' to a flag (already used for exponent) */
    +
    +/*---------------------------------
    +
    +  qh_prompt2
    +    synopsis for qhull
    +*/
    +char qh_prompt2[]= "\n\
    +qvoronoi- compute the Voronoi diagram.  Qhull %s\n\
    +    input (stdin): dimension, number of points, point coordinates\n\
    +    comments start with a non-numeric character\n\
    +\n\
    +options (qvoronoi.htm):\n\
    +    Qu   - compute furthest-site Voronoi diagram\n\
    +    Tv   - verify result: structure, convexity, and in-circle test\n\
    +    .    - concise list of all options\n\
    +    -    - one-line description of all options\n\
    +    -V   - version\n\
    +\n\
    +output options (subset):\n\
    +    s    - summary of results (default)\n\
    +    p    - Voronoi vertices\n\
    +    o    - OFF file format (dim, Voronoi vertices, and Voronoi regions)\n\
    +    FN   - count and Voronoi vertices for each Voronoi region\n\
    +    Fv   - Voronoi diagram as Voronoi vertices between adjacent input sites\n\
    +    Fi   - separating hyperplanes for bounded regions, 'Fo' for unbounded\n\
    +    G    - Geomview output (2-d only)\n\
    +    QVn  - Voronoi vertices for input point n, -n if not\n\
    +    TO file- output results to file, may be enclosed in single quotes\n\
    +\n\
    +examples:\n\
    +rbox c P0 D2 | qvoronoi s o         rbox c P0 D2 | qvoronoi Fi\n\
    +rbox c P0 D2 | qvoronoi Fo          rbox c P0 D2 | qvoronoi Fv\n\
    +rbox c P0 D2 | qvoronoi s Qu Fv     rbox c P0 D2 | qvoronoi Qu Fo\n\
    +rbox c G1 d D2 | qvoronoi s p       rbox c P0 D2 | qvoronoi s Fv QV0\n\
    +\n\
    +";
    +/* for opts, don't assign 'e' or 'E' to a flag (already used for exponent) */
    +
    +/*---------------------------------
    +
    +  qh_prompt3
    +    concise prompt for qhull
    +*/
    +char qh_prompt3[]= "\n\
    +Qhull %s.\n\
    +Except for 'F.' and 'PG', upper-case options take an argument.\n\
    +\n\
    + OFF_format     p_vertices     i_delaunay     summary        facet_dump\n\
    +\n\
    + Fcoincident    Fd_cdd_in      FD_cdd_out     FF-dump-xridge Fi_bounded\n\
    + Fxtremes       Fmerges        Fneighbors     FNeigh_region  FOptions\n\
    + Fo_unbounded   FPoint_near    FQvoronoi      Fsummary       Fvoronoi\n\
    + FIDs\n\
    +\n\
    + Gvertices      Gpoints        Gall_points    Gno_planes     Ginner\n\
    + Gcentrums      Ghyperplanes   Gridges        Gouter         GDrop_dim\n\
    +\n\
    + PArea_keep     Pdrop d0:0D0   Pgood          PFacet_area_keep\n\
    + PGood_neighbors PMerge_keep   Poutput_forced Pprecision_not\n\
    +\n\
    + QG_vertex_good Qsearch_1st    Qupper_voronoi QV_point_good  Qzinfinite\n\
    + T4_trace       Tcheck_often   Tstatistics    Tverify        Tz_stdout\n\
    + TFacet_log     TInput_file    TPoint_trace   TMerge_trace   TOutput_file\n\
    + TWide_trace    TVertex_stop   TCone_stop\n\
    +\n\
    + Angle_max      Centrum_size   Random_dist    Wide_outside\n\
    +";
    +
    +/*---------------------------------
    +
    +  main( argc, argv )
    +    processes the command line, calls qhull() to do the work, and exits
    +
    +  design:
    +    initializes data structures
    +    reads points
    +    finishes initialization
    +    computes convex hull and other structures
    +    checks the result
    +    writes the output
    +    frees memory
    +*/
    +int main(int argc, char *argv[]) {
    +  int curlong, totlong; /* used !qh_NOmem */
    +  int exitcode, numpoints, dim;
    +  coordT *points;
    +  boolT ismalloc;
    +  qhT qh_qh;
    +  qhT *qh= &qh_qh;
    +
    +  QHULL_LIB_CHECK /* Check for compatible library */
    +
    +  if ((argc == 1) && isatty( 0 /*stdin*/)) {
    +    fprintf(stdout, qh_prompt2, qh_version);
    +    exit(qh_ERRnone);
    +  }
    +  if (argc > 1 && *argv[1] == '-' && !*(argv[1]+1)) {
    +    fprintf(stdout, qh_prompta, qh_version,
    +                qh_promptb, qh_promptc, qh_promptd, qh_prompte);
    +    exit(qh_ERRnone);
    +  }
    +  if (argc > 1 && *argv[1] == '.' && !*(argv[1]+1)) {
    +    fprintf(stdout, qh_prompt3, qh_version);
    +    exit(qh_ERRnone);
    +  }
    +  if (argc > 1 && *argv[1] == '-' && *(argv[1]+1)=='V') {
    +      fprintf(stdout, "%s\n", qh_version2);
    +      exit(qh_ERRnone);
    +  }
    +  qh_init_A(qh, stdin, stdout, stderr, argc, argv);  /* sets qh->qhull_command */
    +  exitcode= setjmp(qh->errexit); /* simple statement for CRAY J916 */
    +  if (!exitcode) {
    +    qh->NOerrexit = False;
    +    qh_option(qh, "voronoi  _bbound-last  _coplanar-keep", NULL, NULL);
    +    qh->DELAUNAY= True;     /* 'v'   */
    +    qh->VORONOI= True;
    +    qh->SCALElast= True;    /* 'Qbb' */
    +    qh_checkflags(qh, qh->qhull_command, hidden_options);
    +    qh_initflags(qh, qh->qhull_command);
    +    points= qh_readpoints(qh, &numpoints, &dim, &ismalloc);
    +    if (dim >= 5) {
    +      qh_option(qh, "_merge-exact", NULL, NULL);
    +      qh->MERGEexact= True; /* 'Qx' always */
    +    }
    +    qh_init_B(qh, points, numpoints, dim, ismalloc);
    +    qh_qhull(qh);
    +    qh_check_output(qh);
    +    qh_produce_output(qh);
    +    if (qh->VERIFYoutput && !qh->FORCEoutput && !qh->STOPpoint && !qh->STOPcone)
    +      qh_check_points(qh);
    +    exitcode= qh_ERRnone;
    +  }
    +  qh->NOerrexit= True;  /* no more setjmp */
    +#ifdef qh_NOmem
    +  qh_freeqhull(qh, qh_ALL);
    +#else
    +  qh_freeqhull(qh, !qh_ALL);
    +  qh_memfreeshort(qh, &curlong, &totlong);
    +  if (curlong || totlong)
    +    qh_fprintf_stderr(6263, "qhull internal warning (main): did not free %d bytes of long memory(%d pieces)\n",
    +       totlong, curlong);
    +#endif
    +  return exitcode;
    +} /* main */
    +
    diff --git a/xs/src/qhull/src/rbox/rbox.c b/xs/src/qhull/src/rbox/rbox.c
    new file mode 100644
    index 000000000..d7c51b1aa
    --- /dev/null
    +++ b/xs/src/qhull/src/rbox/rbox.c
    @@ -0,0 +1,88 @@
    +/*
      ---------------------------------
    +
    +   rbox.c
    +     rbox program for generating input points for qhull.
    +
    +   notes:
    +     50 points generated for 'rbox D4'
    +
    +*/
    +
    +#include "libqhull/libqhull.h"
    +#include "libqhull/random.h"
    +
    +#include 
    +#include 
    +#include 
    +#include 
    +
    +#ifdef _MSC_VER  /* Microsoft Visual C++ -- warning level 4 */
    +#pragma warning( disable : 4706)  /* assignment within conditional function */
    +#endif
    +
    +char prompt[]= "\n\
    +-rbox- generate various point distributions.  Default is random in cube.\n\
    +\n\
    +args (any order, space separated):                    Version: 2016/01/18\n\
    +  3000    number of random points in cube, lens, spiral, sphere or grid\n\
    +  D3      dimension 3-d\n\
    +  c       add a unit cube to the output ('c G2.0' sets size)\n\
    +  d       add a unit diamond to the output ('d G2.0' sets size)\n\
    +  l       generate a regular 3-d spiral\n\
    +  r       generate a regular polygon, ('r s Z1 G0.1' makes a cone)\n\
    +  s       generate cospherical points\n\
    +  x       generate random points in simplex, may use 'r' or 'Wn'\n\
    +  y       same as 'x', plus simplex\n\
    +  Cn,r,m  add n nearly coincident points within radius r of m points\n\
    +  Pn,m,r  add point [n,m,r] first, pads with 0, maybe repeated\n\
    +\n\
    +  Ln      lens distribution of radius n.  Also 's', 'r', 'G', 'W'.\n\
    +  Mn,m,r  lattice(Mesh) rotated by [n,-m,0], [m,n,0], [0,0,r], ...\n\
    +          '27 M1,0,1' is {0,1,2} x {0,1,2} x {0,1,2}.  Try 'M3,4 z'.\n\
    +  W0.1    random distribution within 0.1 of the cube's or sphere's surface\n\
    +  Z0.5 s  random points in a 0.5 disk projected to a sphere\n\
    +  Z0.5 s G0.6 same as Z0.5 within a 0.6 gap\n\
    +\n\
    +  Bn      bounding box coordinates, default %2.2g\n\
    +  h       output as homogeneous coordinates for cdd\n\
    +  n       remove command line from the first line of output\n\
    +  On      offset coordinates by n\n\
    +  t       use time as the random number seed(default is command line)\n\
    +  tn      use n as the random number seed\n\
    +  z       print integer coordinates, default 'Bn' is %2.2g\n\
    +";
    +
    +/*--------------------------------------------
    +-rbox-  main procedure of rbox application
    +*/
    +int main(int argc, char **argv) {
    +  char *command;
    +  int command_size;
    +  int return_status;
    +
    +  QHULL_LIB_CHECK_RBOX
    +
    +  if (argc == 1) {
    +    printf(prompt, qh_DEFAULTbox, qh_DEFAULTzbox);
    +    return 1;
    +  }
    +  if (argc == 2 && strcmp(argv[1], "D4")==0)
    +    qh_fprintf_stderr(0, "\nStarting the rbox smoketest for qhull.  An immediate failure indicates\nthat non-reentrant rbox was linked to reentrant routines.  An immediate\nfailure of qhull may indicate that qhull was linked to the wrong\nqhull library.  Also try 'rbox D4 | qhull T1'\n");
    +
    +  command_size= qh_argv_to_command_size(argc, argv);
    +  if ((command= (char *)qh_malloc((size_t)command_size))) {
    +    if (!qh_argv_to_command(argc, argv, command, command_size)) {
    +      qh_fprintf_stderr(6264, "rbox internal error: allocated insufficient memory (%d) for arguments\n", command_size);
    +      return_status= qh_ERRinput;
    +    }else{
    +      return_status= qh_rboxpoints(stdout, stderr, command);
    +    }
    +    qh_free(command);
    +  }else {
    +    qh_fprintf_stderr(6265, "rbox error: insufficient memory for %d bytes\n", command_size);
    +    return_status= qh_ERRmem;
    +  }
    +  return return_status;
    +}/*main*/
    +
    diff --git a/xs/src/qhull/src/rbox/rbox.pro b/xs/src/qhull/src/rbox/rbox.pro
    new file mode 100644
    index 000000000..6c21bdb6d
    --- /dev/null
    +++ b/xs/src/qhull/src/rbox/rbox.pro
    @@ -0,0 +1,9 @@
    +# -------------------------------------------------
    +# rbox.pro -- Qt project for rbox.exe with libqhullstatic
    +# -------------------------------------------------
    +
    +include(../qhull-app-c.pri)
    +
    +TARGET = rbox
    +
    +SOURCES += rbox.c
    diff --git a/xs/src/qhull/src/rbox/rbox_r.c b/xs/src/qhull/src/rbox/rbox_r.c
    new file mode 100644
    index 000000000..6ec74d914
    --- /dev/null
    +++ b/xs/src/qhull/src/rbox/rbox_r.c
    @@ -0,0 +1,78 @@
    +
    +/*
      ---------------------------------
    +
    +   rbox.c
    +     rbox program for generating input points for qhull.
    +
    +   notes:
    +     50 points generated for 'rbox D4'
    +
    +*/
    +
    +#include "libqhull_r/libqhull_r.h"
    +#include "libqhull/random_r.h"
    +
    +#include 
    +#include 
    +#include 
    +#include 
    +
    +#ifdef _MSC_VER  /* Microsoft Visual C++ -- warning level 4 */
    +#pragma warning( disable : 4706)  /* assignment within conditional function */
    +#endif
    +
    +char prompt[]= "\n\
    +-rbox- generate various point distributions.  Default is random in cube.\n\
    +\n\
    +args (any order, space separated):                    Version: 2016/01/18 r\n\
    +  3000    number of random points in cube, lens, spiral, sphere or grid\n\
    +  D3      dimension 3-d\n\
    +  c       add a unit cube to the output ('c G2.0' sets size)\n\
    +  d       add a unit diamond to the output ('d G2.0' sets size)\n\
    +  l       generate a regular 3-d spiral\n\
    +  r       generate a regular polygon, ('r s Z1 G0.1' makes a cone)\n\
    +  s       generate cospherical points\n\
    +  x       generate random points in simplex, may use 'r' or 'Wn'\n\
    +  y       same as 'x', plus simplex\n\
    +  Cn,r,m  add n nearly coincident points within radius r of m points\n\
    +  Pn,m,r  add point [n,m,r] first, pads with 0, maybe repeated\n\
    +\n\
    +  Ln      lens distribution of radius n.  Also 's', 'r', 'G', 'W'.\n\
    +  Mn,m,r  lattice(Mesh) rotated by [n,-m,0], [m,n,0], [0,0,r], ...\n\
    +          '27 M1,0,1' is {0,1,2} x {0,1,2} x {0,1,2}.  Try 'M3,4 z'.\n\
    +  W0.1    random distribution within 0.1 of the cube's or sphere's surface\n\
    +  Z0.5 s  random points in a 0.5 disk projected to a sphere\n\
    +  Z0.5 s G0.6 same as Z0.5 within a 0.6 gap\n\
    +\n\
    +  Bn      bounding box coordinates, default %2.2g\n\
    +  h       output as homogeneous coordinates for cdd\n\
    +  n       remove command line from the first line of output\n\
    +  On      offset coordinates by n\n\
    +  t       use time as the random number seed(default is command line)\n\
    +  tn      use n as the random number seed\n\
    +  z       print integer coordinates, default 'Bn' is %2.2g\n\
    +";
    +
    +/*--------------------------------------------
    +-rbox-  main procedure of rbox application
    +*/
    +int main(int argc, char **argv) {
    +  int return_status;
    +  qhT qh_qh;
    +  qhT *qh= &qh_qh;
    +
    +  QHULL_LIB_CHECK_RBOX
    +
    +  if (argc == 1) {
    +    printf(prompt, qh_DEFAULTbox, qh_DEFAULTzbox);
    +    return 1;
    +  }
    +  if (argc == 2 && strcmp(argv[1], "D4")==0)
    +    qh_fprintf_stderr(0, "\nStarting the rbox smoketest for qhull.  An immediate failure indicates\nthat reentrant rbox was linked to non-reentrant routines.  An immediate\nfailure of qhull may indicate that qhull was linked to the wrong\nqhull library.  Also try 'rbox D4 | qhull T1'\n");
    +
    +  qh_init_A(qh, stdin, stdout, stderr, argc, argv);  /*no qh_errexit, sets qh->qhull_command */
    +  return_status= qh_rboxpoints(qh, qh->qhull_command); /* Traps its own errors, qh_errexit_rbox() */
    +  return return_status;
    +}/*main*/
    +
    diff --git a/xs/src/qhull/src/testqset/testqset.c b/xs/src/qhull/src/testqset/testqset.c
    new file mode 100644
    index 000000000..61057eef9
    --- /dev/null
    +++ b/xs/src/qhull/src/testqset/testqset.c
    @@ -0,0 +1,891 @@
    +/*
      ---------------------------------
    +
    +   testset.c -- test qset.c and its use of mem.c
    +
    +   The test sets are pointers to int.  Normally a set is a pointer to a type (e.g., facetT, ridgeT, etc.).
    +   For consistency in notation, an "int" is typedef'd to i2T
    +
    +Functions and macros from qset.h.  Counts occurrences in this test.  Does not correspond to thoroughness.
    +    qh_setaddsorted -- 4 tests
    +    qh_setaddnth -- 1 test
    +    qh_setappend -- 7 tests
    +    qh_setappend_set -- 1 test
    +    qh_setappend2ndlast -- 1 test
    +    qh_setcheck -- lots of tests
    +    qh_setcompact -- 7 tests
    +    qh_setcopy -- 3 tests
    +    qh_setdel -- 1 tests
    +    qh_setdellast -- 1 tests
    +    qh_setdelnth -- 2 tests
    +    qh_setdelnthsorted -- 2 tests
    +    qh_setdelsorted -- 1 test
    +    qh_setduplicate -- not testable here
    +    qh_setequal -- 4 tests
    +    qh_setequal_except -- 2 tests
    +    qh_setequal_skip -- 2 tests
    +    qh_setfree -- 11+ tests
    +    qh_setfree2 -- not testable here
    +    qh_setfreelong -- 2 tests
    +    qh_setin -- 3 tests
    +    qh_setindex -- 4 tests
    +    qh_setlarger -- 1 test
    +    qh_setlast -- 2 tests
    +    qh_setnew -- 6 tests
    +    qh_setnew_delnthsorted
    +    qh_setprint -- tested elsewhere
    +    qh_setreplace -- 1 test
    +    qh_setsize -- 9+ tests
    +    qh_settemp -- 2 tests
    +    qh_settempfree -- 1 test
    +    qh_settempfree_all -- 1 test
    +    qh_settemppop -- 1 test
    +    qh_settemppush -- 1 test
    +    qh_settruncate -- 3 tests
    +    qh_setunique -- 3 tests
    +    qh_setzero -- 1 test
    +    FOREACHint_ -- 2 test
    +    FOREACHint4_
    +    FOREACHint_i_ -- 1 test
    +    FOREACHintreverse_
    +    FOREACHintreverse12_
    +    FOREACHsetelement_ -- 1 test
    +    FOREACHsetelement_i_ -- 1 test
    +    FOREACHsetelementreverse_ -- 1 test
    +    FOREACHsetelementreverse12_ -- 1 test
    +    SETelem_ -- 3 tests
    +    SETelemaddr_ -- 2 tests
    +    SETelemt_ -- not tested (generic)
    +    SETempty_ -- 1 test
    +    SETfirst_ -- 4 tests
    +    SETfirstt_ -- 2 tests
    +    SETindex_ -- 2 tests
    +    SETref_ -- 2 tests
    +    SETreturnsize_ -- 2 tests
    +    SETsecond_ -- 1 test
    +    SETsecondt_ -- 2 tests
    +    SETtruncate_ -- 2 tests
    +
    +    Copyright (c) 2012-2015 C.B. Barber. All rights reserved.
    +    $Id: //main/2015/qhull/src/testqset/testqset.c#4 $$Change: 2062 $
    +    $DateTime: 2016/01/17 13:13:18 $$Author: bbarber $
    +*/
    +
    +#include "libqhull/user.h"  /* QHULL_CRTDBG */
    +#include "libqhull/qset.h"
    +#include "libqhull/mem.h"
    +
    +#include 
    +#include 
    +#include 
    +#include 
    +
    +typedef int i2T;
    +#define MAXerrorCount 100 /* quit after n errors */
    +
    +#define FOREACHint_( ints ) FOREACHsetelement_( i2T, ints, i2)
    +#define FOREACHint4_( ints ) FOREACHsetelement_( i2T, ints, i4)
    +#define FOREACHint_i_( ints ) FOREACHsetelement_i_( i2T, ints, i2)
    +#define FOREACHintreverse_( ints ) FOREACHsetelementreverse_( i2T, ints, i2)
    +#define FOREACHintreverse12_( ints ) FOREACHsetelementreverse12_( i2T, ints, i2)
    +
    +enum {
    +    MAXint= 0x7fffffff,
    +};
    +
    +char prompt[]= "testqset N [M] [T5] -- Test qset.c and mem.c\n\
    +  \n\
    +  If this test fails then qhull will not work.\n\
    +  \n\
    +  Test qsets of 0..N integers with a check every M iterations (default ~log10)\n\
    +  Additional checking and logging if M is 1\n\
    +  \n\
    +  T5 turns on memory logging (qset does not log)\n\
    +  \n\
    +  For example:\n\
    +    testqset 10000\n\
    +";
    +
    +int error_count= 0;  /* Global error_count.  checkSetContents() keeps its own error count.  It exits on too many errors */
    +
    +/* Macros normally defined in geom.h */
    +#define fmax_( a,b )  ( ( a ) < ( b ) ? ( b ) : ( a ) )
    +
    +/* Macros normally defined in user.h */
    +
    +#define realT double
    +#define qh_MEMalign ((int)(fmax_(sizeof(realT), sizeof(void *))))
    +#define qh_MEMbufsize 0x10000       /* allocate 64K memory buffers */
    +#define qh_MEMinitbuf 0x20000      /* initially allocate 128K buffer */
    +
    +/* Macros normally defined in QhullSet.h */
    +
    +
    +/* Functions normally defined in user.h for usermem.c */
    +
    +void    qh_exit(int exitcode);
    +void    qh_fprintf_stderr(int msgcode, const char *fmt, ... );
    +void    qh_free(void *mem);
    +void   *qh_malloc(size_t size);
    +
    +/* Normally defined in user.c */
    +
    +void    qh_errexit(int exitcode, void *f, void *r)
    +{
    +    (void)f; /* unused */
    +    (void)r; /* unused */
    +    qh_exit(exitcode);
    +}
    +
    +/* Normally defined in userprintf.c */
    +
    +void    qh_fprintf(FILE *fp, int msgcode, const char *fmt, ... )
    +{
    +    static int needs_cr= 0;  /* True if qh_fprintf needs a CR */
    +
    +    size_t fmtlen= strlen(fmt);
    +    va_list args;
    +
    +    if (!fp) {
    +        /* Do not use qh_fprintf_stderr.  This is a standalone program */
    +        fprintf(stderr, "QH6232 qh_fprintf: fp not defined for '%s'", fmt);
    +        qh_errexit(6232, NULL, NULL);
    +    }
    +    if(fmtlen>0){
    +        if(fmt[fmtlen-1]=='\n'){
    +            if(needs_cr && fmtlen>1){
    +                fprintf(fp, "\n");
    +            }
    +            needs_cr= 0;
    +        }else{
    +            needs_cr= 1;
    +        }
    +    }
    +    if(msgcode>=6000 && msgcode<7000){
    +        fprintf(fp, "Error TQ%d ", msgcode);
    +    }
    +    va_start(args, fmt);
    +    vfprintf(fp, fmt, args);
    +    va_end(args);
    +}
    +
    +/* Defined below in order of use */
    +int main(int argc, char **argv);
    +void readOptions(int argc, char **argv, const char *promptstr, int *numInts, int *checkEvery, int *traceLevel);
    +void setupMemory(int tracelevel, int numInts, int **intarray);
    +
    +void testSetappendSettruncate(int numInts, int *intarray, int checkEvery);
    +void testSetdelSetadd(int numInts, int *intarray, int checkEvery);
    +void testSetappendSet(int numInts, int *intarray, int checkEvery);
    +void testSetcompactCopy(int numInts, int *intarray, int checkEvery);
    +void testSetequalInEtc(int numInts, int *intarray, int checkEvery);
    +void testSettemp(int numInts, int *intarray, int checkEvery);
    +void testSetlastEtc(int numInts, int *intarray, int checkEvery);
    +void testSetdelsortedEtc(int numInts, int *intarray, int checkEvery);
    +
    +int log_i(setT *set, const char *s, int i, int numInts, int checkEvery);
    +void checkSetContents(const char *name, setT *set, int count, int rangeA, int rangeB, int rangeC);
    +
    +int main(int argc, char **argv) {
    +    int *intarray= NULL;
    +    int numInts;
    +    int checkEvery= MAXint;
    +    int curlong, totlong;
    +    int traceLevel= 4; /* 4 normally, no tracing since qset does not log.  5 for memory tracing */
    +
    +#if defined(_MSC_VER) && defined(_DEBUG) && defined(QHULL_CRTDBG)  /* user.h */
    +    _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 );
    +#endif
    +
    +    readOptions(argc, argv, prompt, &numInts, &checkEvery, &traceLevel);
    +    setupMemory(traceLevel, numInts, &intarray);
    +
    +    testSetappendSettruncate(numInts, intarray, checkEvery);
    +    testSetdelSetadd(numInts, intarray, checkEvery);
    +    testSetappendSet(numInts, intarray, checkEvery);
    +    testSetcompactCopy(numInts, intarray, checkEvery);
    +    testSetequalInEtc(numInts, intarray, checkEvery);
    +    testSettemp(numInts, intarray, checkEvery);
    +    testSetlastEtc(numInts, intarray, checkEvery);
    +    testSetdelsortedEtc(numInts, intarray, checkEvery);
    +    printf("\n\nNot testing qh_setduplicate and qh_setfree2.\n  These routines use heap-allocated set contents.  See qhull tests.\n");
    +
    +    qh_memstatistics(stdout);
    +    qh_memfreeshort(&curlong, &totlong);
    +    if (curlong || totlong){
    +        qh_fprintf(stderr, 8043, "qh_memfreeshort: did not free %d bytes of long memory(%d pieces)\n", totlong, curlong);
    +        error_count++;
    +    }
    +    if(error_count){
    +        qh_fprintf(stderr, 8012, "testqset: %d errors\n\n", error_count);
    +        exit(1);
    +    }else{
    +        printf("testqset: OK\n\n");
    +    }
    +    return 0;
    +}/*main*/
    +
    +void readOptions(int argc, char **argv, const char *promptstr, int *numInts, int *checkEvery, int *traceLevel)
    +{
    +    long numIntsArg;
    +    long checkEveryArg;
    +    char *endp;
    +    int isTracing= 0;
    +
    +    if (argc < 2 || argc > 4) {
    +        printf("%s", promptstr);
    +        exit(0);
    +    }
    +    numIntsArg= strtol(argv[1], &endp, 10);
    +    if(numIntsArg<1){
    +        qh_fprintf(stderr, 6301, "First argument should be 1 or greater.  Got '%s'\n", argv[1]);
    +        exit(1);
    +    }
    +    if(numIntsArg>MAXint){
    +        qh_fprintf(stderr, 6302, "qset does not currently support 64-bit ints.  Maximum count is %d\n", MAXint);
    +        exit(1);
    +    }
    +    *numInts= (int)numIntsArg;
    +
    +    if(argc==3 && argv[2][0]=='T' && argv[2][1]=='5' ){
    +        isTracing= 1;
    +        *traceLevel= 5;
    +    }
    +    if(argc==4 || (argc==3 && !isTracing)){
    +        checkEveryArg= strtol(argv[2], &endp, 10);
    +        if(checkEveryArg<1){
    +            qh_fprintf(stderr, 6321, "checkEvery argument should be 1 or greater.  Got '%s'\n", argv[2]);
    +            exit(1);
    +        }
    +        if(checkEveryArg>MAXint){
    +            qh_fprintf(stderr, 6322, "qset does not currently support 64-bit ints.  Maximum checkEvery is %d\n", MAXint);
    +            exit(1);
    +        }
    +        if(argc==4){
    +            if(argv[3][0]=='T' && argv[3][1]=='5' ){
    +                isTracing= 1;
    +                *traceLevel= 5;
    +            }else{
    +                qh_fprintf(stderr, 6242, "Optional third argument must be 'T5'.  Got '%s'\n", argv[3]);
    +                exit(1);
    +            }
    +        }
    +        *checkEvery= (int)checkEveryArg;
    +    }
    +}/*readOptions*/
    +
    +void setupMemory(int tracelevel, int numInts, int **intarray)
    +{
    +    int i;
    +    if(numInts<0 || numInts*(int)sizeof(int)<0){
    +        qh_fprintf(stderr, 6303, "qset does not currently support 64-bit ints.  Integer overflow\n");
    +        exit(1);
    +    }
    +    *intarray= qh_malloc(numInts * sizeof(int));
    +    if(!*intarray){
    +        qh_fprintf(stderr, 6304, "Failed to allocate %d bytes of memory\n", numInts * sizeof(int));
    +        exit(1);
    +    }
    +    for(i= 0; i=2){
    +        isCheck= log_i(ints, "n", numInts/2, numInts, checkEvery);
    +        qh_settruncate(ints, numInts/2);
    +        checkSetContents("qh_settruncate by half", ints, numInts/2, 0, -1, -1);
    +    }
    +    isCheck= log_i(ints, "n", 0, numInts, checkEvery);
    +    qh_settruncate(ints, 0);
    +    checkSetContents("qh_settruncate", ints, 0, -1, -1, -1);
    +
    +    qh_fprintf(stderr, 8003, "\n\nTesting qh_setappend2ndlast 0,0..%d.  Test 0", numInts-1);
    +    qh_setfree(&ints);
    +    ints= qh_setnew(4);
    +    qh_setappend(&ints, intarray+0);
    +    for(i= 0; i=2){
    +        isCheck= log_i(ints, "n", numInts/2, numInts, checkEvery);
    +        SETtruncate_(ints, numInts/2);
    +        checkSetContents("SETtruncate_ by half", ints, numInts/2, 0, -1, -1);
    +    }
    +    isCheck= log_i(ints, "n", 0, numInts, checkEvery);
    +    SETtruncate_(ints, 0);
    +    checkSetContents("SETtruncate_", ints, 0, -1, -1, -1);
    +
    +    qh_setfree(&ints);
    +}/*testSetappendSettruncate*/
    +
    +void testSetdelSetadd(int numInts, int *intarray, int checkEvery)
    +{
    +    setT *ints=qh_setnew(1);
    +    int i,j,isCheck;
    +
    +    qh_fprintf(stderr, 8003, "\n\nTesting qh_setdelnthsorted and qh_setaddnth 1..%d. Test", numInts-1);
    +    for(j=1; j3){
    +                qh_setdelsorted(ints, intarray+i/2);
    +                checkSetContents("qh_setdelsorted", ints, j-1, 0, i/2+1, -1);
    +                qh_setaddsorted(&ints, intarray+i/2);
    +                checkSetContents("qh_setaddsorted i/2", ints, j, 0, 0, -1);
    +            }
    +            qh_setdellast(ints);
    +            checkSetContents("qh_setdellast", ints, (j ? j-1 : 0), 0, -1, -1);
    +            if(j>0){
    +                qh_setaddsorted(&ints, intarray+j-1);
    +                checkSetContents("qh_setaddsorted j-1", ints, j, 0, -1, -1);
    +            }
    +            if(j>4){
    +                qh_setdelnthsorted(ints, i/2);
    +                if (checkEvery==1)
    +                  checkSetContents("qh_setdelnthsorted", ints, j-1, 0, i/2+1, -1);
    +                /* test qh_setdelnth and move-to-front */
    +                qh_setdelsorted(ints, intarray+i/2+1);
    +                checkSetContents("qh_setdelsorted 2", ints, j-2, 0, i/2+2, -1);
    +                qh_setaddsorted(&ints, intarray+i/2+1);
    +                if (checkEvery==1)
    +                  checkSetContents("qh_setaddsorted i/2+1", ints, j-1, 0, i/2+1, -1);
    +                qh_setaddsorted(&ints, intarray+i/2);
    +                checkSetContents("qh_setaddsorted i/2 again", ints, j, 0, -1, -1);
    +            }
    +            qh_setfree(&ints2);
    +            ints2= qh_setcopy(ints, 0);
    +            qh_setcompact(ints);
    +            qh_setcompact(ints2);
    +            checkSetContents("qh_setcompact", ints, j, 0, 0, -1);
    +            checkSetContents("qh_setcompact 2", ints2, j, 0, 0, -1);
    +            qh_setcompact(ints);
    +            checkSetContents("qh_setcompact 3", ints, j, 0, 0, -1);
    +            qh_setfree(&ints2);
    +        }
    +    }
    +    qh_setfreelong(&ints);
    +    if(ints){
    +        qh_setfree(&ints); /* Was quick memory */
    +    }
    +}/*testSetdelsortedEtc*/
    +
    +void testSetequalInEtc(int numInts, int *intarray, int checkEvery)
    +{
    +    setT *ints= NULL;
    +    setT *ints2= NULL;
    +    setT *ints3= NULL;
    +    int i,j,n;
    +
    +    qh_fprintf(stderr, 8019, "\n\nTesting qh_setequal*, qh_setin*, qh_setdel, qh_setdelnth, and qh_setlarger 0..%d. Test", numInts-1);
    +    for(j=0; j0){
    +                if(qh_setequal(ints, ints2)){
    +                    qh_fprintf(stderr, 6324, "testSetequalInEtc: non-empty set equal to empty set\n", j);
    +                    error_count++;
    +                }
    +                qh_setfree(&ints3);
    +                ints3= qh_setcopy(ints, 0);
    +                checkSetContents("qh_setreplace", ints3, j, 0, -1, -1);
    +                qh_setreplace(ints3, intarray+j/2, intarray+j/2+1);
    +                if(j==1){
    +                    checkSetContents("qh_setreplace 2", ints3, j, j/2+1, -1, -1);
    +                }else if(j==2){
    +                    checkSetContents("qh_setreplace 3", ints3, j, 0, j/2+1, -1);
    +                }else{
    +                    checkSetContents("qh_setreplace 3", ints3, j, 0, j/2+1, j/2+1);
    +                }
    +                if(qh_setequal(ints, ints3)){
    +                    qh_fprintf(stderr, 6325, "testSetequalInEtc: modified set equal to original set at %d/2\n", j);
    +                    error_count++;
    +                }
    +                if(!qh_setequal_except(ints, intarray+j/2, ints3, intarray+j/2+1)){
    +                    qh_fprintf(stderr, 6326, "qh_setequal_except: modified set not equal to original set except modified\n", j);
    +                    error_count++;
    +                }
    +                if(qh_setequal_except(ints, intarray+j/2, ints3, intarray)){
    +                    qh_fprintf(stderr, 6327, "qh_setequal_except: modified set equal to original set with wrong excepts\n", j);
    +                    error_count++;
    +                }
    +                if(!qh_setequal_skip(ints, j/2, ints3, j/2)){
    +                    qh_fprintf(stderr, 6328, "qh_setequal_skip: modified set not equal to original set except modified\n", j);
    +                    error_count++;
    +                }
    +                if(j>2 && qh_setequal_skip(ints, j/2, ints3, 0)){
    +                    qh_fprintf(stderr, 6329, "qh_setequal_skip: modified set equal to original set with wrong excepts\n", j);
    +                    error_count++;
    +                }
    +                if(intarray+j/2+1!=qh_setdel(ints3, intarray+j/2+1)){
    +                    qh_fprintf(stderr, 6330, "qh_setdel: failed to find added element\n", j);
    +                    error_count++;
    +                }
    +                checkSetContents("qh_setdel", ints3, j-1, 0, j-1, (j==1 ? -1 : j/2+1));  /* swaps last element with deleted element */
    +                if(j>3){
    +                    qh_setdelnth(ints3, j/2); /* Delete at the same location as the original replace, for only one out-of-order element */
    +                    checkSetContents("qh_setdelnth", ints3, j-2, 0, j-2, (j==2 ? -1 : j/2+1));
    +                }
    +                if(qh_setin(ints3, intarray+j/2)){
    +                    qh_fprintf(stderr, 6331, "qh_setin: found deleted element\n");
    +                    error_count++;
    +                }
    +                if(j>4 && !qh_setin(ints3, intarray+1)){
    +                    qh_fprintf(stderr, 6332, "qh_setin: did not find second element\n");
    +                    error_count++;
    +                }
    +                if(j>4 && !qh_setin(ints3, intarray+j-2)){
    +                    qh_fprintf(stderr, 6333, "qh_setin: did not find last element\n");
    +                    error_count++;
    +                }
    +                if(-1!=qh_setindex(ints2, intarray)){
    +                    qh_fprintf(stderr, 6334, "qh_setindex: found element in empty set\n");
    +                    error_count++;
    +                }
    +                if(-1!=qh_setindex(ints3, intarray+j/2)){
    +                    qh_fprintf(stderr, 6335, "qh_setindex: found deleted element in set\n");
    +                    error_count++;
    +                }
    +                if(0!=qh_setindex(ints, intarray)){
    +                    qh_fprintf(stderr, 6336, "qh_setindex: did not find first in set\n");
    +                    error_count++;
    +                }
    +                if(j-1!=qh_setindex(ints, intarray+j-1)){
    +                    qh_fprintf(stderr, 6337, "qh_setindex: did not find last in set\n");
    +                    error_count++;
    +                }
    +            }
    +            qh_setfree(&ints2);
    +        }
    +    }
    +    qh_setfree(&ints3);
    +    qh_setfreelong(&ints);
    +    if(ints){
    +        qh_setfree(&ints); /* Was quick memory */
    +    }
    +}/*testSetequalInEtc*/
    +
    +
    +void testSetlastEtc(int numInts, int *intarray, int checkEvery)
    +{
    +    setT *ints= NULL;
    +    setT *ints2= NULL;
    +    int i,j,prepend;
    +
    +    qh_fprintf(stderr, 8020, "\n\nTesting qh_setlast, qh_setnew_delnthsorted, qh_setunique, and qh_setzero 0..%d. Test", numInts-1);
    +    for(j=0; j0){
    +                if(intarray+j-1!=qh_setlast(ints)){
    +                    qh_fprintf(stderr, 6338, "qh_setlast: wrong last element\n");
    +                    error_count++;
    +                }
    +                prepend= (j<100 ? j/4 : 0);
    +                ints2= qh_setnew_delnthsorted(ints, qh_setsize(ints), j/2, prepend);
    +                if(qh_setsize(ints2)!=j+prepend-1){
    +                    qh_fprintf(stderr, 6345, "qh_setnew_delnthsorted: Expecting %d elements, got %d\n", j+prepend-1, qh_setsize(ints2));
    +                    error_count++;
    +                }
    +                /* Define prepended elements.  Otherwise qh_setdelnthsorted may fail */
    +                for(i= 0; i2){
    +                    qh_setzero(ints2, j/2, j-1);  /* max size may be j-1 */
    +                    if(qh_setsize(ints2)!=j-1){
    +                        qh_fprintf(stderr, 6342, "qh_setzero: Expecting %d elements, got %d\n", j, qh_setsize(ints2));
    +                        error_count++;
    +                    }
    +                    qh_setcompact(ints2);
    +                    checkSetContents("qh_setzero", ints2, j/2, 0, -1, -1);
    +                }
    +            }
    +            qh_setfree(&ints2);
    +        }
    +    }
    +    qh_setfreelong(&ints);
    +    if(ints){
    +        qh_setfree(&ints); /* Was quick memory */
    +    }
    +}/*testSetlastEtc*/
    +
    +void testSettemp(int numInts, int *intarray, int checkEvery)
    +{
    +    setT *ints= NULL;
    +    setT *ints2= NULL;
    +    setT *ints3= NULL;
    +    int i,j;
    +
    +    qh_fprintf(stderr, 8021, "\n\nTesting qh_settemp* 0..%d. Test", numInts-1);
    +    for(j=0; j0){
    +                qh_settemppush(ints);
    +                ints3= qh_settemppop();
    +                if(ints!=ints3){
    +                    qh_fprintf(stderr, 6343, "qh_settemppop: didn't pop the push\n");
    +                    error_count++;
    +                }
    +            }
    +            qh_settempfree(&ints2);
    +        }
    +    }
    +    qh_setfreelong(&ints);
    +    if(ints){
    +        qh_setfree(&ints); /* Was quick memory */
    +    }
    +}/*testSettemp*/
    +
    +/* Check that a set contains count elements
    +   Ranges are consecutive (e.g., 1,2,3,...) starting with first, mid, and last
    +   Use -1 for missing ranges
    +   Returns -1 if should check results
    +*/
    +int log_i(setT *set, const char *s, int i, int numInts, int checkEvery)
    +{
    +    int j= i;
    +    int scale= 1;
    +    int e= 0;
    +    int *i2, **i2p;
    +
    +    if(*s || checkEvery==1){
    +        if(i<10){
    +            qh_fprintf(stderr, 8004, " %s%d", s, i);
    +        }else{
    +            if(i==11 && checkEvery==1){
    +                qh_fprintf(stderr, 8005, "\nResults after 10: ");
    +                FOREACHint_(set){
    +                    qh_fprintf(stderr, 8006, " %d", *i2);
    +                }
    +                qh_fprintf(stderr, 8007, " Continue");
    +            }
    +            while((j= j/10)>=1){
    +                scale *= 10;
    +                e++;
    +            }
    +            if(i==numInts-1){
    +                qh_fprintf(stderr, 8008, " %s%d", s, i);
    +            }else if(i==scale){
    +                if(i<=1000){
    +                    qh_fprintf(stderr, 8010, " %s%d", s, i);
    +                }else{
    +                    qh_fprintf(stderr, 8009, " %s1e%d", s, e);
    +                }
    +            }
    +        }
    +    }
    +    if(i<1000 || i%checkEvery==0 || i== scale || i==numInts-1){
    +        return 1;
    +    }
    +    return 0;
    +}/*log_i*/
    +
    +/* Check that a set contains count elements
    +   Ranges are consecutive (e.g., 1,2,3,...) starting with first, mid, and last
    +   Use -1 for missing ranges
    +*/
    +void checkSetContents(const char *name, setT *set, int count, int rangeA, int rangeB, int rangeC)
    +{
    +
    +    i2T *i2, **i2p;
    +    int i2_i, i2_n;
    +    int prev= -1; /* avoid warning */
    +    int i;
    +    int first= -3;
    +    int second= -3;
    +    int rangeCount=1;
    +    int actualSize= 0;
    +
    +    qh_setcheck(set, name, 0);
    +    if(set){
    +        SETreturnsize_(set, actualSize);  /* normally used only when speed is critical */
    +        if(*qh_setendpointer(set)!=NULL){
    +            qh_fprintf(stderr, 6344, "%s: qh_setendpointer(), 0x%x, is not NULL terminator of set 0x%x", name, qh_setendpointer(set), set);
    +            error_count++;
    +        }
    +    }
    +    if(actualSize!=qh_setsize(set)){
    +        qh_fprintf(stderr, 6305, "%s: SETreturnsize_() returned %d while qh_setsize() returns %d\n", name, actualSize, qh_setsize(set));
    +        error_count++;
    +    }else if(actualSize!=count){
    +        qh_fprintf(stderr, 6306, "%s: Expecting %d elements for set.  Got %d elements\n", name, count, actualSize);
    +        error_count++;
    +    }
    +    if(SETempty_(set)){
    +        if(count!=0){
    +            qh_fprintf(stderr, 6307, "%s: Got empty set instead of count %d, rangeA %d, rangeB %d, rangeC %d\n", name, count, rangeA, rangeB, rangeC);
    +            error_count++;
    +        }
    +    }else{
    +        /* Must be first, otherwise trips msvc 8 */
    +        i2T **p= SETaddr_(set, i2T);
    +        if(*p!=SETfirstt_(set, i2T)){
    +            qh_fprintf(stderr, 6309, "%s: SETaddr_(set, i2t) [%p] is not the same as SETfirst_(set) [%p]\n", name, SETaddr_(set, i2T), SETfirst_(set));
    +            error_count++;
    +        }
    +        first= *(int *)SETfirst_(set);
    +        if(SETfirst_(set)!=SETfirstt_(set, i2T)){
    +            qh_fprintf(stderr, 6308, "%s: SETfirst_(set) [%p] is not the same as SETfirstt_(set, i2T [%p]\n", name, SETfirst_(set), SETfirstt_(set, i2T));
    +            error_count++;
    +        }
    +        if(qh_setsize(set)>1){
    +            second= *(int *)SETsecond_(set);
    +            if(SETsecond_(set)!=SETsecondt_(set, i2T)){
    +                qh_fprintf(stderr, 6310, "%s: SETsecond_(set) [%p] is not the same as SETsecondt_(set, i2T) [%p]\n", name, SETsecond_(set), SETsecondt_(set, i2T));
    +                error_count++;
    +            }
    +        }
    +    }
    +    /* Test first run of ints in set*/
    +    i= 0;
    +    FOREACHint_(set){
    +        if(i2!=SETfirst_(set) && *i2!=prev+1){
    +            break;
    +        }
    +        prev= *i2;
    +        if(SETindex_(set, i2)!=i){
    +            qh_fprintf(stderr, 6311, "%s: Expecting SETIndex_(set, pointer-to-%d) to be %d.  Got %d\n", name, *i2, i, SETindex_(set, i2));
    +            error_count++;;
    +        }
    +        if(i2!=SETref_(i2)){
    +            qh_fprintf(stderr, 6312, "%s: SETref_(i2) [%p] does not point to i2 (the %d'th element)\n", name, SETref_(i2), i);
    +            error_count++;;
    +        }
    +        i++;
    +    }
    +    FOREACHint_i_(set){
    +        /* Must be first conditional, otherwise it trips up msvc 8 */
    +        i2T **p= SETelemaddr_(set, i2_i, i2T);
    +        if(i2!=*p){
    +            qh_fprintf(stderr, 6320, "%s: SETelemaddr_(set, %d, i2T) [%p] does not point to i2\n", name, i2_i, SETelemaddr_(set, i2_i, int));
    +            error_count++;;
    +        }
    +        if(i2_i==0){
    +            if(first!=*i2){
    +                qh_fprintf(stderr, 6314, "%s: First element is %d instead of SETfirst %d\n", name, *i2, first);
    +                error_count++;;
    +            }
    +            if(rangeA!=*i2){
    +                qh_fprintf(stderr, 6315, "%s: starts with %d instead of rangeA %d\n", name, *i2, rangeA);
    +                error_count++;;
    +            }
    +            prev= rangeA;
    +        }else{
    +            if(i2_i==1 && second!=*i2){
    +                qh_fprintf(stderr, 6316, "%s: Second element is %d instead of SETsecond %d\n", name, *i2, second);
    +                error_count++;;
    +            }
    +            if(prev+1==*i2){
    +                prev++;
    +            }else{
    +                if(*i2==rangeB){
    +                    prev= rangeB;
    +                    rangeB= -1;
    +                    rangeCount++;
    +                }else if(rangeB==-1 && *i2==rangeC){
    +                    prev= rangeC;
    +                    rangeC= -1;
    +                    rangeCount++;
    +                }else{
    +                    prev++;
    +                    qh_fprintf(stderr, 6317, "%s: Expecting %d'th element to be %d.  Got %d\n", name, i2_i, prev, *i2);
    +                    error_count++;
    +                }
    +            }
    +        }
    +        if(i2!=SETelem_(set, i2_i)){
    +            qh_fprintf(stderr, 6318, "%s: SETelem_(set, %d) [%p] is not i2 [%p] (the %d'th element)\n", name, i2_i, SETelem_(set, i2_i), i2, i2_i);
    +            error_count++;;
    +        }
    +        if(SETelemt_(set, i2_i, i2T)!=SETelem_(set, i2_i)){   /* Normally SETelemt_ is used for generic sets */
    +            qh_fprintf(stderr, 6319, "%s: SETelemt_(set, %d, i2T) [%p] is not SETelem_(set, %d) [%p] (the %d'th element)\n", name, i2_i, SETelemt_(set, i2_i, int), i2_i, SETelem_(set, i2_i), i2_i);
    +            error_count++;;
    +        }
    +    }
    +    if(error_count>=MAXerrorCount){
    +        qh_fprintf(stderr, 8011, "testqset: Stop testing after %d errors\n", error_count);
    +        exit(1);
    +    }
    +}/*checkSetContents*/
    +
    diff --git a/xs/src/qhull/src/testqset/testqset.pro b/xs/src/qhull/src/testqset/testqset.pro
    new file mode 100644
    index 000000000..3f69048aa
    --- /dev/null
    +++ b/xs/src/qhull/src/testqset/testqset.pro
    @@ -0,0 +1,30 @@
    +# -------------------------------------------------
    +# testqset.pro -- Qt project file for testqset.exe
    +# -------------------------------------------------
    +
    +include(../qhull-warn.pri)
    +
    +TARGET = testqset
    +
    +DESTDIR = ../../bin
    +TEMPLATE = app
    +CONFIG += console warn_on
    +CONFIG -= qt
    +CONFIG += qhull_warn_conversion
    +
    +build_pass:CONFIG(debug, debug|release){
    +   OBJECTS_DIR = Debug
    +}else:build_pass:CONFIG(release, debug|release){
    +   OBJECTS_DIR = Release
    +}
    +
    +INCLUDEPATH += ..
    +
    +SOURCES += testqset.c
    +SOURCES += ../libqhull/qset.c
    +SOURCES += ../libqhull/mem.c
    +SOURCES += ../libqhull/usermem.c
    +
    +HEADERS += ../libqhull/mem.h
    +HEADERS += ../libqhull/qset.h
    +
    diff --git a/xs/src/qhull/src/testqset_r/testqset_r.c b/xs/src/qhull/src/testqset_r/testqset_r.c
    new file mode 100644
    index 000000000..9a6d496e4
    --- /dev/null
    +++ b/xs/src/qhull/src/testqset_r/testqset_r.c
    @@ -0,0 +1,890 @@
    +/*
      ---------------------------------
    +
    +   testset.c -- test qset.c and its use of mem.c
    +
    +   The test sets are pointers to int.  Normally a set is a pointer to a type (e.g., facetT, ridgeT, etc.).
    +   For consistency in notation, an "int" is typedef'd to i2T
    +
    +Functions and macros from qset.h.  Counts occurrences in this test.  Does not correspond to thoroughness.
    +    qh_setaddsorted -- 4 tests
    +    qh_setaddnth -- 1 test
    +    qh_setappend -- 7 tests
    +    qh_setappend_set -- 1 test
    +    qh_setappend2ndlast -- 1 test
    +    qh_setcheck -- lots of tests
    +    qh_setcompact -- 7 tests
    +    qh_setcopy -- 3 tests
    +    qh_setdel -- 1 tests
    +    qh_setdellast -- 1 tests
    +    qh_setdelnth -- 2 tests
    +    qh_setdelnthsorted -- 2 tests
    +    qh_setdelsorted -- 1 test
    +    qh_setduplicate -- not testable here
    +    qh_setequal -- 4 tests
    +    qh_setequal_except -- 2 tests
    +    qh_setequal_skip -- 2 tests
    +    qh_setfree -- 11+ tests
    +    qh_setfree2 -- not testable here
    +    qh_setfreelong -- 2 tests
    +    qh_setin -- 3 tests
    +    qh_setindex -- 4 tests
    +    qh_setlarger -- 1 test
    +    qh_setlast -- 2 tests
    +    qh_setnew -- 6 tests
    +    qh_setnew_delnthsorted
    +    qh_setprint -- tested elsewhere
    +    qh_setreplace -- 1 test
    +    qh_setsize -- 9+ tests
    +    qh_settemp -- 2 tests
    +    qh_settempfree -- 1 test
    +    qh_settempfree_all -- 1 test
    +    qh_settemppop -- 1 test
    +    qh_settemppush -- 1 test
    +    qh_settruncate -- 3 tests
    +    qh_setunique -- 3 tests
    +    qh_setzero -- 1 test
    +    FOREACHint_ -- 2 test
    +    FOREACHint4_
    +    FOREACHint_i_ -- 1 test
    +    FOREACHintreverse_
    +    FOREACHintreverse12_
    +    FOREACHsetelement_ -- 1 test
    +    FOREACHsetelement_i_ -- 1 test
    +    FOREACHsetelementreverse_ -- 1 test
    +    FOREACHsetelementreverse12_ -- 1 test
    +    SETelem_ -- 3 tests
    +    SETelemaddr_ -- 2 tests
    +    SETelemt_ -- not tested (generic)
    +    SETempty_ -- 1 test
    +    SETfirst_ -- 4 tests
    +    SETfirstt_ -- 2 tests
    +    SETindex_ -- 2 tests
    +    SETref_ -- 2 tests
    +    SETreturnsize_ -- 2 tests
    +    SETsecond_ -- 1 test
    +    SETsecondt_ -- 2 tests
    +    SETtruncate_ -- 2 tests
    +
    +    Copyright (c) 2012-2015 C.B. Barber. All rights reserved.
    +    $Id: //main/2015/qhull/src/testqset_r/testqset_r.c#5 $$Change: 2064 $
    +    $DateTime: 2016/01/18 12:36:08 $$Author: bbarber $
    +*/
    +
    +#include "libqhull_r/user_r.h"  /* QHULL_CRTDBG */
    +#include "libqhull_r/qset_r.h"
    +#include "libqhull_r/mem_r.h"
    +#include "libqhull_r/libqhull_r.h"
    +
    +#include 
    +#include 
    +#include 
    +#include 
    +
    +typedef int i2T;
    +#define MAXerrorCount 100 /* quit after n errors */
    +
    +#define FOREACHint_( ints ) FOREACHsetelement_( i2T, ints, i2)
    +#define FOREACHint4_( ints ) FOREACHsetelement_( i2T, ints, i4)
    +#define FOREACHint_i_( qh, ints ) FOREACHsetelement_i_( qh, i2T, ints, i2)
    +#define FOREACHintreverse_( qh, ints ) FOREACHsetelementreverse_( qh, i2T, ints, i2)
    +#define FOREACHintreverse12_( ints ) FOREACHsetelementreverse12_( i2T, ints, i2)
    +
    +enum {
    +    MAXint= 0x7fffffff,
    +};
    +
    +char prompt[]= "testqset_r N [M] [T5] -- Test reentrant qset_r.c and mem_r.c\n\
    +  \n\
    +  If this test fails then reentrant Qhull will not work.\n\
    +  \n\
    +  Test qsets of 0..N integers with a check every M iterations (default ~log10)\n\
    +  Additional checking and logging if M is 1\n\
    +  \n\
    +  T5 turns on memory logging (qset does not log)\n\
    +  \n\
    +  For example:\n\
    +    testqset_r 10000\n\
    +";
    +
    +int error_count= 0;  /* Global error_count.  checkSetContents(qh) keeps its own error count.  It exits on too many errors */
    +
    +/* Macros normally defined in geom.h */
    +#define fmax_( a,b )  ( ( a ) < ( b ) ? ( b ) : ( a ) )
    +
    +/* Macros normally defined in QhullSet.h */
    +
    +/* Functions normally defined in user_r.h for usermem_r.c */
    +
    +void    qh_exit(int exitcode);
    +void    qh_fprintf_stderr(int msgcode, const char *fmt, ... );
    +void    qh_free(void *mem);
    +void   *qh_malloc(size_t size);
    +
    +/* Normally defined in user_r.c */
    +
    +void    qh_errexit(qhT *qh, int exitcode, facetT *f, ridgeT *r)
    +{
    +    (void)f; /* unused */
    +    (void)r; /* unused */
    +    (void)qh; /* unused */
    +    qh_exit(exitcode);
    +}
    +
    +/* Normally defined in userprintf.c */
    +
    +void    qh_fprintf(qhT *qh, FILE *fp, int msgcode, const char *fmt, ... )
    +{
    +    static int needs_cr= 0;  /* True if qh_fprintf needs a CR. testqset_r is not itself reentrant */
    +
    +    size_t fmtlen= strlen(fmt);
    +    va_list args;
    +
    +    if (!fp) {
    +        /* Do not use qh_fprintf_stderr.  This is a standalone program */
    +        if(!qh)
    +            fprintf(stderr, "QH6241 qh_fprintf: fp and qh not defined for '%s'", fmt);
    +        else
    +            fprintf(stderr, "QH6232 qh_fprintf: fp is 0.  Was wrong qh_fprintf called for '%s'", fmt);
    +        qh_errexit(qh, 6232, NULL, NULL);
    +    }
    +    if(fmtlen>0){
    +        if(fmt[fmtlen-1]=='\n'){
    +            if(needs_cr && fmtlen>1){
    +                fprintf(fp, "\n");
    +            }
    +            needs_cr= 0;
    +        }else{
    +            needs_cr= 1;
    +        }
    +    }
    +    if(msgcode>=6000 && msgcode<7000){
    +        fprintf(fp, "Error TQ%d ", msgcode);
    +    }
    +    va_start(args, fmt);
    +    vfprintf(fp, fmt, args);
    +    va_end(args);
    +}
    +
    +/* Defined below in order of use */
    +int main(int argc, char **argv);
    +void readOptions(qhT *qh, int argc, char **argv, const char *promptstr, int *numInts, int *checkEvery, int *traceLevel);
    +void setupMemory(qhT *qh, int tracelevel, int numInts, int **intarray);
    +
    +void testSetappendSettruncate(qhT *qh, int numInts, int *intarray, int checkEvery);
    +void testSetdelSetadd(qhT *qh, int numInts, int *intarray, int checkEvery);
    +void testSetappendSet(qhT *qh, int numInts, int *intarray, int checkEvery);
    +void testSetcompactCopy(qhT *qh, int numInts, int *intarray, int checkEvery);
    +void testSetequalInEtc(qhT *qh, int numInts, int *intarray, int checkEvery);
    +void testSettemp(qhT *qh, int numInts, int *intarray, int checkEvery);
    +void testSetlastEtc(qhT *qh, int numInts, int *intarray, int checkEvery);
    +void testSetdelsortedEtc(qhT *qh, int numInts, int *intarray, int checkEvery);
    +
    +int log_i(qhT *qh, setT *set, const char *s, int i, int numInts, int checkEvery);
    +void checkSetContents(qhT *qh, const char *name, setT *set, int count, int rangeA, int rangeB, int rangeC);
    +
    +int main(int argc, char **argv) {
    +    int *intarray= NULL;
    +    int numInts;
    +    int checkEvery= MAXint;
    +    int curlong, totlong;
    +    int traceLevel= 4; /* 4 normally, no tracing since qset does not log.  Option 'T5' for memory tracing */
    +    qhT qh_qh;
    +    qhT *qh= &qh_qh;
    +
    +#if defined(_MSC_VER) && defined(_DEBUG) && defined(QHULL_CRTDBG)  /* user_r.h */
    +    _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 );
    +#endif
    +
    +    readOptions(qh, argc, argv, prompt, &numInts, &checkEvery, &traceLevel);
    +    setupMemory(qh, traceLevel, numInts, &intarray);
    +
    +    testSetappendSettruncate(qh, numInts, intarray, checkEvery);
    +    testSetdelSetadd(qh, numInts, intarray, checkEvery);
    +    testSetappendSet(qh, numInts, intarray, checkEvery);
    +    testSetcompactCopy(qh, numInts, intarray, checkEvery);
    +    testSetequalInEtc(qh, numInts, intarray, checkEvery);
    +    testSettemp(qh, numInts, intarray, checkEvery);
    +    testSetlastEtc(qh, numInts, intarray, checkEvery);
    +    testSetdelsortedEtc(qh, numInts, intarray, checkEvery);
    +    printf("\n\nNot testing qh_setduplicate and qh_setfree2.\n  These routines use heap-allocated set contents.  See qhull tests.\n");
    +
    +    qh_memstatistics(qh, stdout);
    +    qh_memfreeshort(qh, &curlong, &totlong);
    +    if (curlong || totlong){
    +        qh_fprintf(qh, stderr, 8043, "qh_memfreeshort: did not free %d bytes of long memory(%d pieces)\n", totlong, curlong);
    +        error_count++;
    +    }
    +    if(error_count){
    +        qh_fprintf(qh, stderr, 8012, "testqset: %d errors\n\n", error_count);
    +        exit(1);
    +    }else{
    +        printf("testqset_r: OK\n\n");
    +    }
    +    return 0;
    +}/*main*/
    +
    +void readOptions(qhT *qh, int argc, char **argv, const char *promptstr, int *numInts, int *checkEvery, int *traceLevel)
    +{
    +    long numIntsArg;
    +    long checkEveryArg;
    +    char *endp;
    +    int isTracing= 0;
    +
    +    if (argc < 2 || argc > 4) {
    +        printf("%s", promptstr);
    +        exit(0);
    +    }
    +    numIntsArg= strtol(argv[1], &endp, 10);
    +    if(numIntsArg<1){
    +        qh_fprintf(qh, stderr, 6301, "First argument should be 1 or greater.  Got '%s'\n", argv[1]);
    +        exit(1);
    +    }
    +    if(numIntsArg>MAXint){
    +        qh_fprintf(qh, stderr, 6302, "qset does not currently support 64-bit ints.  Maximum count is %d\n", MAXint);
    +        exit(1);
    +    }
    +    *numInts= (int)numIntsArg;
    +
    +    if(argc==3 && argv[2][0]=='T' && argv[2][1]=='5' ){
    +        isTracing= 1;
    +        *traceLevel= 5;
    +    }
    +    if(argc==4 || (argc==3 && !isTracing)){
    +        checkEveryArg= strtol(argv[2], &endp, 10);
    +        if(checkEveryArg<1){
    +            qh_fprintf(qh, stderr, 6321, "checkEvery argument should be 1 or greater.  Got '%s'\n", argv[2]);
    +            exit(1);
    +        }
    +        if(checkEveryArg>MAXint){
    +            qh_fprintf(qh, stderr, 6322, "qset does not currently support 64-bit ints.  Maximum checkEvery is %d\n", MAXint);
    +            exit(1);
    +        }
    +        if(argc==4){
    +            if(argv[3][0]=='T' && argv[3][1]=='5' ){
    +                isTracing= 1;
    +                *traceLevel= 5;
    +            }else{
    +                qh_fprintf(qh, stderr, 6242, "Optional third argument must be 'T5'.  Got '%s'\n", argv[3]);
    +                exit(1);
    +            }
    +        }
    +        *checkEvery= (int)checkEveryArg;
    +    }
    +}/*readOptions*/
    +
    +void setupMemory(qhT *qh, int tracelevel, int numInts, int **intarray)
    +{
    +    int i;
    +    if(numInts<0 || numInts*(int)sizeof(int)<0){
    +        qh_fprintf(qh, stderr, 6303, "qset does not currently support 64-bit ints.  Integer overflow\n");
    +        exit(1);
    +    }
    +    *intarray= qh_malloc(numInts * sizeof(int));
    +    if(!*intarray){
    +        qh_fprintf(qh, stderr, 6304, "Failed to allocate %d bytes of memory\n", numInts * sizeof(int));
    +        exit(1);
    +    }
    +    for(i= 0; i=2){
    +        isCheck= log_i(qh, ints, "n", numInts/2, numInts, checkEvery);
    +        qh_settruncate(qh, ints, numInts/2);
    +        checkSetContents(qh, "qh_settruncate by half", ints, numInts/2, 0, -1, -1);
    +    }
    +    isCheck= log_i(qh, ints, "n", 0, numInts, checkEvery);
    +    qh_settruncate(qh, ints, 0);
    +    checkSetContents(qh, "qh_settruncate", ints, 0, -1, -1, -1);
    +
    +    qh_fprintf(qh, stderr, 8003, "\n\nTesting qh_setappend2ndlast 0,0..%d.  Test 0", numInts-1);
    +    qh_setfree(qh, &ints);
    +    ints= qh_setnew(qh, 4);
    +    qh_setappend(qh, &ints, intarray+0);
    +    for(i= 0; i=2){
    +        isCheck= log_i(qh, ints, "n", numInts/2, numInts, checkEvery);
    +        SETtruncate_(ints, numInts/2);
    +        checkSetContents(qh, "SETtruncate_ by half", ints, numInts/2, 0, -1, -1);
    +    }
    +    isCheck= log_i(qh, ints, "n", 0, numInts, checkEvery);
    +    SETtruncate_(ints, 0);
    +    checkSetContents(qh, "SETtruncate_", ints, 0, -1, -1, -1);
    +
    +    qh_setfree(qh, &ints);
    +}/*testSetappendSettruncate*/
    +
    +void testSetdelSetadd(qhT *qh, int numInts, int *intarray, int checkEvery)
    +{
    +    setT *ints=qh_setnew(qh, 1);
    +    int i,j,isCheck;
    +
    +    qh_fprintf(qh, stderr, 8003, "\n\nTesting qh_setdelnthsorted and qh_setaddnth 1..%d. Test", numInts-1);
    +    for(j=1; j3){
    +                qh_setdelsorted(ints, intarray+i/2);
    +                checkSetContents(qh, "qh_setdelsorted", ints, j-1, 0, i/2+1, -1);
    +                qh_setaddsorted(qh, &ints, intarray+i/2);
    +                checkSetContents(qh, "qh_setaddsorted i/2", ints, j, 0, 0, -1);
    +            }
    +            qh_setdellast(ints);
    +            checkSetContents(qh, "qh_setdellast", ints, (j ? j-1 : 0), 0, -1, -1);
    +            if(j>0){
    +                qh_setaddsorted(qh, &ints, intarray+j-1);
    +                checkSetContents(qh, "qh_setaddsorted j-1", ints, j, 0, -1, -1);
    +            }
    +            if(j>4){
    +                qh_setdelnthsorted(qh, ints, i/2);
    +                if (checkEvery==1)
    +                  checkSetContents(qh, "qh_setdelnthsorted", ints, j-1, 0, i/2+1, -1);
    +                /* test qh_setdelnth and move-to-front */
    +                qh_setdelsorted(ints, intarray+i/2+1);
    +                checkSetContents(qh, "qh_setdelsorted 2", ints, j-2, 0, i/2+2, -1);
    +                qh_setaddsorted(qh, &ints, intarray+i/2+1);
    +                if (checkEvery==1)
    +                  checkSetContents(qh, "qh_setaddsorted i/2+1", ints, j-1, 0, i/2+1, -1);
    +                qh_setaddsorted(qh, &ints, intarray+i/2);
    +                checkSetContents(qh, "qh_setaddsorted i/2 again", ints, j, 0, -1, -1);
    +            }
    +            qh_setfree(qh, &ints2);
    +            ints2= qh_setcopy(qh, ints, 0);
    +            qh_setcompact(qh, ints);
    +            qh_setcompact(qh, ints2);
    +            checkSetContents(qh, "qh_setcompact", ints, j, 0, 0, -1);
    +            checkSetContents(qh, "qh_setcompact 2", ints2, j, 0, 0, -1);
    +            qh_setcompact(qh, ints);
    +            checkSetContents(qh, "qh_setcompact 3", ints, j, 0, 0, -1);
    +            qh_setfree(qh, &ints2);
    +        }
    +    }
    +    qh_setfreelong(qh, &ints);
    +    if(ints){
    +        qh_setfree(qh, &ints); /* Was quick memory */
    +    }
    +}/*testSetdelsortedEtc*/
    +
    +void testSetequalInEtc(qhT *qh, int numInts, int *intarray, int checkEvery)
    +{
    +    setT *ints= NULL;
    +    setT *ints2= NULL;
    +    setT *ints3= NULL;
    +    int i,j,n;
    +
    +    qh_fprintf(qh, stderr, 8019, "\n\nTesting qh_setequal*, qh_setin*, qh_setdel, qh_setdelnth, and qh_setlarger 0..%d. Test", numInts-1);
    +    for(j=0; j0){
    +                if(qh_setequal(ints, ints2)){
    +                    qh_fprintf(qh, stderr, 6324, "testSetequalInEtc: non-empty set equal to empty set\n", j);
    +                    error_count++;
    +                }
    +                qh_setfree(qh, &ints3);
    +                ints3= qh_setcopy(qh, ints, 0);
    +                checkSetContents(qh, "qh_setreplace", ints3, j, 0, -1, -1);
    +                qh_setreplace(qh, ints3, intarray+j/2, intarray+j/2+1);
    +                if(j==1){
    +                    checkSetContents(qh, "qh_setreplace 2", ints3, j, j/2+1, -1, -1);
    +                }else if(j==2){
    +                    checkSetContents(qh, "qh_setreplace 3", ints3, j, 0, j/2+1, -1);
    +                }else{
    +                    checkSetContents(qh, "qh_setreplace 3", ints3, j, 0, j/2+1, j/2+1);
    +                }
    +                if(qh_setequal(ints, ints3)){
    +                    qh_fprintf(qh, stderr, 6325, "testSetequalInEtc: modified set equal to original set at %d/2\n", j);
    +                    error_count++;
    +                }
    +                if(!qh_setequal_except(ints, intarray+j/2, ints3, intarray+j/2+1)){
    +                    qh_fprintf(qh, stderr, 6326, "qh_setequal_except: modified set not equal to original set except modified\n", j);
    +                    error_count++;
    +                }
    +                if(qh_setequal_except(ints, intarray+j/2, ints3, intarray)){
    +                    qh_fprintf(qh, stderr, 6327, "qh_setequal_except: modified set equal to original set with wrong excepts\n", j);
    +                    error_count++;
    +                }
    +                if(!qh_setequal_skip(ints, j/2, ints3, j/2)){
    +                    qh_fprintf(qh, stderr, 6328, "qh_setequal_skip: modified set not equal to original set except modified\n", j);
    +                    error_count++;
    +                }
    +                if(j>2 && qh_setequal_skip(ints, j/2, ints3, 0)){
    +                    qh_fprintf(qh, stderr, 6329, "qh_setequal_skip: modified set equal to original set with wrong excepts\n", j);
    +                    error_count++;
    +                }
    +                if(intarray+j/2+1!=qh_setdel(ints3, intarray+j/2+1)){
    +                    qh_fprintf(qh, stderr, 6330, "qh_setdel: failed to find added element\n", j);
    +                    error_count++;
    +                }
    +                checkSetContents(qh, "qh_setdel", ints3, j-1, 0, j-1, (j==1 ? -1 : j/2+1));  /* swaps last element with deleted element */
    +                if(j>3){
    +                    qh_setdelnth(qh, ints3, j/2); /* Delete at the same location as the original replace, for only one out-of-order element */
    +                    checkSetContents(qh, "qh_setdelnth", ints3, j-2, 0, j-2, (j==2 ? -1 : j/2+1));
    +                }
    +                if(qh_setin(ints3, intarray+j/2)){
    +                    qh_fprintf(qh, stderr, 6331, "qh_setin: found deleted element\n");
    +                    error_count++;
    +                }
    +                if(j>4 && !qh_setin(ints3, intarray+1)){
    +                    qh_fprintf(qh, stderr, 6332, "qh_setin: did not find second element\n");
    +                    error_count++;
    +                }
    +                if(j>4 && !qh_setin(ints3, intarray+j-2)){
    +                    qh_fprintf(qh, stderr, 6333, "qh_setin: did not find last element\n");
    +                    error_count++;
    +                }
    +                if(-1!=qh_setindex(ints2, intarray)){
    +                    qh_fprintf(qh, stderr, 6334, "qh_setindex: found element in empty set\n");
    +                    error_count++;
    +                }
    +                if(-1!=qh_setindex(ints3, intarray+j/2)){
    +                    qh_fprintf(qh, stderr, 6335, "qh_setindex: found deleted element in set\n");
    +                    error_count++;
    +                }
    +                if(0!=qh_setindex(ints, intarray)){
    +                    qh_fprintf(qh, stderr, 6336, "qh_setindex: did not find first in set\n");
    +                    error_count++;
    +                }
    +                if(j-1!=qh_setindex(ints, intarray+j-1)){
    +                    qh_fprintf(qh, stderr, 6337, "qh_setindex: did not find last in set\n");
    +                    error_count++;
    +                }
    +            }
    +            qh_setfree(qh, &ints2);
    +        }
    +    }
    +    qh_setfree(qh, &ints3);
    +    qh_setfreelong(qh, &ints);
    +    if(ints){
    +        qh_setfree(qh, &ints); /* Was quick memory */
    +    }
    +}/*testSetequalInEtc*/
    +
    +
    +void testSetlastEtc(qhT *qh, int numInts, int *intarray, int checkEvery)
    +{
    +    setT *ints= NULL;
    +    setT *ints2= NULL;
    +    int i,j,prepend;
    +
    +    qh_fprintf(qh, stderr, 8020, "\n\nTesting qh_setlast, qh_setnew_delnthsorted, qh_setunique, and qh_setzero 0..%d. Test", numInts-1);
    +    for(j=0; j0){
    +                if(intarray+j-1!=qh_setlast(ints)){
    +                    qh_fprintf(qh, stderr, 6338, "qh_setlast: wrong last element\n");
    +                    error_count++;
    +                }
    +                prepend= (j<100 ? j/4 : 0);
    +                ints2= qh_setnew_delnthsorted(qh, ints, qh_setsize(qh, ints), j/2, prepend);
    +                if(qh_setsize(qh, ints2)!=j+prepend-1){
    +                    qh_fprintf(qh, stderr, 6345, "qh_setnew_delnthsorted: Expecting %d elements, got %d\n", j+prepend-1, qh_setsize(qh, ints2));
    +                    error_count++;
    +                }
    +                /* Define prepended elements.  Otherwise qh_setdelnthsorted may fail */
    +                for(i= 0; i2){
    +                    qh_setzero(qh, ints2, j/2, j-1);  /* max size may be j-1 */
    +                    if(qh_setsize(qh, ints2)!=j-1){
    +                        qh_fprintf(qh, stderr, 6342, "qh_setzero: Expecting %d elements, got %d\n", j, qh_setsize(qh, ints2));
    +                        error_count++;
    +                    }
    +                    qh_setcompact(qh, ints2);
    +                    checkSetContents(qh, "qh_setzero", ints2, j/2, 0, -1, -1);
    +                }
    +            }
    +            qh_setfree(qh, &ints2);
    +        }
    +    }
    +    qh_setfreelong(qh, &ints);
    +    if(ints){
    +        qh_setfree(qh, &ints); /* Was quick memory */
    +    }
    +}/*testSetlastEtc*/
    +
    +void testSettemp(qhT *qh, int numInts, int *intarray, int checkEvery)
    +{
    +    setT *ints= NULL;
    +    setT *ints2= NULL;
    +    setT *ints3= NULL;
    +    int i,j;
    +
    +    qh_fprintf(qh, stderr, 8021, "\n\nTesting qh_settemp* 0..%d. Test", numInts-1);
    +    for(j=0; j0){
    +                qh_settemppush(qh, ints);
    +                ints3= qh_settemppop(qh);
    +                if(ints!=ints3){
    +                    qh_fprintf(qh, stderr, 6343, "qh_settemppop: didn't pop the push\n");
    +                    error_count++;
    +                }
    +            }
    +            qh_settempfree(qh, &ints2);
    +        }
    +    }
    +    qh_setfreelong(qh, &ints);
    +    if(ints){
    +        qh_setfree(qh, &ints); /* Was quick memory */
    +    }
    +}/*testSettemp*/
    +
    +/* Check that a set contains count elements
    +   Ranges are consecutive (e.g., 1,2,3,...) starting with first, mid, and last
    +   Use -1 for missing ranges
    +   Returns -1 if should check results
    +*/
    +int log_i(qhT *qh, setT *set, const char *s, int i, int numInts, int checkEvery)
    +{
    +    int j= i;
    +    int scale= 1;
    +    int e= 0;
    +    int *i2, **i2p;
    +
    +    if(*s || checkEvery==1){
    +        if(i<10){
    +            qh_fprintf(qh, stderr, 8004, " %s%d", s, i);
    +        }else{
    +            if(i==11 && checkEvery==1){
    +                qh_fprintf(qh, stderr, 8005, "\nResults after 10: ");
    +                FOREACHint_(set){
    +                    qh_fprintf(qh, stderr, 8006, " %d", *i2);
    +                }
    +                qh_fprintf(qh, stderr, 8007, " Continue");
    +            }
    +            while((j= j/10)>=1){
    +                scale *= 10;
    +                e++;
    +            }
    +            if(i==numInts-1){
    +                qh_fprintf(qh, stderr, 8008, " %s%d", s, i);
    +            }else if(i==scale){
    +                if(i<=1000){
    +                    qh_fprintf(qh, stderr, 8010, " %s%d", s, i);
    +                }else{
    +                    qh_fprintf(qh, stderr, 8009, " %s1e%d", s, e);
    +                }
    +            }
    +        }
    +    }
    +    if(i<1000 || i%checkEvery==0 || i== scale || i==numInts-1){
    +        return 1;
    +    }
    +    return 0;
    +}/*log_i*/
    +
    +/* Check that a set contains count elements
    +   Ranges are consecutive (e.g., 1,2,3,...) starting with first, mid, and last
    +   Use -1 for missing ranges
    +*/
    +void checkSetContents(qhT *qh, const char *name, setT *set, int count, int rangeA, int rangeB, int rangeC)
    +{
    +
    +    i2T *i2, **i2p;
    +    int i2_i, i2_n;
    +    int prev= -1; /* avoid warning */
    +    int i;
    +    int first= -3;
    +    int second= -3;
    +    int rangeCount=1;
    +    int actualSize= 0;
    +
    +    qh_setcheck(qh, set, name, 0);
    +    if(set){
    +        SETreturnsize_(set, actualSize);  /* normally used only when speed is critical */
    +        if(*qh_setendpointer(set)!=NULL){
    +            qh_fprintf(qh, stderr, 6344, "%s: qh_setendpointer(set), 0x%x, is not NULL terminator of set 0x%x", name, qh_setendpointer(set), set);
    +            error_count++;
    +        }
    +    }
    +    if(actualSize!=qh_setsize(qh, set)){
    +        qh_fprintf(qh, stderr, 6305, "%s: SETreturnsize_(qh) returned %d while qh_setsize(qh) returns %d\n", name, actualSize, qh_setsize(qh, set));
    +        error_count++;
    +    }else if(actualSize!=count){
    +        qh_fprintf(qh, stderr, 6306, "%s: Expecting %d elements for set.  Got %d elements\n", name, count, actualSize);
    +        error_count++;
    +    }
    +    if(SETempty_(set)){
    +        if(count!=0){
    +            qh_fprintf(qh, stderr, 6307, "%s: Got empty set instead of count %d, rangeA %d, rangeB %d, rangeC %d\n", name, count, rangeA, rangeB, rangeC);
    +            error_count++;
    +        }
    +    }else{
    +        /* Must be first, otherwise trips msvc 8 */
    +        i2T **p= SETaddr_(set, i2T);
    +        if(*p!=SETfirstt_(set, i2T)){
    +            qh_fprintf(qh, stderr, 6309, "%s: SETaddr_(set, i2t) [%p] is not the same as SETfirst_(set) [%p]\n", name, SETaddr_(set, i2T), SETfirst_(set));
    +            error_count++;
    +        }
    +        first= *(int *)SETfirst_(set);
    +        if(SETfirst_(set)!=SETfirstt_(set, i2T)){
    +            qh_fprintf(qh, stderr, 6308, "%s: SETfirst_(set) [%p] is not the same as SETfirstt_(set, i2T [%p]\n", name, SETfirst_(set), SETfirstt_(set, i2T));
    +            error_count++;
    +        }
    +        if(qh_setsize(qh, set)>1){
    +            second= *(int *)SETsecond_(set);
    +            if(SETsecond_(set)!=SETsecondt_(set, i2T)){
    +                qh_fprintf(qh, stderr, 6310, "%s: SETsecond_(set) [%p] is not the same as SETsecondt_(set, i2T) [%p]\n", name, SETsecond_(set), SETsecondt_(set, i2T));
    +                error_count++;
    +            }
    +        }
    +    }
    +    /* Test first run of ints in set*/
    +    i= 0;
    +    FOREACHint_(set){
    +        if(i2!=SETfirst_(set) && *i2!=prev+1){
    +            break;
    +        }
    +        prev= *i2;
    +        if(SETindex_(set, i2)!=i){
    +            qh_fprintf(qh, stderr, 6311, "%s: Expecting SETindex_(set, pointer-to-%d) to be %d.  Got %d\n", name, *i2, i, SETindex_(set, i2));
    +            error_count++;;
    +        }
    +        if(i2!=SETref_(i2)){
    +            qh_fprintf(qh, stderr, 6312, "%s: SETref_(i2) [%p] does not point to i2 (the %d'th element)\n", name, SETref_(i2), i);
    +            error_count++;;
    +        }
    +        i++;
    +    }
    +    FOREACHint_i_(qh, set){
    +        /* Must be first conditional, otherwise it trips up msvc 8 */
    +        i2T **p= SETelemaddr_(set, i2_i, i2T);
    +        if(i2!=*p){
    +            qh_fprintf(qh, stderr, 6320, "%s: SETelemaddr_(set, %d, i2T) [%p] does not point to i2\n", name, i2_i, SETelemaddr_(set, i2_i, int));
    +            error_count++;;
    +        }
    +        if(i2_i==0){
    +            if(first!=*i2){
    +                qh_fprintf(qh, stderr, 6314, "%s: First element is %d instead of SETfirst %d\n", name, *i2, first);
    +                error_count++;;
    +            }
    +            if(rangeA!=*i2){
    +                qh_fprintf(qh, stderr, 6315, "%s: starts with %d instead of rangeA %d\n", name, *i2, rangeA);
    +                error_count++;;
    +            }
    +            prev= rangeA;
    +        }else{
    +            if(i2_i==1 && second!=*i2){
    +                qh_fprintf(qh, stderr, 6316, "%s: Second element is %d instead of SETsecond %d\n", name, *i2, second);
    +                error_count++;;
    +            }
    +            if(prev+1==*i2){
    +                prev++;
    +            }else{
    +                if(*i2==rangeB){
    +                    prev= rangeB;
    +                    rangeB= -1;
    +                    rangeCount++;
    +                }else if(rangeB==-1 && *i2==rangeC){
    +                    prev= rangeC;
    +                    rangeC= -1;
    +                    rangeCount++;
    +                }else{
    +                    prev++;
    +                    qh_fprintf(qh, stderr, 6317, "%s: Expecting %d'th element to be %d.  Got %d\n", name, i2_i, prev, *i2);
    +                    error_count++;
    +                }
    +            }
    +        }
    +        if(i2!=SETelem_(set, i2_i)){
    +            qh_fprintf(qh, stderr, 6318, "%s: SETelem_(set, %d) [%p] is not i2 [%p] (the %d'th element)\n", name, i2_i, SETelem_(set, i2_i), i2, i2_i);
    +            error_count++;;
    +        }
    +        if(SETelemt_(set, i2_i, i2T)!=SETelem_(set, i2_i)){   /* Normally SETelemt_ is used for generic sets */
    +            qh_fprintf(qh, stderr, 6319, "%s: SETelemt_(set, %d, i2T) [%p] is not SETelem_(set, %d) [%p] (the %d'th element)\n", name, i2_i, SETelemt_(set, i2_i, int), i2_i, SETelem_(set, i2_i), i2_i);
    +            error_count++;;
    +        }
    +    }
    +    if(error_count>=MAXerrorCount){
    +        qh_fprintf(qh, stderr, 8011, "testqset: Stop testing after %d errors\n", error_count);
    +        exit(1);
    +    }
    +}/*checkSetContents*/
    +
    diff --git a/xs/src/qhull/src/testqset_r/testqset_r.pro b/xs/src/qhull/src/testqset_r/testqset_r.pro
    new file mode 100644
    index 000000000..951e0624e
    --- /dev/null
    +++ b/xs/src/qhull/src/testqset_r/testqset_r.pro
    @@ -0,0 +1,30 @@
    +# -------------------------------------------------
    +# testqset_r.pro -- Qt project file for testqset_r.exe
    +# -------------------------------------------------
    +
    +include(../qhull-warn.pri)
    +
    +TARGET = testqset_r
    +
    +DESTDIR = ../../bin
    +TEMPLATE = app
    +CONFIG += console warn_on
    +CONFIG -= qt
    +CONFIG += qhull_warn_conversion
    +
    +build_pass:CONFIG(debug, debug|release){
    +   OBJECTS_DIR = Debug
    +}else:build_pass:CONFIG(release, debug|release){
    +   OBJECTS_DIR = Release
    +}
    +
    +INCLUDEPATH += ..
    +
    +SOURCES += testqset_r.c
    +SOURCES += ../libqhull_r/qset_r.c
    +SOURCES += ../libqhull_r/mem_r.c
    +SOURCES += ../libqhull_r/usermem_r.c
    +
    +HEADERS += ../libqhull_r/mem_r.h
    +HEADERS += ../libqhull_r/qset_r.h
    +
    diff --git a/xs/src/qhull/src/user_eg/user_eg.c b/xs/src/qhull/src/user_eg/user_eg.c
    new file mode 100644
    index 000000000..9c5fee51b
    --- /dev/null
    +++ b/xs/src/qhull/src/user_eg/user_eg.c
    @@ -0,0 +1,330 @@
    +/*
      ---------------------------------
    +
    +  user_eg.c
    +  sample code for calling qhull() from an application
    +
    +  call with:
    +
    +     user_eg "cube/diamond options" "delaunay options" "halfspace options"
    +
    +  for example:
    +
    +     user_eg                             # return summaries
    +
    +     user_eg "n" "o" "Fp"                # return normals, OFF, points
    +
    +     user_eg "n Qt" "o" "Fp"             # triangulated cube
    +
    +     user_eg "QR0 p" "QR0 v p" "QR0 Fp"  # rotate input and return points
    +                                         # 'v' returns Voronoi
    +                                         # transform is rotated for halfspaces
    +
    +   main() makes three runs of qhull.
    +
    +     1) compute the convex hull of a cube
    +
    +     2a) compute the Delaunay triangulation of random points
    +
    +     2b) find the Delaunay triangle closest to a point.
    +
    +     3) compute the halfspace intersection of a diamond
    +
    + notes:
    +
    +   For another example, see main() in unix.c and user_eg2.c.
    +   These examples, call qh_qhull() directly.  They allow
    +   tighter control on the code loaded with Qhull.
    +
    +   For a C++ example, see user_eg3/user_eg3_r.cpp
    +
    +   Summaries are sent to stderr if other output formats are used
    +
    +   compiled by 'make bin/user_eg'
    +
    +   see libqhull.h for data structures, macros, and user-callable functions.
    +*/
    +
    +#define qh_QHimport
    +#include "libqhull/qhull_a.h"
    +
    +/*-------------------------------------------------
    +-internal function prototypes
    +*/
    +void print_summary(void);
    +void makecube(coordT *points, int numpoints, int dim);
    +void makeDelaunay(coordT *points, int numpoints, int dim, int seed);
    +void findDelaunay(int dim);
    +void makehalf(coordT *points, int numpoints, int dim);
    +
    +/*-------------------------------------------------
    +-print_summary()
    +*/
    +void print_summary(void) {
    +  facetT *facet;
    +  int k;
    +
    +  printf("\n%d vertices and %d facets with normals:\n",
    +                 qh num_vertices, qh num_facets);
    +  FORALLfacets {
    +    for (k=0; k < qh hull_dim; k++)
    +      printf("%6.2g ", facet->normal[k]);
    +    printf("\n");
    +  }
    +}
    +
    +/*--------------------------------------------------
    +-makecube- set points to vertices of cube
    +  points is numpoints X dim
    +*/
    +void makecube(coordT *points, int numpoints, int dim) {
    +  int j,k;
    +  coordT *point;
    +
    +  for (j=0; jlocate a facet with qh_findbestfacet()
    +*/
    +void findDelaunay(int dim) {
    +  int k;
    +  coordT point[ 100];
    +  boolT isoutside;
    +  realT bestdist;
    +  facetT *facet;
    +  vertexT *vertex, **vertexp;
    +
    +  for (k= 0; k < dim; k++)
    +    point[k]= 0.5;
    +  qh_setdelaunay(dim+1, 1, point);
    +  facet= qh_findbestfacet(point, qh_ALL, &bestdist, &isoutside);
    +  if (facet->tricoplanar) {
    +    fprintf(stderr, "findDelaunay: not implemented for triangulated, non-simplicial Delaunay regions (tricoplanar facet, f%d).\n",
    +       facet->id);
    +    qh_errexit(qh_ERRqhull, facet, NULL);
    +  }
    +  FOREACHvertex_(facet->vertices) {
    +    for (k=0; k < dim; k++)
    +      printf("%5.2f ", vertex->point[k]);
    +    printf("\n");
    +  }
    +} /*.findDelaunay.*/
    +
    +/*--------------------------------------------------
    +-makehalf- set points to halfspaces for a (dim)-dimensional diamond
    +  points is numpoints X dim+1
    +
    +  each halfspace consists of dim coefficients followed by an offset
    +*/
    +void makehalf(coordT *points, int numpoints, int dim) {
    +  int j,k;
    +  coordT *point;
    +
    +  for (j=0; j&1'\n\n");
    +
    +#if qh_QHpointer  /* see user.h */
    +  if (qh_qh){
    +      printf("QH6233: Qhull link error.  The global variable qh_qh was not initialized\n\
    +to NULL by global.c.  Please compile user_eg.c with -Dqh_QHpointer_dllimport\n\
    +as well as -Dqh_QHpointer, or use libqhullstatic, or use a different tool chain.\n\n");
    +      return -1;
    +  }
    +#endif
    +
    +  /*
    +    Run 1: convex hull
    +  */
    +  printf( "\ncompute convex hull of cube after rotating input\n");
    +  sprintf(flags, "qhull s Tcv %s", argc >= 2 ? argv[1] : "");
    +  numpoints= SIZEcube;
    +  makecube(points, numpoints, DIM);
    +  for (i=numpoints; i--; )
    +    rows[i]= points+dim*i;
    +  qh_printmatrix(outfile, "input", rows, numpoints, dim);
    +  exitcode= qh_new_qhull(dim, numpoints, points, ismalloc,
    +                      flags, outfile, errfile);
    +  if (!exitcode) {                  /* if no error */
    +    /* 'qh facet_list' contains the convex hull */
    +    print_summary();
    +    FORALLfacets {
    +       /* ... your code ... */
    +    }
    +  }
    +  qh_freeqhull(!qh_ALL);                   /* free long memory  */
    +  qh_memfreeshort(&curlong, &totlong);    /* free short memory and memory allocator */
    +  if (curlong || totlong)
    +    fprintf(errfile, "qhull internal warning (user_eg, #1): did not free %d bytes of long memory (%d pieces)\n", totlong, curlong);
    +
    +  /*
    +    Run 2: Delaunay triangulation
    +  */
    +
    +  printf( "\ncompute %d-d Delaunay triangulation\n", dim);
    +  sprintf(flags, "qhull s d Tcv %s", argc >= 3 ? argv[2] : "");
    +  numpoints= SIZEcube;
    +  makeDelaunay(points, numpoints, dim, (int)time(NULL));
    +  for (i=numpoints; i--; )
    +    rows[i]= points+dim*i;
    +  qh_printmatrix(outfile, "input", rows, numpoints, dim);
    +  exitcode= qh_new_qhull(dim, numpoints, points, ismalloc,
    +                      flags, outfile, errfile);
    +  if (!exitcode) {                  /* if no error */
    +    /* 'qh facet_list' contains the convex hull */
    +    /* If you want a Voronoi diagram ('v') and do not request output (i.e., outfile=NULL),
    +       call qh_setvoronoi_all() after qh_new_qhull(). */
    +    print_summary();
    +    FORALLfacets {
    +       /* ... your code ... */
    +    }
    +    printf( "\nfind %d-d Delaunay triangle closest to [0.5, 0.5, ...]\n", dim);
    +    exitcode= setjmp(qh errexit);
    +    if (!exitcode) {
    +      /* Trap Qhull errors in findDelaunay().  Without the setjmp(), Qhull
    +         will exit() after reporting an error */
    +      qh NOerrexit= False;
    +      findDelaunay(DIM);
    +    }
    +    qh NOerrexit= True;
    +  }
    +#if qh_QHpointer  /* see user.h */
    +  {
    +    qhT *oldqhA, *oldqhB;
    +    coordT pointsB[DIM*TOTpoints]; /* array of coordinates for each point */
    +
    +    printf( "\nsave first triangulation and compute a new triangulation\n");
    +    oldqhA= qh_save_qhull();
    +    sprintf(flags, "qhull s d Tcv %s", argc >= 3 ? argv[2] : "");
    +    numpoints= SIZEcube;
    +    makeDelaunay(pointsB, numpoints, dim, (int)time(NULL)+1);
    +    for (i=numpoints; i--; )
    +      rows[i]= pointsB+dim*i;
    +    qh_printmatrix(outfile, "input", rows, numpoints, dim);
    +    exitcode= qh_new_qhull(dim, numpoints, pointsB, ismalloc,
    +                      flags, outfile, errfile);
    +    if (!exitcode)
    +      print_summary();
    +    printf( "\nsave second triangulation and restore first one\n");
    +    oldqhB= qh_save_qhull();
    +    qh_restore_qhull(&oldqhA);
    +    print_summary();
    +    printf( "\nfree first triangulation and restore second one.\n");
    +    qh_freeqhull(qh_ALL);               /* free short and long memory used by first call */
    +                                         /* do not use qh_memfreeshort */
    +    qh_restore_qhull(&oldqhB);
    +    print_summary();
    +  }
    +#endif
    +  qh_freeqhull(!qh_ALL);                 /* free long memory */
    +  qh_memfreeshort(&curlong, &totlong);  /* free short memory and memory allocator */
    +  if (curlong || totlong)
    +    fprintf(errfile, "qhull internal warning (user_eg, #2): did not free %d bytes of long memory (%d pieces)\n", totlong, curlong);
    +
    +  /*
    +    Run 3: halfspace intersection about the origin
    +  */
    +  printf( "\ncompute halfspace intersection about the origin for a diamond\n");
    +  sprintf(flags, "qhull H0 s Tcv %s", argc >= 4 ? argv[3] : "Fp");
    +  numpoints= SIZEcube;
    +  makehalf(points, numpoints, dim);
    +  for (i=numpoints; i--; )
    +    rows[i]= points+(dim+1)*i;
    +  qh_printmatrix(outfile, "input as halfspace coefficients + offsets", rows, numpoints, dim+1);
    +  /* use qh_sethalfspace_all to transform the halfspaces yourself.
    +     If so, set 'qh feasible_point and do not use option 'Hn,...' [it would retransform the halfspaces]
    +  */
    +  exitcode= qh_new_qhull(dim+1, numpoints, points, ismalloc,
    +                      flags, outfile, errfile);
    +  if (!exitcode)
    +    print_summary();
    +  qh_freeqhull(!qh_ALL);
    +  qh_memfreeshort(&curlong, &totlong);
    +  if (curlong || totlong)  /* could also check previous runs */
    +    fprintf(stderr, "qhull internal warning (user_eg, #3): did not free %d bytes of long memory (%d pieces)\n",
    +       totlong, curlong);
    +  return exitcode;
    +} /* main */
    +
    diff --git a/xs/src/qhull/src/user_eg/user_eg.pro b/xs/src/qhull/src/user_eg/user_eg.pro
    new file mode 100644
    index 000000000..9dda01009
    --- /dev/null
    +++ b/xs/src/qhull/src/user_eg/user_eg.pro
    @@ -0,0 +1,11 @@
    +# -------------------------------------------------
    +# user_eg.pro -- Qt project for Qhull demonstration using shared Qhull library
    +#
    +# It uses reentrant Qhull
    +# -------------------------------------------------
    +
    +include(../qhull-app-shared_r.pri)
    +
    +TARGET = user_eg
    +
    +SOURCES += user_eg_r.c
    diff --git a/xs/src/qhull/src/user_eg/user_eg_r.c b/xs/src/qhull/src/user_eg/user_eg_r.c
    new file mode 100644
    index 000000000..21b0ccf4e
    --- /dev/null
    +++ b/xs/src/qhull/src/user_eg/user_eg_r.c
    @@ -0,0 +1,326 @@
    +/*
      ---------------------------------
    +
    +  user_eg_r.c
    +  sample code for calling qhull() from an application.  Uses reentrant libqhull_r
    +
    +  call with:
    +
    +     user_eg "cube/diamond options" "delaunay options" "halfspace options"
    +
    +  for example:
    +
    +     user_eg                             # return summaries
    +
    +     user_eg "n" "o" "Fp"                # return normals, OFF, points
    +
    +     user_eg "n Qt" "o" "Fp"             # triangulated cube
    +
    +     user_eg "QR0 p" "QR0 v p" "QR0 Fp"  # rotate input and return points
    +                                         # 'v' returns Voronoi
    +                                         # transform is rotated for halfspaces
    +
    +   main() makes three runs of qhull.
    +
    +     1) compute the convex hull of a cube
    +
    +     2a) compute the Delaunay triangulation of random points
    +
    +     2b) find the Delaunay triangle closest to a point.
    +
    +     3) compute the halfspace intersection of a diamond
    +
    + notes:
    +
    +   For another example, see main() in unix_r.c and user_eg2_r.c.
    +   These examples, call qh_qhull() directly.  They allow
    +   tighter control on the code loaded with Qhull.
    +
    +   For a C++ example, see user_eg3/user_eg3_r.cpp
    +
    +   Summaries are sent to stderr if other output formats are used
    +
    +   compiled by 'make bin/user_eg'
    +
    +   see libqhull.h for data structures, macros, and user-callable functions.
    +*/
    +
    +#define qh_QHimport
    +#include "libqhull_r/qhull_ra.h"
    +
    +/*-------------------------------------------------
    +-internal function prototypes
    +*/
    +void print_summary(qhT *qh);
    +void makecube(coordT *points, int numpoints, int dim);
    +void makeDelaunay(qhT *qh, coordT *points, int numpoints, int dim, int seed);
    +void findDelaunay(qhT *qh, int dim);
    +void makehalf(coordT *points, int numpoints, int dim);
    +
    +/*-------------------------------------------------
    +-print_summary(qh)
    +*/
    +void print_summary(qhT *qh) {
    +  facetT *facet;
    +  int k;
    +
    +  printf("\n%d vertices and %d facets with normals:\n",
    +                 qh->num_vertices, qh->num_facets);
    +  FORALLfacets {
    +    for (k=0; k < qh->hull_dim; k++)
    +      printf("%6.2g ", facet->normal[k]);
    +    printf("\n");
    +  }
    +}
    +
    +/*--------------------------------------------------
    +-makecube- set points to vertices of cube
    +  points is numpoints X dim
    +*/
    +void makecube(coordT *points, int numpoints, int dim) {
    +  int j,k;
    +  coordT *point;
    +
    +  for (j=0; jlocate a facet with qh_findbestfacet()
    +*/
    +void findDelaunay(qhT *qh, int dim) {
    +  int k;
    +  coordT point[ 100];
    +  boolT isoutside;
    +  realT bestdist;
    +  facetT *facet;
    +  vertexT *vertex, **vertexp;
    +
    +  for (k= 0; k < dim; k++)
    +    point[k]= 0.5;
    +  qh_setdelaunay(qh, dim+1, 1, point);
    +  facet= qh_findbestfacet(qh, point, qh_ALL, &bestdist, &isoutside);
    +  if (facet->tricoplanar) {
    +    fprintf(stderr, "findDelaunay: not implemented for triangulated, non-simplicial Delaunay regions (tricoplanar facet, f%d).\n",
    +       facet->id);
    +    qh_errexit(qh, qh_ERRqhull, facet, NULL);
    +  }
    +  FOREACHvertex_(facet->vertices) {
    +    for (k=0; k < dim; k++)
    +      printf("%5.2f ", vertex->point[k]);
    +    printf("\n");
    +  }
    +} /*.findDelaunay.*/
    +
    +/*--------------------------------------------------
    +-makehalf- set points to halfspaces for a (dim)-dimensional diamond
    +  points is numpoints X dim+1
    +
    +  each halfspace consists of dim coefficients followed by an offset
    +*/
    +void makehalf(coordT *points, int numpoints, int dim) {
    +  int j,k;
    +  coordT *point;
    +
    +  for (j=0; j&1'\n\n");
    +
    +  /*
    +    Run 1: convex hull
    +  */
    +  printf( "\ncompute convex hull of cube after rotating input\n");
    +  sprintf(flags, "qhull s Tcv %s", argc >= 2 ? argv[1] : "");
    +  numpoints= SIZEcube;
    +  makecube(points, numpoints, DIM);
    +  for (i=numpoints; i--; )
    +    rows[i]= points+dim*i;
    +  qh_printmatrix(qh, outfile, "input", rows, numpoints, dim);
    +  exitcode= qh_new_qhull(qh, dim, numpoints, points, ismalloc,
    +                      flags, outfile, errfile);
    +  if (!exitcode) {                  /* if no error */
    +    /* 'qh->facet_list' contains the convex hull */
    +    print_summary(qh);
    +    FORALLfacets {
    +       /* ... your code ... */
    +    }
    +  }
    +  qh_freeqhull(qh, !qh_ALL);                   /* free long memory  */
    +  qh_memfreeshort(qh, &curlong, &totlong);    /* free short memory and memory allocator */
    +  if (curlong || totlong)
    +    fprintf(errfile, "qhull internal warning (user_eg, #1): did not free %d bytes of long memory (%d pieces)\n", totlong, curlong);
    +
    +  /*
    +    Run 2: Delaunay triangulation, reusing the previous qh/qh_qh
    +  */
    +
    +  printf( "\ncompute %d-d Delaunay triangulation\n", dim);
    +  sprintf(flags, "qhull s d Tcv %s", argc >= 3 ? argv[2] : "");
    +  numpoints= SIZEcube;
    +  makeDelaunay(qh, points, numpoints, dim, (int)time(NULL));
    +  for (i=numpoints; i--; )
    +    rows[i]= points+dim*i;
    +  qh_printmatrix(qh, outfile, "input", rows, numpoints, dim);
    +  exitcode= qh_new_qhull(qh, dim, numpoints, points, ismalloc,
    +                      flags, outfile, errfile);
    +  if (!exitcode) {                  /* if no error */
    +    /* 'qh->facet_list' contains the convex hull */
    +    /* If you want a Voronoi diagram ('v') and do not request output (i.e., outfile=NULL),
    +       call qh_setvoronoi_all() after qh_new_qhull(). */
    +    print_summary(qh);
    +    FORALLfacets {
    +       /* ... your code ... */
    +    }
    +    printf( "\nfind %d-d Delaunay triangle closest to [0.5, 0.5, ...]\n", dim);
    +    exitcode= setjmp(qh->errexit);
    +    if (!exitcode) {
    +      /* Trap Qhull errors in findDelaunay().  Without the setjmp(), Qhull
    +         will exit() after reporting an error */
    +      qh->NOerrexit= False;
    +      findDelaunay(qh, DIM);
    +    }
    +    qh->NOerrexit= True;
    +  }
    +  {
    +    coordT pointsB[DIM*TOTpoints]; /* array of coordinates for each point */
    +
    +    qhT qh_qhB;    /* Create a new instance of Qhull (qhB) */
    +    qhT *qhB= &qh_qhB;
    +    qh_zero(qhB, errfile);
    +
    +    printf( "\nCompute a new triangulation as a separate instance of Qhull\n");
    +    sprintf(flags, "qhull s d Tcv %s", argc >= 3 ? argv[2] : "");
    +    numpoints= SIZEcube;
    +    makeDelaunay(qhB, pointsB, numpoints, dim, (int)time(NULL)+1);
    +    for (i=numpoints; i--; )
    +      rows[i]= pointsB+dim*i;
    +    qh_printmatrix(qhB, outfile, "input", rows, numpoints, dim);
    +    exitcode= qh_new_qhull(qhB, dim, numpoints, pointsB, ismalloc,
    +                      flags, outfile, errfile);
    +    if (!exitcode)
    +      print_summary(qhB);
    +    printf( "\nFree memory allocated by the new instance of Qhull, and redisplay the old results.\n");
    +    qh_freeqhull(qhB, !qh_ALL);                 /* free long memory */
    +    qh_memfreeshort(qhB, &curlong, &totlong);  /* free short memory and memory allocator */
    +    if (curlong || totlong)
    +        fprintf(errfile, "qhull internal warning (user_eg, #4): did not free %d bytes of long memory (%d pieces)\n", totlong, curlong);
    +
    +    printf( "\n\n");
    +    print_summary(qh);  /* The other instance is unchanged */
    +    /* Exiting the block frees qh_qhB */
    +  }
    +  qh_freeqhull(qh, !qh_ALL);                 /* free long memory */
    +  qh_memfreeshort(qh, &curlong, &totlong);  /* free short memory and memory allocator */
    +  if (curlong || totlong)
    +    fprintf(errfile, "qhull internal warning (user_eg, #2): did not free %d bytes of long memory (%d pieces)\n", totlong, curlong);
    +
    +  /*
    +    Run 3: halfspace intersection about the origin
    +  */
    +  printf( "\ncompute halfspace intersection about the origin for a diamond\n");
    +  sprintf(flags, "qhull H0 s Tcv %s", argc >= 4 ? argv[3] : "Fp");
    +  numpoints= SIZEcube;
    +  makehalf(points, numpoints, dim);
    +  for (i=numpoints; i--; )
    +    rows[i]= points+(dim+1)*i;
    +  qh_printmatrix(qh, outfile, "input as halfspace coefficients + offsets", rows, numpoints, dim+1);
    +  /* use qh_sethalfspace_all to transform the halfspaces yourself.
    +     If so, set 'qh->feasible_point and do not use option 'Hn,...' [it would retransform the halfspaces]
    +  */
    +  exitcode= qh_new_qhull(qh, dim+1, numpoints, points, ismalloc,
    +                      flags, outfile, errfile);
    +  if (!exitcode)
    +    print_summary(qh);
    +  qh_freeqhull(qh, !qh_ALL);
    +  qh_memfreeshort(qh, &curlong, &totlong);
    +  if (curlong || totlong)  /* could also check previous runs */
    +    fprintf(stderr, "qhull internal warning (user_eg, #3): did not free %d bytes of long memory (%d pieces)\n",
    +       totlong, curlong);
    +  return exitcode;
    +} /* main */
    +
    diff --git a/xs/src/qhull/src/user_eg2/user_eg2.c b/xs/src/qhull/src/user_eg2/user_eg2.c
    new file mode 100644
    index 000000000..a455f025d
    --- /dev/null
    +++ b/xs/src/qhull/src/user_eg2/user_eg2.c
    @@ -0,0 +1,746 @@
    +/*
      ---------------------------------
    +
    +  user_eg2.c
    +
    +  sample code for calling qhull() from an application.
    +
    +  See user_eg.c for a simpler method using qh_new_qhull().
    +  The method used here and in unix.c gives you additional
    +  control over Qhull.
    +
    +  See user_eg3/user_eg3_r.cpp for a C++ example
    +
    +  call with:
    +
    +     user_eg2 "triangulated cube/diamond options" "delaunay options" "halfspace options"
    +
    +  for example:
    +
    +     user_eg2                             # return summaries
    +
    +     user_eg2 "n" "o" "Fp"                # return normals, OFF, points
    +
    +     user_eg2 "QR0 p" "QR0 v p" "QR0 Fp"  # rotate input and return points
    +                                         # 'v' returns Voronoi
    +                                         # transform is rotated for halfspaces
    +
    +   main() makes three runs of qhull.
    +
    +     1) compute the convex hull of a cube, and incrementally add a diamond
    +
    +     2a) compute the Delaunay triangulation of random points, and add points.
    +
    +     2b) find the Delaunay triangle closest to a point.
    +
    +     3) compute the halfspace intersection of a diamond, and add a cube
    +
    + notes:
    +
    +   summaries are sent to stderr if other output formats are used
    +
    +   derived from unix.c and compiled by 'make bin/user_eg2'
    +
    +   see libqhull.h for data structures, macros, and user-callable functions.
    +
    +   If you want to control all output to stdio and input to stdin,
    +   set the #if below to "1" and delete all lines that contain "io.c".
    +   This prevents the loading of io.o.  Qhull will
    +   still write to 'qh ferr' (stderr) for error reporting and tracing.
    +
    +   Defining #if 1, also prevents user.o from being loaded.
    +*/
    +
    +#include "libqhull/qhull_a.h"
    +
    +/*-------------------------------------------------
    +-internal function prototypes
    +*/
    +void print_summary(void);
    +void makecube(coordT *points, int numpoints, int dim);
    +void adddiamond(coordT *points, int numpoints, int numnew, int dim);
    +void makeDelaunay(coordT *points, int numpoints, int dim);
    +void addDelaunay(coordT *points, int numpoints, int numnew, int dim);
    +void findDelaunay(int dim);
    +void makehalf(coordT *points, int numpoints, int dim);
    +void addhalf(coordT *points, int numpoints, int numnew, int dim, coordT *feasible);
    +
    +/*-------------------------------------------------
    +-print_summary()
    +*/
    +void print_summary(void) {
    +  facetT *facet;
    +  int k;
    +
    +  printf("\n%d vertices and %d facets with normals:\n",
    +                 qh num_vertices, qh num_facets);
    +  FORALLfacets {
    +    for (k=0; k < qh hull_dim; k++)
    +      printf("%6.2g ", facet->normal[k]);
    +    printf("\n");
    +  }
    +}
    +
    +/*--------------------------------------------------
    +-makecube- set points to vertices of cube
    +  points is numpoints X dim
    +*/
    +void makecube(coordT *points, int numpoints, int dim) {
    +  int j,k;
    +  coordT *point;
    +
    +  for (j=0; jlocate a facet with qh_findbestfacet()
    +*/
    +void findDelaunay(int dim) {
    +  int k;
    +  coordT point[ 100];
    +  boolT isoutside;
    +  realT bestdist;
    +  facetT *facet;
    +  vertexT *vertex, **vertexp;
    +
    +  for (k= 0; k < dim-1; k++)
    +    point[k]= 0.5;
    +  qh_setdelaunay(dim, 1, point);
    +  facet= qh_findbestfacet(point, qh_ALL, &bestdist, &isoutside);
    +  if (facet->tricoplanar) {
    +    fprintf(stderr, "findDelaunay: not implemented for triangulated, non-simplicial Delaunay regions (tricoplanar facet, f%d).\n",
    +       facet->id);
    +    qh_errexit(qh_ERRqhull, facet, NULL);
    +  }
    +  FOREACHvertex_(facet->vertices) {
    +    for (k=0; k < dim-1; k++)
    +      printf("%5.2f ", vertex->point[k]);
    +    printf("\n");
    +  }
    +} /*.findDelaunay.*/
    +
    +/*--------------------------------------------------
    +-makehalf- set points to halfspaces for a (dim)-d diamond
    +  points is numpoints X dim+1
    +
    +  each halfspace consists of dim coefficients followed by an offset
    +*/
    +void makehalf(coordT *points, int numpoints, int dim) {
    +  int j,k;
    +  coordT *point;
    +
    +  for (j=0; j= 2 ? argv[1] : "");
    +    qh_initflags(options);
    +    printf( "\ncompute triangulated convex hull of cube after rotating input\n");
    +    makecube(array[0], SIZEcube, DIM);
    +    qh_init_B(array[0], SIZEcube, DIM, ismalloc);
    +    qh_qhull();
    +    qh_check_output();
    +    qh_triangulate();  /* requires option 'Q11' if want to add points */
    +    print_summary();
    +    if (qh VERIFYoutput && !qh STOPpoint && !qh STOPcone)
    +      qh_check_points();
    +    printf( "\nadd points in a diamond\n");
    +    adddiamond(array[0], SIZEcube, SIZEdiamond, DIM);
    +    qh_check_output();
    +    print_summary();
    +    qh_produce_output();  /* delete this line to help avoid io.c */
    +    if (qh VERIFYoutput && !qh STOPpoint && !qh STOPcone)
    +      qh_check_points();
    +  }
    +  qh NOerrexit= True;
    +  qh_freeqhull(!qh_ALL);
    +  qh_memfreeshort(&curlong, &totlong);
    +  if (curlong || totlong)
    +    fprintf(stderr, "qhull warning (user_eg, run 1): did not free %d bytes of long memory (%d pieces)\n",
    +       totlong, curlong);
    +
    +  /*
    +    Run 2: Delaunay triangulation
    +  */
    +  qh_init_A(stdin, stdout, stderr, 0, NULL);
    +  exitcode= setjmp(qh errexit);
    +  if (!exitcode) {
    +    coordT array[TOTpoints][DIM];
    +
    +    strcat(qh rbox_command, "user_eg Delaunay");
    +    sprintf(options, "qhull s d Tcv %s", argc >= 3 ? argv[2] : "");
    +    qh_initflags(options);
    +    printf( "\ncompute %d-d Delaunay triangulation\n", DIM-1);
    +    makeDelaunay(array[0], SIZEcube, DIM);
    +    /* Instead of makeDelaunay with qh_setdelaunay, you may
    +       produce a 2-d array of points, set DIM to 2, and set
    +       qh PROJECTdelaunay to True.  qh_init_B will call
    +       qh_projectinput to project the points to the paraboloid
    +       and add a point "at-infinity".
    +    */
    +    qh_init_B(array[0], SIZEcube, DIM, ismalloc);
    +    qh_qhull();
    +    /* If you want Voronoi ('v') without qh_produce_output(), call
    +       qh_setvoronoi_all() after qh_qhull() */
    +    qh_check_output();
    +    print_summary();
    +    qh_produce_output();  /* delete this line to help avoid io.c */
    +    if (qh VERIFYoutput && !qh STOPpoint && !qh STOPcone)
    +      qh_check_points();
    +    printf( "\nadd points to triangulation\n");
    +    addDelaunay(array[0], SIZEcube, SIZEdiamond, DIM);
    +    qh_check_output();
    +    qh_produce_output();  /* delete this line to help avoid io.c */
    +    if (qh VERIFYoutput && !qh STOPpoint && !qh STOPcone)
    +      qh_check_points();
    +    printf( "\nfind Delaunay triangle closest to [0.5, 0.5, ...]\n");
    +    findDelaunay(DIM);
    +  }
    +  qh NOerrexit= True;
    +  qh_freeqhull(!qh_ALL);
    +  qh_memfreeshort(&curlong, &totlong);
    +  if (curlong || totlong)
    +    fprintf(stderr, "qhull warning (user_eg, run 2): did not free %d bytes of long memory (%d pieces)\n",
    +       totlong, curlong);
    +
    +  /*
    +    Run 3: halfspace intersection
    +  */
    +  qh_init_A(stdin, stdout, stderr, 0, NULL);
    +  exitcode= setjmp(qh errexit);
    +  if (!exitcode) {
    +    coordT array[TOTpoints][DIM+1];  /* +1 for halfspace offset */
    +    pointT *points;
    +
    +    strcat(qh rbox_command, "user_eg halfspaces");
    +    sprintf(options, "qhull H0 s Tcv %s", argc >= 4 ? argv[3] : "");
    +    qh_initflags(options);
    +    printf( "\ncompute halfspace intersection about the origin for a diamond\n");
    +    makehalf(array[0], SIZEcube, DIM);
    +    qh_setfeasible(DIM); /* from io.c, sets qh feasible_point from 'Hn,n' */
    +    /* you may malloc and set qh feasible_point directly.  It is only used for
    +       option 'Fp' */
    +    points= qh_sethalfspace_all( DIM+1, SIZEcube, array[0], qh feasible_point);
    +    qh_init_B(points, SIZEcube, DIM, True); /* qh_freeqhull frees points */
    +    qh_qhull();
    +    qh_check_output();
    +    qh_produce_output();  /* delete this line to help avoid io.c */
    +    if (qh VERIFYoutput && !qh STOPpoint && !qh STOPcone)
    +      qh_check_points();
    +    printf( "\nadd halfspaces for cube to intersection\n");
    +    addhalf(array[0], SIZEcube, SIZEdiamond, DIM, qh feasible_point);
    +    qh_check_output();
    +    qh_produce_output();  /* delete this line to help avoid io.c */
    +    if (qh VERIFYoutput && !qh STOPpoint && !qh STOPcone)
    +      qh_check_points();
    +  }
    +  qh NOerrexit= True;
    +  qh NOerrexit= True;
    +  qh_freeqhull(!qh_ALL);
    +  qh_memfreeshort(&curlong, &totlong);
    +  if (curlong || totlong)
    +    fprintf(stderr, "qhull warning (user_eg, run 3): did not free %d bytes of long memory (%d pieces)\n",
    +       totlong, curlong);
    +  return exitcode;
    +} /* main */
    +
    +#if 1    /* use 1 to prevent loading of io.o and user.o */
    +/*-------------------------------------------
    +-errexit- return exitcode to system after an error
    +  assumes exitcode non-zero
    +  prints useful information
    +  see qh_errexit2() in libqhull.c for 2 facets
    +*/
    +void qh_errexit(int exitcode, facetT *facet, ridgeT *ridge) {
    +  QHULL_UNUSED(facet);
    +  QHULL_UNUSED(ridge);
    +
    +  if (qh ERREXITcalled) {
    +    fprintf(qh ferr, "qhull error while processing previous error.  Exit program\n");
    +    exit(1);
    +  }
    +  qh ERREXITcalled= True;
    +  if (!qh QHULLfinished)
    +    qh hulltime= (unsigned)clock() - qh hulltime;
    +  fprintf(qh ferr, "\nWhile executing: %s | %s\n", qh rbox_command, qh qhull_command);
    +  fprintf(qh ferr, "Options selected:\n%s\n", qh qhull_options);
    +  if (qh furthest_id >= 0) {
    +    fprintf(qh ferr, "\nLast point added to hull was p%d", qh furthest_id);
    +    if (zzval_(Ztotmerge))
    +      fprintf(qh ferr, "  Last merge was #%d.", zzval_(Ztotmerge));
    +    if (qh QHULLfinished)
    +      fprintf(qh ferr, "\nQhull has finished constructing the hull.");
    +    else if (qh POSTmerging)
    +      fprintf(qh ferr, "\nQhull has started post-merging");
    +    fprintf(qh ferr, "\n\n");
    +  }
    +  if (qh NOerrexit) {
    +    fprintf(qh ferr, "qhull error while ending program.  Exit program\n");
    +    exit(1);
    +  }
    +  if (!exitcode)
    +    exitcode= qh_ERRqhull;
    +  qh NOerrexit= True;
    +  longjmp(qh errexit, exitcode);
    +} /* errexit */
    +
    +
    +/*-------------------------------------------
    +-errprint- prints out the information of the erroneous object
    +    any parameter may be NULL, also prints neighbors and geomview output
    +*/
    +void qh_errprint(const char *string, facetT *atfacet, facetT *otherfacet, ridgeT *atridge, vertexT *atvertex) {
    +
    +  fprintf(qh ferr, "%s facets f%d f%d ridge r%d vertex v%d\n",
    +           string, getid_(atfacet), getid_(otherfacet), getid_(atridge),
    +           getid_(atvertex));
    +} /* errprint */
    +
    +
    +void qh_printfacetlist(facetT *facetlist, setT *facets, boolT printall) {
    +  facetT *facet, **facetp;
    +
    +  /* remove these calls to help avoid io.c */
    +  qh_printbegin(qh ferr, qh_PRINTfacets, facetlist, facets, printall);/*io.c*/
    +  FORALLfacet_(facetlist)                                              /*io.c*/
    +    qh_printafacet(qh ferr, qh_PRINTfacets, facet, printall);          /*io.c*/
    +  FOREACHfacet_(facets)                                                /*io.c*/
    +    qh_printafacet(qh ferr, qh_PRINTfacets, facet, printall);          /*io.c*/
    +  qh_printend(qh ferr, qh_PRINTfacets, facetlist, facets, printall);  /*io.c*/
    +
    +  FORALLfacet_(facetlist)
    +    fprintf( qh ferr, "facet f%d\n", facet->id);
    +} /* printfacetlist */
    +
    +/* qh_printhelp_degenerate( fp )
    +    prints descriptive message for precision error
    +
    +  notes:
    +    no message if qh_QUICKhelp
    +*/
    +void qh_printhelp_degenerate(FILE *fp) {
    +
    +  if (qh MERGEexact || qh PREmerge || qh JOGGLEmax < REALmax/2)
    +    qh_fprintf(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(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(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(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(fp, 9372, "\
    +When computing the Delaunay triangulation:\n\
    +  - use 'Qz' to add a point at-infinity.  This reduces precision problems.\n");
    +
    +    qh_fprintf(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(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 */
    +
    +
    +/* qh_printhelp_narrowhull( minangle )
    +     Warn about a narrow hull
    +
    +  notes:
    +    Alternatively, reduce qh_WARNnarrow in user.h
    +
    +*/
    +void qh_printhelp_narrowhull(FILE *fp, realT minangle) {
    +
    +    qh_fprintf(fp, 9375, "qhull precision warning: \n\
    +The initial hull is narrow (cosine of min. angle is %.16f).\n\
    +A coplanar point may lead to a wide facet.  Options 'QbB' (scale to unit box)\n\
    +or 'Qbb' (scale last coordinate) may remove this warning.  Use 'Pp' to skip\n\
    +this warning.  See 'Limitations' in qh-impre.htm.\n",
    +          -minangle);   /* convert from angle between normals to angle between facets */
    +} /* printhelp_narrowhull */
    +
    +/* qh_printhelp_singular
    +      prints descriptive message for singular input
    +*/
    +void qh_printhelp_singular(FILE *fp) {
    +  facetT *facet;
    +  vertexT *vertex, **vertexp;
    +  realT min, max, *coord, dist;
    +  int i,k;
    +
    +  qh_fprintf(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(fp, "", qh facet_list, NULL, qh_ALL);
    +  if (!qh_QUICKhelp)
    +    qh_fprintf(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(fp, "center point", qh hull_dim, qh interior_point, -1);
    +  qh_fprintf(fp, 9378, "\n");
    +  FORALLfacets {
    +    qh_fprintf(fp, 9379, "facet");
    +    FOREACHvertex_(facet->vertices)
    +      qh_fprintf(fp, 9380, " p%d", qh_pointid(vertex->point));
    +    zinc_(Zdistio);
    +    qh_distplane(qh interior_point, facet, &dist);
    +    qh_fprintf(fp, 9381, " distance= %4.2g\n", dist);
    +  }
    +  if (!qh_QUICKhelp) {
    +    if (qh HALFspace)
    +      qh_fprintf(fp, 9382, "\n\
    +These points are the dual of the given halfspaces.  They indicate that\n\
    +the intersection is degenerate.\n");
    +    qh_fprintf(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(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(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(fp, 9386, "  %d:  %8.4g  %8.4g  difference= %4.4g\n", k, min, max, max-min);
    +  }
    +  if (!qh_QUICKhelp) {
    +    qh_fprintf(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(fp, 9388, "\
    +  - recompile qhull for realT precision(#define REALfloat 0 in libqhull.h).\n");
    +#endif
    +    qh_fprintf(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\
    +");
    +    if (qh DELAUNAY && !qh ATinfinity)
    +      qh_fprintf(fp, 9390, "\n\n\
    +This is a Delaunay triangulation and the input is co-circular or co-spherical:\n\
    +  - use 'Qz' to add a point \"at infinity\" (i.e., above the paraboloid)\n\
    +  - or use 'QJ' to joggle the input and avoid co-circular data\n");
    +  }
    +} /* printhelp_singular */
    +
    +
    +/*-----------------------------------------
    +-user_memsizes- allocate up to 10 additional, quick allocation sizes
    +*/
    +void qh_user_memsizes(void) {
    +
    +  /* qh_memsize(size); */
    +} /* user_memsizes */
    +
    +#endif
    diff --git a/xs/src/qhull/src/user_eg2/user_eg2.pro b/xs/src/qhull/src/user_eg2/user_eg2.pro
    new file mode 100644
    index 000000000..c841bfe13
    --- /dev/null
    +++ b/xs/src/qhull/src/user_eg2/user_eg2.pro
    @@ -0,0 +1,11 @@
    +# -------------------------------------------------
    +# user_eg2.pro -- Qt project for Qhull demonstration using the static Qhull library
    +#
    +# It uses reentrant Qhull
    +# -------------------------------------------------
    +
    +include(../qhull-app-c_r.pri)
    +
    +TARGET = user_eg2
    +
    +SOURCES += user_eg2_r.c
    diff --git a/xs/src/qhull/src/user_eg2/user_eg2_r.c b/xs/src/qhull/src/user_eg2/user_eg2_r.c
    new file mode 100644
    index 000000000..2f8b4e6c7
    --- /dev/null
    +++ b/xs/src/qhull/src/user_eg2/user_eg2_r.c
    @@ -0,0 +1,742 @@
    +/*
      ---------------------------------
    +
    +  user_eg2_r.c
    +
    +  sample code for calling qhull() from an application.
    +
    +  See user_eg_r.c for a simpler method using qh_new_qhull().
    +  The method used here and in unix_r.c gives you additional
    +  control over Qhull.
    +
    +  See user_eg3/user_eg3_r.cpp for a C++ example
    +
    +  call with:
    +
    +     user_eg2 "triangulated cube/diamond options" "delaunay options" "halfspace options"
    +
    +  for example:
    +
    +     user_eg2                             # return summaries
    +
    +     user_eg2 "n" "o" "Fp"                # return normals, OFF, points
    +
    +     user_eg2 "QR0 p" "QR0 v p" "QR0 Fp"  # rotate input and return points
    +                                         # 'v' returns Voronoi
    +                                         # transform is rotated for halfspaces
    +
    +   main() makes three runs of qhull.
    +
    +     1) compute the convex hull of a cube, and incrementally add a diamond
    +
    +     2a) compute the Delaunay triangulation of random points, and add points.
    +
    +     2b) find the Delaunay triangle closest to a point.
    +
    +     3) compute the halfspace intersection of a diamond, and add a cube
    +
    + notes:
    +
    +   summaries are sent to stderr if other output formats are used
    +
    +   derived from unix.c and compiled by 'make bin/user_eg2'
    +
    +   see libqhull.h for data structures, macros, and user-callable functions.
    +
    +   If you want to control all output to stdio and input to stdin,
    +   set the #if below to "1" and delete all lines that contain "io.c".
    +   This prevents the loading of io.o.  Qhull will
    +   still write to 'qh->ferr' (stderr) for error reporting and tracing.
    +
    +   Defining #if 1, also prevents user.o from being loaded.
    +*/
    +
    +#include "libqhull_r/qhull_ra.h"
    +
    +/*-------------------------------------------------
    +-internal function prototypes
    +*/
    +void print_summary(qhT *qh);
    +void makecube(coordT *points, int numpoints, int dim);
    +void adddiamond(qhT *qh, coordT *points, int numpoints, int numnew, int dim);
    +void makeDelaunay(qhT *qh, coordT *points, int numpoints, int dim);
    +void addDelaunay(qhT *qh, coordT *points, int numpoints, int numnew, int dim);
    +void findDelaunay(qhT *qh, int dim);
    +void makehalf(coordT *points, int numpoints, int dim);
    +void addhalf(qhT *qh, coordT *points, int numpoints, int numnew, int dim, coordT *feasible);
    +
    +/*-------------------------------------------------
    +-print_summary(qh)
    +*/
    +void print_summary(qhT *qh) {
    +  facetT *facet;
    +  int k;
    +
    +  printf("\n%d vertices and %d facets with normals:\n",
    +                 qh->num_vertices, qh->num_facets);
    +  FORALLfacets {
    +    for (k=0; k < qh->hull_dim; k++)
    +      printf("%6.2g ", facet->normal[k]);
    +    printf("\n");
    +  }
    +}
    +
    +/*--------------------------------------------------
    +-makecube- set points to vertices of cube
    +  points is numpoints X dim
    +*/
    +void makecube(coordT *points, int numpoints, int dim) {
    +  int j,k;
    +  coordT *point;
    +
    +  for (j=0; jfirst_point)  /* in case of 'QRn' */
    +      qh->num_points= numpoints+j+1;
    +    /* qh.num_points sets the size of the points array.  You may
    +       allocate the points elsewhere.  If so, qh_addpoint records
    +       the point's address in qh->other_points
    +    */
    +    for (k=dim; k--; ) {
    +      if (j/2 == k)
    +        point[k]= (j & 1) ? 2.0 : -2.0;
    +      else
    +        point[k]= 0.0;
    +    }
    +    facet= qh_findbestfacet(qh, point, !qh_ALL, &bestdist, &isoutside);
    +    if (isoutside) {
    +      if (!qh_addpoint(qh, point, facet, False))
    +        break;  /* user requested an early exit with 'TVn' or 'TCn' */
    +    }
    +    printf("%d vertices and %d facets\n",
    +                 qh->num_vertices, qh->num_facets);
    +    /* qh_produce_output(); */
    +  }
    +  if (qh->DOcheckmax)
    +    qh_check_maxout(qh);
    +  else if (qh->KEEPnearinside)
    +    qh_nearcoplanar(qh);
    +} /*.adddiamond.*/
    +
    +/*--------------------------------------------------
    +-makeDelaunay- set points for dim-1 Delaunay triangulation of random points
    +  points is numpoints X dim.  Each point is projected to a paraboloid.
    +*/
    +void makeDelaunay(qhT *qh, coordT *points, int numpoints, int dim) {
    +  int j,k, seed;
    +  coordT *point, realr;
    +
    +  seed= (int)time(NULL); /* time_t to int */
    +  printf("seed: %d\n", seed);
    +  qh_RANDOMseed_(qh, seed);
    +  for (j=0; jfirst_point)  /* in case of 'QRn' */
    +      qh->num_points= numpoints+j+1;
    +    /* qh.num_points sets the size of the points array.  You may
    +       allocate the point elsewhere.  If so, qh_addpoint records
    +       the point's address in qh->other_points
    +    */
    +    for (k= 0; k < dim-1; k++) {
    +      realr= qh_RANDOMint;
    +      point[k]= 2.0 * realr/(qh_RANDOMmax+1) - 1.0;
    +    }
    +    qh_setdelaunay(qh, dim, 1, point);
    +    facet= qh_findbestfacet(qh, point, !qh_ALL, &bestdist, &isoutside);
    +    if (isoutside) {
    +      if (!qh_addpoint(qh, point, facet, False))
    +        break;  /* user requested an early exit with 'TVn' or 'TCn' */
    +    }
    +    qh_printpoint(qh, stdout, "added point", point);
    +    printf("%d points, %d extra points, %d vertices, and %d facets in total\n",
    +                  qh->num_points, qh_setsize(qh, qh->other_points),
    +                  qh->num_vertices, qh->num_facets);
    +
    +    /* qh_produce_output(qh); */
    +  }
    +  if (qh->DOcheckmax)
    +    qh_check_maxout(qh);
    +  else if (qh->KEEPnearinside)
    +    qh_nearcoplanar(qh);
    +} /*.addDelaunay.*/
    +
    +/*--------------------------------------------------
    +-findDelaunay- find Delaunay triangle for [0.5,0.5,...]
    +  assumes dim < 100
    +notes:
    +  calls qh_setdelaunay() to project the point to a parabaloid
    +warning:
    +  This is not implemented for tricoplanar facets ('Qt'),
    +  See locate a facet with qh_findbestfacet()
    +*/
    +void findDelaunay(qhT *qh, int dim) {
    +  int k;
    +  coordT point[ 100];
    +  boolT isoutside;
    +  realT bestdist;
    +  facetT *facet;
    +  vertexT *vertex, **vertexp;
    +
    +  for (k= 0; k < dim-1; k++)
    +    point[k]= 0.5;
    +  qh_setdelaunay(qh, dim, 1, point);
    +  facet= qh_findbestfacet(qh, point, qh_ALL, &bestdist, &isoutside);
    +  if (facet->tricoplanar) {
    +    fprintf(stderr, "findDelaunay: not implemented for triangulated, non-simplicial Delaunay regions (tricoplanar facet, f%d).\n",
    +       facet->id);
    +    qh_errexit(qh, qh_ERRqhull, facet, NULL);
    +  }
    +  FOREACHvertex_(facet->vertices) {
    +    for (k=0; k < dim-1; k++)
    +      printf("%5.2f ", vertex->point[k]);
    +    printf("\n");
    +  }
    +} /*.findDelaunay.*/
    +
    +/*--------------------------------------------------
    +-makehalf- set points to halfspaces for a (dim)-d diamond
    +  points is numpoints X dim+1
    +
    +  each halfspace consists of dim coefficients followed by an offset
    +*/
    +void makehalf(coordT *points, int numpoints, int dim) {
    +  int j,k;
    +  coordT *point;
    +
    +  for (j=0; jnum_points, qh_setsize(qh, qh->other_points),
    +                  qh->num_vertices, qh->num_facets);
    +    /* qh_produce_output(qh); */
    +  }
    +  if (qh->DOcheckmax)
    +    qh_check_maxout(qh);
    +  else if (qh->KEEPnearinside)
    +    qh_nearcoplanar(qh);
    +} /*.addhalf.*/
    +
    +#define DIM 3     /* dimension of points, must be < 31 for SIZEcube */
    +#define SIZEcube (1<&1'\n\n");
    +
    +  ismalloc= False;      /* True if qh_freeqhull should 'free(array)' */
    +  /*
    +    Run 1: convex hull
    +  */
    +  qh_init_A(qh, stdin, stdout, stderr, 0, NULL);
    +  exitcode= setjmp(qh->errexit);
    +  if (!exitcode) {
    +    coordT array[TOTpoints][DIM];
    +
    +    qh->NOerrexit= False;
    +    strcat(qh->rbox_command, "user_eg2 cube example");
    +    sprintf(options, "qhull s Tcv Q11 %s ", argc >= 2 ? argv[1] : "");
    +    qh_initflags(qh, options);
    +    printf( "\ncompute triangulated convex hull of cube after rotating input\n");
    +    makecube(array[0], SIZEcube, DIM);
    +    qh_init_B(qh, array[0], SIZEcube, DIM, ismalloc);
    +    qh_qhull(qh);
    +    qh_check_output(qh);
    +    qh_triangulate(qh);  /* requires option 'Q11' if want to add points */
    +    print_summary(qh);
    +    if (qh->VERIFYoutput && !qh->STOPpoint && !qh->STOPcone)
    +      qh_check_points(qh);
    +    printf( "\nadd points in a diamond\n");
    +    adddiamond(qh, array[0], SIZEcube, SIZEdiamond, DIM);
    +    qh_check_output(qh);
    +    print_summary(qh);
    +    qh_produce_output(qh);  /* delete this line to help avoid io.c */
    +    if (qh->VERIFYoutput && !qh->STOPpoint && !qh->STOPcone)
    +      qh_check_points(qh);
    +  }
    +  qh->NOerrexit= True;
    +  qh_freeqhull(qh, !qh_ALL);
    +  qh_memfreeshort(qh, &curlong, &totlong);
    +  if (curlong || totlong)
    +      fprintf(stderr, "qhull warning (user_eg2, run 1): did not free %d bytes of long memory (%d pieces)\n",
    +          totlong, curlong);
    +  /*
    +    Run 2: Delaunay triangulation
    +  */
    +  qh_init_A(qh, stdin, stdout, stderr, 0, NULL);
    +  exitcode= setjmp(qh->errexit);
    +  if (!exitcode) {
    +    coordT array[TOTpoints][DIM];
    +
    +    qh->NOerrexit= False;
    +    strcat(qh->rbox_command, "user_eg2 Delaunay example");
    +    sprintf(options, "qhull s d Tcv %s", argc >= 3 ? argv[2] : "");
    +    qh_initflags(qh, options);
    +    printf( "\ncompute %d-d Delaunay triangulation\n", DIM-1);
    +    makeDelaunay(qh, array[0], SIZEcube, DIM);
    +    /* Instead of makeDelaunay with qh_setdelaunay, you may
    +       produce a 2-d array of points, set DIM to 2, and set
    +       qh->PROJECTdelaunay to True.  qh_init_B will call
    +       qh_projectinput to project the points to the paraboloid
    +       and add a point "at-infinity".
    +    */
    +    qh_init_B(qh, array[0], SIZEcube, DIM, ismalloc);
    +    qh_qhull(qh);
    +    /* If you want Voronoi ('v') without qh_produce_output(), call
    +       qh_setvoronoi_all() after qh_qhull() */
    +    qh_check_output(qh);
    +    print_summary(qh);
    +    qh_produce_output(qh);  /* delete this line to help avoid io.c */
    +    if (qh->VERIFYoutput && !qh->STOPpoint && !qh->STOPcone)
    +      qh_check_points(qh);
    +    printf( "\nadd points to triangulation\n");
    +    addDelaunay(qh, array[0], SIZEcube, SIZEdiamond, DIM);
    +    qh_check_output(qh);
    +    qh_produce_output(qh);  /* delete this line to help avoid io.c */
    +    if (qh->VERIFYoutput && !qh->STOPpoint && !qh->STOPcone)
    +      qh_check_points(qh);
    +    printf( "\nfind Delaunay triangle closest to [0.5, 0.5, ...]\n");
    +    findDelaunay(qh, DIM);
    +  }
    +  qh->NOerrexit= True;
    +  qh_freeqhull(qh, !qh_ALL);
    +  qh_memfreeshort(qh, &curlong, &totlong);
    +  if (curlong || totlong) 
    +      fprintf(stderr, "qhull warning (user_eg2, run 2): did not free %d bytes of long memory (%d pieces)\n",
    +         totlong, curlong);
    +  /*
    +    Run 3: halfspace intersection
    +  */
    +  qh_init_A(qh, stdin, stdout, stderr, 0, NULL);
    +  exitcode= setjmp(qh->errexit);
    +  if (!exitcode) {
    +    coordT array[TOTpoints][DIM+1];  /* +1 for halfspace offset */
    +    pointT *points;
    +
    +    qh->NOerrexit= False;
    +    strcat(qh->rbox_command, "user_eg2 halfspace example");
    +    sprintf(options, "qhull H0 s Tcv %s", argc >= 4 ? argv[3] : "");
    +    qh_initflags(qh, options);
    +    printf( "\ncompute halfspace intersection about the origin for a diamond\n");
    +    makehalf(array[0], SIZEcube, DIM);
    +    qh_setfeasible(qh, DIM); /* from io.c, sets qh->feasible_point from 'Hn,n' */
    +    /* you may malloc and set qh->feasible_point directly.  It is only used for
    +       option 'Fp' */
    +    points= qh_sethalfspace_all(qh, DIM+1, SIZEcube, array[0], qh->feasible_point);
    +    qh_init_B(qh, points, SIZEcube, DIM, True); /* qh_freeqhull frees points */
    +    qh_qhull(qh);
    +    qh_check_output(qh);
    +    qh_produce_output(qh);  /* delete this line to help avoid io.c */
    +    if (qh->VERIFYoutput && !qh->STOPpoint && !qh->STOPcone)
    +      qh_check_points(qh);
    +    printf( "\nadd halfspaces for cube to intersection\n");
    +    addhalf(qh, array[0], SIZEcube, SIZEdiamond, DIM, qh->feasible_point);
    +    qh_check_output(qh);
    +    qh_produce_output(qh);  /* delete this line to help avoid io.c */
    +    if (qh->VERIFYoutput && !qh->STOPpoint && !qh->STOPcone)
    +      qh_check_points(qh);
    +  }
    +  qh->NOerrexit= True;
    +  qh->NOerrexit= True;
    +  qh_freeqhull(qh, !qh_ALL);
    +  qh_memfreeshort(qh, &curlong, &totlong);
    +  if (curlong || totlong)
    +      fprintf(stderr, "qhull warning (user_eg2, run 3): did not free %d bytes of long memory (%d pieces)\n",
    +          totlong, curlong);
    +  return exitcode;
    +} /* main */
    +
    +#if 1    /* use 1 to prevent loading of io.o and user.o */
    +/*-------------------------------------------
    +-errexit- return exitcode to system after an error
    +  assumes exitcode non-zero
    +  prints useful information
    +  see qh_errexit2() in libqhull.c for 2 facets
    +*/
    +void qh_errexit(qhT *qh, int exitcode, facetT *facet, ridgeT *ridge) {
    +  QHULL_UNUSED(facet);
    +  QHULL_UNUSED(ridge);
    +
    +  if (qh->ERREXITcalled) {
    +    fprintf(qh->ferr, "qhull error while processing previous error.  Exit program\n");
    +    exit(1);
    +  }
    +  qh->ERREXITcalled= True;
    +  if (!qh->QHULLfinished)
    +    qh->hulltime= (unsigned)clock() - qh->hulltime;
    +  fprintf(qh->ferr, "\nWhile executing: %s | %s\n", qh->rbox_command, qh->qhull_command);
    +  fprintf(qh->ferr, "Options selected:\n%s\n", qh->qhull_options);
    +  if (qh->furthest_id >= 0) {
    +    fprintf(qh->ferr, "\nLast point added to hull was p%d", qh->furthest_id);
    +    if (zzval_(Ztotmerge))
    +      fprintf(qh->ferr, "  Last merge was #%d.", zzval_(Ztotmerge));
    +    if (qh->QHULLfinished)
    +      fprintf(qh->ferr, "\nQhull has finished constructing the hull.");
    +    else if (qh->POSTmerging)
    +      fprintf(qh->ferr, "\nQhull has started post-merging");
    +    fprintf(qh->ferr, "\n\n");
    +  }
    +  if (qh->NOerrexit) {
    +    fprintf(qh->ferr, "qhull error while ending program.  Exit program\n");
    +    exit(1);
    +  }
    +  if (!exitcode)
    +    exitcode= qh_ERRqhull;
    +  qh->NOerrexit= True;
    +  longjmp(qh->errexit, exitcode);
    +} /* errexit */
    +
    +
    +/*-------------------------------------------
    +-errprint- prints out the information of the erroneous object
    +    any parameter may be NULL, also prints neighbors and geomview output
    +*/
    +void qh_errprint(qhT *qh, const char *string, facetT *atfacet, facetT *otherfacet, ridgeT *atridge, vertexT *atvertex) {
    +
    +  fprintf(qh->ferr, "%s facets f%d f%d ridge r%d vertex v%d\n",
    +           string, getid_(atfacet), getid_(otherfacet), getid_(atridge),
    +           getid_(atvertex));
    +} /* errprint */
    +
    +
    +void qh_printfacetlist(qhT *qh, facetT *facetlist, setT *facets, boolT printall) {
    +  facetT *facet, **facetp;
    +
    +  /* remove these calls to help avoid io.c */
    +  qh_printbegin(qh, qh->ferr, qh_PRINTfacets, facetlist, facets, printall);/*io.c*/
    +  FORALLfacet_(facetlist)                                              /*io.c*/
    +    qh_printafacet(qh, qh->ferr, qh_PRINTfacets, facet, printall);          /*io.c*/
    +  FOREACHfacet_(facets)                                                /*io.c*/
    +    qh_printafacet(qh, qh->ferr, qh_PRINTfacets, facet, printall);          /*io.c*/
    +  qh_printend(qh, qh->ferr, qh_PRINTfacets, facetlist, facets, printall);  /*io.c*/
    +
    +  FORALLfacet_(facetlist)
    +    fprintf( qh->ferr, "facet f%d\n", facet->id);
    +} /* printfacetlist */
    +
    +/* qh_printhelp_degenerate( 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 */
    +
    +
    +/* qh_printhelp_narrowhull( 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\
    +A coplanar point may lead to a wide facet.  Options 'QbB' (scale to unit box)\n\
    +or 'Qbb' (scale last coordinate) may remove this warning.  Use 'Pp' to skip\n\
    +this warning.  See 'Limitations' in qh-impre.htm.\n",
    +          -minangle);   /* convert from angle between normals to angle between facets */
    +} /* printhelp_narrowhull */
    +
    +/* qh_printhelp_singular
    +      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, -1);
    +  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.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\
    +");
    +    if (qh->DELAUNAY && !qh->ATinfinity)
    +      qh_fprintf(qh, fp, 9390, "\n\n\
    +This is a Delaunay triangulation and the input is co-circular or co-spherical:\n\
    +  - use 'Qz' to add a point \"at infinity\" (i.e., above the paraboloid)\n\
    +  - or use 'QJ' to joggle the input and avoid co-circular data\n");
    +  }
    +} /* printhelp_singular */
    +
    +
    +/*-----------------------------------------
    +-user_memsizes- allocate up to 10 additional, quick allocation sizes
    +*/
    +void qh_user_memsizes(qhT *qh) {
    +
    +  QHULL_UNUSED(qh);
    +  /* qh_memsize(qh, size); */
    +} /* user_memsizes */
    +
    +#endif
    diff --git a/xs/src/qhull/src/user_eg3/user_eg3.pro b/xs/src/qhull/src/user_eg3/user_eg3.pro
    new file mode 100644
    index 000000000..35372fbf9
    --- /dev/null
    +++ b/xs/src/qhull/src/user_eg3/user_eg3.pro
    @@ -0,0 +1,12 @@
    +# -------------------------------------------------
    +# user_eg3.pro -- Qt project for cpp demonstration user_eg3.exe
    +#
    +# The C++ interface requires reentrant Qhull.
    +# -------------------------------------------------
    +
    +include(../qhull-app-cpp.pri)
    +
    +TARGET = user_eg3
    +CONFIG -= qt
    +
    +SOURCES += user_eg3_r.cpp
    diff --git a/xs/src/qhull/src/user_eg3/user_eg3_r.cpp b/xs/src/qhull/src/user_eg3/user_eg3_r.cpp
    new file mode 100644
    index 000000000..5257872ab
    --- /dev/null
    +++ b/xs/src/qhull/src/user_eg3/user_eg3_r.cpp
    @@ -0,0 +1,162 @@
    +#//! user_eg3_r.cpp -- Invoke rbox and qhull from C++
    +
    +#include "libqhullcpp/RboxPoints.h"
    +#include "libqhullcpp/QhullError.h"
    +#include "libqhullcpp/QhullQh.h"
    +#include "libqhullcpp/QhullFacet.h"
    +#include "libqhullcpp/QhullFacetList.h"
    +#include "libqhullcpp/QhullLinkedList.h"
    +#include "libqhullcpp/QhullVertex.h"
    +#include "libqhullcpp/Qhull.h"
    +
    +#include    /* for printf() of help message */
    +#include 
    +#include 
    +
    +using std::cerr;
    +using std::cin;
    +using std::cout;
    +using std::endl;
    +
    +using orgQhull::Qhull;
    +using orgQhull::QhullError;
    +using orgQhull::QhullFacet;
    +using orgQhull::QhullFacetList;
    +using orgQhull::QhullQh;
    +using orgQhull::RboxPoints;
    +using orgQhull::QhullVertex;
    +using orgQhull::QhullVertexSet;
    +
    +int main(int argc, char **argv);
    +int user_eg3(int argc, char **argv);
    +
    +char prompt[]= "\n\
    +user_eg3 -- demonstrate calling rbox and qhull from C++.\n\
    +\n\
    +user_eg3 is statically linked to reentrant qhull.  If user_eg3\n\
    +fails immediately, it is probably linked to the non-reentrant qhull.\n\
    +Try 'user_eg3 rbox qhull \"T1\"'\n\
    +\n\
    +  eg-100                       Run the example in qh-code.htm\n\
    +  rbox \"200 D4\" ...            Generate points from rbox\n\
    +  qhull \"d p\" ...              Run qhull and produce output\n\
    +  qhull-cout \"o\" ...           Run qhull and produce output to cout\n\
    +  qhull \"T1\" ...               Run qhull with level-1 trace to cerr\n\
    +  facets                       Print facets when done\n\
    +\n\
    +For example\n\
    +  user_eg3 rbox qhull\n\
    +  user_eg3 rbox qhull d\n\
    +  user_eg3 rbox \"10 D2\"  \"2 D2\" qhull  \"s p\" facets\n\
    +\n\
    +";
    +
    +
    +/*--------------------------------------------
    +-user_eg3-  main procedure of user_eg3 application
    +*/
    +int main(int argc, char **argv) {
    +
    +    QHULL_LIB_CHECK
    +
    +    if(argc==1){
    +        cout << prompt;
    +        return 1;
    +    }
    +    try{
    +        return user_eg3(argc, argv);
    +    }catch(QhullError &e){
    +        cerr << e.what() << std::endl;
    +        return e.errorCode();
    +    }
    +}//main
    +
    +int user_eg3(int argc, char **argv)
    +{
    +    if(strcmp(argv[1], "eg-100")==0){
    +        RboxPoints rbox("100");
    +        Qhull q(rbox, "");
    +        QhullFacetList facets= q.facetList();
    +        cout << facets;
    +        return 0;
    +    }
    +    bool printFacets= false;
    +    RboxPoints rbox;
    +    Qhull qhull;
    +    int readingRbox= 0;
    +    int readingQhull= 0;
    +    for(int i=1; i
    Date: Thu, 9 Aug 2018 16:35:28 +0200
    Subject: [PATCH 02/21] First naive implementation of TriangleMesh convex hull
     calculation
    
    ---
     xs/src/libslic3r/TriangleMesh.cpp | 48 +++++++++++++++++++++++++++++++
     xs/src/libslic3r/TriangleMesh.hpp |  1 +
     2 files changed, 49 insertions(+)
    
    diff --git a/xs/src/libslic3r/TriangleMesh.cpp b/xs/src/libslic3r/TriangleMesh.cpp
    index 45e4b6f5d..197b4d488 100644
    --- a/xs/src/libslic3r/TriangleMesh.cpp
    +++ b/xs/src/libslic3r/TriangleMesh.cpp
    @@ -1,6 +1,9 @@
     #include "TriangleMesh.hpp"
     #include "ClipperUtils.hpp"
     #include "Geometry.hpp"
    +#include "qhull/src/libqhullcpp/Qhull.h"
    +#include "qhull/src/libqhullcpp/QhullFacetList.h"
    +#include "qhull/src/libqhullcpp/QhullVertexSet.h"
     #include 
     #include 
     #include 
    @@ -10,6 +13,7 @@
     #include 
     #include 
     #include 
    +#include 
     
     #include 
     
    @@ -597,6 +601,50 @@ TriangleMesh::bounding_box() const
         return bb;
     }
     
    +
    +TriangleMesh TriangleMesh::convex_hull3d() const
    +{
    +    // qhull's realT is assumed to be a typedef for float - let's better check it first:
    +    static_assert(std::is_same::value, "Internal type realT in the qhull library must be float!");
    +
    +    // Helper struct for qhull:
    +    struct PointForQHull{
    +        PointForQHull(float x_p, float y_p, float z_p) : x(x_p), y(y_p), z(z_p) {}
    +        float x,y,z;
    +        };
    +    std::vector input_verts;
    +
    +    // We will now fill the vector with input points for computation:
    +    stl_facet* facet_ptr = stl.facet_start;
    +    while (facet_ptr < stl.facet_start+stl.stats.number_of_facets) {
    +        for (int j=0;j<3;++j)
    +            input_verts.emplace_back(PointForQHull(facet_ptr->vertex[j].x, facet_ptr->vertex[j].y, facet_ptr->vertex[j].z));
    +        facet_ptr+=1;
    +    }
    +
    +    // The qhull call:
    +    orgQhull::Qhull qhull;
    +    qhull.disableOutputStream(); // we want qhull to be quiet
    +    qhull.runQhull("", 3, input_verts.size(), (const realT*)(input_verts.data()), "Qt" );
    +
    +    // Let's collect results:
    +    Pointf3s vertices;
    +    std::vector facets;
    +    auto facet_list = qhull.facetList().toStdVector();
    +    for (const orgQhull::QhullFacet& facet : facet_list) {   // iterate through facets
    +        for (unsigned char i=0; i<3; ++i) {        // iterate through facet's vertices
    +            orgQhull::QhullPoint p = (facet.vertices())[i].point();
    +            const float* coords = p.coordinates();
    +            Pointf3 vert((float)coords[0], (float)coords[1], (float)coords[2]);
    +            vertices.emplace_back(vert);
    +        }
    +        facets.emplace_back(Point3(vertices.size()-3, vertices.size()-2, vertices.size()-1));
    +    }
    +    TriangleMesh output_mesh(vertices, facets);
    +    output_mesh.repair();
    +    return output_mesh;
    +}
    +
     void
     TriangleMesh::require_shared_vertices()
     {
    diff --git a/xs/src/libslic3r/TriangleMesh.hpp b/xs/src/libslic3r/TriangleMesh.hpp
    index c700784a5..14fdba6c3 100644
    --- a/xs/src/libslic3r/TriangleMesh.hpp
    +++ b/xs/src/libslic3r/TriangleMesh.hpp
    @@ -55,6 +55,7 @@ public:
         ExPolygons horizontal_projection() const;
         Polygon convex_hull();
         BoundingBoxf3 bounding_box() const;
    +    TriangleMesh convex_hull3d() const;
         void reset_repair_stats();
         bool needed_repair() const;
         size_t facets_count() const;
    
    From 25a6c7e30e83e6a691d580fdf3645635ed459a06 Mon Sep 17 00:00:00 2001
    From: Lukas Matena 
    Date: Thu, 9 Aug 2018 16:55:43 +0200
    Subject: [PATCH 03/21] Created a new gizmo for flattening an object
    
    ---
     lib/Slic3r/GUI/Plater.pm         |   3 +-
     xs/src/slic3r/GUI/GLCanvas3D.cpp |  66 ++++++++++++++++++-
     xs/src/slic3r/GUI/GLCanvas3D.hpp |   4 ++
     xs/src/slic3r/GUI/GLGizmo.cpp    | 109 ++++++++++++++++++++++++++++++-
     xs/src/slic3r/GUI/GLGizmo.hpp    |  32 +++++++++
     5 files changed, 211 insertions(+), 3 deletions(-)
    
    diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm
    index a0eef72fe..e9cdf1527 100644
    --- a/lib/Slic3r/GUI/Plater.pm
    +++ b/lib/Slic3r/GUI/Plater.pm
    @@ -141,8 +141,9 @@ sub new {
         
         # callback to react to gizmo rotate
         my $on_gizmo_rotate = sub {
    -        my ($angle_z) = @_;
    +        my ($angle_z, $angle_y) = @_;
             $self->rotate(rad2deg($angle_z), Z, 'absolute');
    +        $self->rotate(rad2deg($angle_y), Y, 'absolute');
         };
     
         # callback to update object's geometry info while using gizmos
    diff --git a/xs/src/slic3r/GUI/GLCanvas3D.cpp b/xs/src/slic3r/GUI/GLCanvas3D.cpp
    index ab4095e6f..f1bbba934 100644
    --- a/xs/src/slic3r/GUI/GLCanvas3D.cpp
    +++ b/xs/src/slic3r/GUI/GLCanvas3D.cpp
    @@ -1,5 +1,6 @@
     #include "GLCanvas3D.hpp"
     
    +#include "../../admesh/stl.h"
     #include "../../libslic3r/libslic3r.h"
     #include "../../slic3r/GUI/3DScene.hpp"
     #include "../../slic3r/GUI/GLShader.hpp"
    @@ -1154,6 +1155,18 @@ bool GLCanvas3D::Gizmos::init()
     
         m_gizmos.insert(GizmosMap::value_type(Rotate, gizmo));
     
    +    gizmo = new GLGizmoFlatten;
    +    if (gizmo == nullptr)
    +        return false;
    +
    +    if (!gizmo->init()) {
    +        _reset();
    +        return false;
    +    }
    +
    +    m_gizmos.insert(GizmosMap::value_type(Flatten, gizmo));
    +
    +
         return true;
     }
     
    @@ -1387,6 +1400,25 @@ void GLCanvas3D::Gizmos::set_angle_z(float angle_z)
             reinterpret_cast(it->second)->set_angle_z(angle_z);
     }
     
    +Pointf3 GLCanvas3D::Gizmos::get_flattening_normal() const
    +{
    +    if (!m_enabled)
    +        return Pointf3(0.f, 0.f, 0.f);
    +
    +    GizmosMap::const_iterator it = m_gizmos.find(Flatten);
    +    return (it != m_gizmos.end()) ? reinterpret_cast(it->second)->get_flattening_normal() : Pointf3(0.f, 0.f, 0.f);
    +}
    +
    +void GLCanvas3D::Gizmos::set_flattening_data(std::vector vertices_list)
    +{
    +    if (!m_enabled)
    +        return;
    +
    +    GizmosMap::const_iterator it = m_gizmos.find(Flatten);
    +    if (it != m_gizmos.end())
    +        reinterpret_cast(it->second)->set_flattening_data(vertices_list);
    +}
    +
     void GLCanvas3D::Gizmos::render(const GLCanvas3D& canvas, const BoundingBoxf3& box) const
     {
         if (!m_enabled)
    @@ -2170,6 +2202,27 @@ void GLCanvas3D::update_gizmos_data()
                 {
                     m_gizmos.set_scale(model_instance->scaling_factor);
                     m_gizmos.set_angle_z(model_instance->rotation);
    +
    +                /////////////////////////////////////////////////////////////////////////
    +                // Following block provides convex hull data to the Flatten gizmo
    +                // It is temporary, it should be optimized and moved elsewhere later
    +                TriangleMesh ch = model_object->mesh().convex_hull3d();
    +                stl_facet* facet_ptr = ch.stl.facet_start;
    +                std::vector points;
    +                while (facet_ptr < ch.stl.facet_start+ch.stl.stats.number_of_facets) {
    +                    Pointf3 a = Pointf3(facet_ptr->vertex[1].x - facet_ptr->vertex[0].x, facet_ptr->vertex[1].y - facet_ptr->vertex[0].y, facet_ptr->vertex[1].z - facet_ptr->vertex[0].z);
    +                    Pointf3 b = Pointf3(facet_ptr->vertex[2].x - facet_ptr->vertex[0].x, facet_ptr->vertex[2].y - facet_ptr->vertex[0].y, facet_ptr->vertex[2].z - facet_ptr->vertex[0].z);
    +
    +                    if (0.5 * sqrt(dot(cross(a, b), cross(a,b))) > 50.f) {
    +                        points.emplace_back(Pointf3s());
    +                        for (unsigned int j=0; j<3; ++j)
    +                            points.back().emplace_back(Pointf3(facet_ptr->vertex[j].x, facet_ptr->vertex[j].y, facet_ptr->vertex[j].z));
    +                        points.back().emplace_back(Pointf3(facet_ptr->normal.x, facet_ptr->normal.y, facet_ptr->normal.z));
    +                    }
    +                    facet_ptr+=1;
    +                }
    +                m_gizmos.set_flattening_data(points);
    +                ////////////////////////////////////////////////////////////////////////
                 }
             }
         }
    @@ -2177,6 +2230,7 @@ void GLCanvas3D::update_gizmos_data()
         {
             m_gizmos.set_scale(1.0f);
             m_gizmos.set_angle_z(0.0f);
    +        m_gizmos.set_flattening_data(std::vector());
         }
     }
     
    @@ -2760,6 +2814,16 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
                 m_gizmos.start_dragging();
                 m_mouse.drag.gizmo_volume_idx = _get_first_selected_volume_id(selected_object_idx);
                 m_dirty = true;
    +
    +            if (m_gizmos.get_current_type() == Gizmos::Flatten) {
    +                // Rotate the object so the normal points downward:
    +                Pointf3 normal = m_gizmos.get_flattening_normal();
    +                if (normal.x != 0.f || normal.y != 0.f || normal.z != 0.f) {
    +                    float angle_z = -atan2(normal.y, normal.x);
    +                    float angle_y = M_PI - atan2(normal.x*cos(angle_z)-normal.y*sin(angle_z), normal.z);
    +                    m_on_gizmo_rotate_callback.call((double)angle_z, (double)angle_y);
    +                }
    +            }
             }
             else
             {
    @@ -3039,7 +3103,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
                 }
                 case Gizmos::Rotate:
                 {
    -                m_on_gizmo_rotate_callback.call((double)m_gizmos.get_angle_z());
    +                m_on_gizmo_rotate_callback.call((double)m_gizmos.get_angle_z(), 0.);
                     break;
                 }
                 default:
    diff --git a/xs/src/slic3r/GUI/GLCanvas3D.hpp b/xs/src/slic3r/GUI/GLCanvas3D.hpp
    index ae20882fc..09a7de823 100644
    --- a/xs/src/slic3r/GUI/GLCanvas3D.hpp
    +++ b/xs/src/slic3r/GUI/GLCanvas3D.hpp
    @@ -338,6 +338,7 @@ public:
                 Undefined,
                 Scale,
                 Rotate,
    +            Flatten,
                 Num_Types
             };
     
    @@ -382,6 +383,9 @@ public:
             float get_angle_z() const;
             void set_angle_z(float angle_z);
     
    +        void set_flattening_data(std::vector vertices_list);
    +        Pointf3 get_flattening_normal() const;
    +
             void render(const GLCanvas3D& canvas, const BoundingBoxf3& box) const;
             void render_current_gizmo_for_picking_pass(const BoundingBoxf3& box) const;
     
    diff --git a/xs/src/slic3r/GUI/GLGizmo.cpp b/xs/src/slic3r/GUI/GLGizmo.cpp
    index 47b01e8a2..5a0944758 100644
    --- a/xs/src/slic3r/GUI/GLGizmo.cpp
    +++ b/xs/src/slic3r/GUI/GLGizmo.cpp
    @@ -110,7 +110,7 @@ int GLGizmoBase::get_hover_id() const
     
     void GLGizmoBase::set_hover_id(int id)
     {
    -    if (id < (int)m_grabbers.size())
    +    //if (id < (int)m_grabbers.size())
             m_hover_id = id;
     }
     
    @@ -502,5 +502,112 @@ void GLGizmoScale::on_render_for_picking(const BoundingBoxf3& box) const
         render_grabbers();
     }
     
    +
    +GLGizmoFlatten::GLGizmoFlatten()
    +    : GLGizmoBase(),
    +      m_normal(Pointf3(0.f, 0.f, 0.f))
    +{}
    +
    +
    +bool GLGizmoFlatten::on_init()
    +{
    +    std::string path = resources_dir() + "/icons/overlay/";
    +
    +    std::string filename = path + "scale_off.png";
    +    if (!m_textures[Off].load_from_file(filename, false))
    +        return false;
    +
    +    filename = path + "scale_hover.png";
    +    if (!m_textures[Hover].load_from_file(filename, false))
    +        return false;
    +
    +    filename = path + "scale_on.png";
    +    if (!m_textures[On].load_from_file(filename, false))
    +        return false;
    +
    +    return true;
    +}
    +
    +void GLGizmoFlatten::on_start_dragging()
    +{
    +    if (m_hover_id != -1)
    +        m_normal = m_planes[m_hover_id].normal;
    +}
    +
    +void GLGizmoFlatten::on_update(const Pointf& mouse_pos)
    +{
    +    /*Pointf center(0.5 * (m_grabbers[1].center.x + m_grabbers[0].center.x), 0.5 * (m_grabbers[3].center.y + m_grabbers[0].center.y));
    +
    +    coordf_t orig_len = length(m_starting_drag_position - center);
    +    coordf_t new_len = length(mouse_pos - center);
    +    coordf_t ratio = (orig_len != 0.0) ? new_len / orig_len : 1.0;
    +
    +    m_scale = m_starting_scale * (float)ratio;*/
    +}
    +
    +void GLGizmoFlatten::on_render(const BoundingBoxf3& box) const
    +{
    +    bool blending_was_enabled = ::glIsEnabled(GL_BLEND);
    +    ::glEnable(GL_BLEND);
    +    ::glDisable(GL_DEPTH_TEST);
    +
    +    for (int i=0; i<(int)m_planes.size(); ++i) {
    +        if (i == m_hover_id)
    +            ::glColor4f(0.9f, 0.9f, 0.9f, 0.75f);
    +            else
    +                ::glColor4f(0.9f, 0.9f, 0.9f, 0.5f);
    +
    +        ::glBegin(GL_POLYGON);
    +        for (const auto& vertex : m_planes[i].vertices)
    +            ::glVertex3f((GLfloat)vertex.x, (GLfloat)vertex.y, (GLfloat)vertex.z);
    +        ::glEnd();
    +    }
    +
    +    if (!blending_was_enabled)
    +        ::glDisable(GL_BLEND);
    +}
    +
    +void GLGizmoFlatten::on_render_for_picking(const BoundingBoxf3& box) const
    +{
    +    static const GLfloat INV_255 = 1.0f / 255.0f;
    +
    +    ::glDisable(GL_DEPTH_TEST);
    +
    +    for (unsigned int i = 0; i < m_planes.size(); ++i)
    +    {
    +        ::glColor3f(1.f, 1.f, (254.0f - (float)i) * INV_255);
    +        ::glBegin(GL_POLYGON);
    +        for (const auto& vertex : m_planes[i].vertices)
    +            ::glVertex3f((GLfloat)vertex.x, (GLfloat)vertex.y, (GLfloat)vertex.z);
    +        ::glEnd();
    +    }
    +}
    +
    +void GLGizmoFlatten::set_flattening_data(std::vector vertices_list)
    +{
    +    // Each entry in vertices_list describe one polygon that can be laid flat.
    +    // All points but the last one are vertices of the polygon, the last "point" is the outer normal vector.
    +
    +    m_planes.clear();
    +    m_planes.reserve(vertices_list.size());
    +
    +    for (const auto& plane_data : vertices_list) {
    +        m_planes.emplace_back(PlaneData());
    +        for (unsigned int i=0; i vertices;
    +        Pointf3 normal;
    +        float color[3];
    +    };
    +
    +    std::vector m_planes;
    +
    +public:
    +    GLGizmoFlatten();
    +
    +    void set_flattening_data(std::vector vertices_list);
    +    Pointf3 get_flattening_normal() const;
    +
    +protected:
    +    virtual bool on_init();
    +    virtual void on_start_dragging();
    +    virtual void on_update(const Pointf& mouse_pos);
    +    virtual void on_render(const BoundingBoxf3& box) const;
    +    virtual void on_render_for_picking(const BoundingBoxf3& box) const;
    +};
    +
    +
    +
     } // namespace GUI
     } // namespace Slic3r
     
    
    From 09ce6c62ea94c5f3d88eb2a7a0aa9de91c8a728e Mon Sep 17 00:00:00 2001
    From: Lukas Matena 
    Date: Mon, 13 Aug 2018 14:51:03 +0200
    Subject: [PATCH 04/21] Retraction after ramming is now done without moving the
     head
    
    ---
     xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp | 9 +++++++--
     1 file changed, 7 insertions(+), 2 deletions(-)
    
    diff --git a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
    index 3d0dba07a..4009ebf3a 100644
    --- a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
    +++ b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
    @@ -793,11 +793,16 @@ void WipeTowerPrusaMM::toolchange_Unload(
         float turning_point = (!m_left_to_right ? xl : xr );
         float total_retraction_distance = m_cooling_tube_retraction + m_cooling_tube_length/2.f - 15.f; // the 15mm is reserved for the first part after ramming
         writer.suppress_preview()
    -          .load_move_x_advanced(turning_point, -15.f, 83.f, 50.f) // this is done at fixed speed
    +          .retract(15.f, 5000.f) // feedrate 5000mm/min = 83mm/s
    +          .retract(0.70f * total_retraction_distance, 1.0f * m_filpar[m_current_tool].unloading_speed * 60.f)
    +          .retract(0.20f * total_retraction_distance, 0.5f * m_filpar[m_current_tool].unloading_speed * 60.f)
    +          .retract(0.10f * total_retraction_distance, 0.3f * m_filpar[m_current_tool].unloading_speed * 60.f)
    +          
    +          /*.load_move_x_advanced(turning_point, -15.f, 83.f, 50.f) // this is done at fixed speed
               .load_move_x_advanced(old_x,         -0.70f * total_retraction_distance, 1.0f * m_filpar[m_current_tool].unloading_speed)
               .load_move_x_advanced(turning_point, -0.20f * total_retraction_distance, 0.5f * m_filpar[m_current_tool].unloading_speed)
               .load_move_x_advanced(old_x,         -0.10f * total_retraction_distance, 0.3f * m_filpar[m_current_tool].unloading_speed)
    -          .travel(old_x, writer.y()) // in case previous move was shortened to limit feedrate
    +          .travel(old_x, writer.y()) // in case previous move was shortened to limit feedrate*/
               .resume_preview();
     
         if (new_temperature != 0 && new_temperature != m_old_temperature ) { 	// Set the extruder temperature, but don't wait.
    
    From 93ce0d23b75881e8fd7cf71df4ff3a885d54b6af Mon Sep 17 00:00:00 2001
    From: Lukas Matena 
    Date: Tue, 14 Aug 2018 13:08:49 +0200
    Subject: [PATCH 05/21] Simple attempt to smooth the lay flat triangles
    
    ---
     xs/src/slic3r/GUI/GLCanvas3D.cpp | 41 +++++++++++++++++++++++++++++---
     1 file changed, 38 insertions(+), 3 deletions(-)
    
    diff --git a/xs/src/slic3r/GUI/GLCanvas3D.cpp b/xs/src/slic3r/GUI/GLCanvas3D.cpp
    index f1bbba934..45260544d 100644
    --- a/xs/src/slic3r/GUI/GLCanvas3D.cpp
    +++ b/xs/src/slic3r/GUI/GLCanvas3D.cpp
    @@ -2209,14 +2209,49 @@ void GLCanvas3D::update_gizmos_data()
                     TriangleMesh ch = model_object->mesh().convex_hull3d();
                     stl_facet* facet_ptr = ch.stl.facet_start;
                     std::vector points;
    +                const unsigned int k = 20;
    +                const float ratio = 0.2f;
    +                const unsigned int N = 3; // 3 - triangle
    +
                     while (facet_ptr < ch.stl.facet_start+ch.stl.stats.number_of_facets) {
                         Pointf3 a = Pointf3(facet_ptr->vertex[1].x - facet_ptr->vertex[0].x, facet_ptr->vertex[1].y - facet_ptr->vertex[0].y, facet_ptr->vertex[1].z - facet_ptr->vertex[0].z);
                         Pointf3 b = Pointf3(facet_ptr->vertex[2].x - facet_ptr->vertex[0].x, facet_ptr->vertex[2].y - facet_ptr->vertex[0].y, facet_ptr->vertex[2].z - facet_ptr->vertex[0].z);
     
    +
                         if (0.5 * sqrt(dot(cross(a, b), cross(a,b))) > 50.f) {
    -                        points.emplace_back(Pointf3s());
    -                        for (unsigned int j=0; j<3; ++j)
    -                            points.back().emplace_back(Pointf3(facet_ptr->vertex[j].x, facet_ptr->vertex[j].y, facet_ptr->vertex[j].z));
    +                        points.emplace_back(Pointf3s(2*k*N));
    +
    +                        std::vector> neighbours;
    +                        if (k != 0) {
    +                            for (unsigned int j=0; jvertex[j].x, facet_ptr->vertex[j].y, facet_ptr->vertex[j].z);
    +                                neighbours.push_back(std::make_pair((int)(j*2*k-k) < 0 ? (N-1)*2*k+k : j*2*k-k, j*2*k+k));
    +                            }
    +
    +                            for (unsigned int i=0; ivertex[j].x, facet_ptr->vertex[j].y, facet_ptr->vertex[j].z));
    +
                             points.back().emplace_back(Pointf3(facet_ptr->normal.x, facet_ptr->normal.y, facet_ptr->normal.z));
                         }
                         facet_ptr+=1;
    
    From 74e807f89bb03472924563212e7b11878424f077 Mon Sep 17 00:00:00 2001
    From: Lukas Matena 
    Date: Tue, 14 Aug 2018 16:23:23 +0200
    Subject: [PATCH 06/21] New experimental parameter to adjust initial loading
     speed of the filament from the nozzle
    
    ---
     xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp | 2 +-
     xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp | 6 ++++--
     xs/src/libslic3r/Print.cpp                  | 2 ++
     xs/src/libslic3r/PrintConfig.cpp            | 8 ++++++++
     xs/src/libslic3r/PrintConfig.hpp            | 2 ++
     xs/src/slic3r/GUI/Preset.cpp                | 4 ++--
     xs/src/slic3r/GUI/Tab.cpp                   | 1 +
     7 files changed, 20 insertions(+), 5 deletions(-)
    
    diff --git a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
    index 4009ebf3a..de1f9a59b 100644
    --- a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
    +++ b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
    @@ -793,7 +793,7 @@ void WipeTowerPrusaMM::toolchange_Unload(
         float turning_point = (!m_left_to_right ? xl : xr );
         float total_retraction_distance = m_cooling_tube_retraction + m_cooling_tube_length/2.f - 15.f; // the 15mm is reserved for the first part after ramming
         writer.suppress_preview()
    -          .retract(15.f, 5000.f) // feedrate 5000mm/min = 83mm/s
    +          .retract(15.f, m_filpar[m_current_tool].unloading_speed_start * 60.f) // feedrate 5000mm/min = 83mm/s
               .retract(0.70f * total_retraction_distance, 1.0f * m_filpar[m_current_tool].unloading_speed * 60.f)
               .retract(0.20f * total_retraction_distance, 0.5f * m_filpar[m_current_tool].unloading_speed * 60.f)
               .retract(0.10f * total_retraction_distance, 0.3f * m_filpar[m_current_tool].unloading_speed * 60.f)
    diff --git a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp
    index e1529bcf4..4b96ce17c 100644
    --- a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp
    +++ b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp
    @@ -66,8 +66,8 @@ public:
     
     	// Set the extruder properties.
     	void set_extruder(size_t idx, material_type material, int temp, int first_layer_temp, float loading_speed,
    -                      float unloading_speed, float delay, int cooling_moves, float cooling_initial_speed,
    -                      float cooling_final_speed, std::string ramming_parameters, float nozzle_diameter)
    +                      float unloading_speed, float unloading_speed_start, float delay, int cooling_moves,
    +                      float cooling_initial_speed, float cooling_final_speed, std::string ramming_parameters, float nozzle_diameter)
     	{
             //while (m_filpar.size() < idx+1)   // makes sure the required element is in the vector
             m_filpar.push_back(FilamentParameters());
    @@ -77,6 +77,7 @@ public:
             m_filpar[idx].first_layer_temperature = first_layer_temp;
             m_filpar[idx].loading_speed = loading_speed;
             m_filpar[idx].unloading_speed = unloading_speed;
    +        m_filpar[idx].unloading_speed_start = unloading_speed_start;
             m_filpar[idx].delay = delay;
             m_filpar[idx].cooling_moves = cooling_moves;
             m_filpar[idx].cooling_initial_speed = cooling_initial_speed;
    @@ -217,6 +218,7 @@ private:
             int  			    first_layer_temperature = 0;
             float               loading_speed = 0.f;
             float               unloading_speed = 0.f;
    +        float               unloading_speed_start = 0.f;
             float               delay = 0.f ;
             int                 cooling_moves = 0;
             float               cooling_initial_speed = 0.f;
    diff --git a/xs/src/libslic3r/Print.cpp b/xs/src/libslic3r/Print.cpp
    index 7d2906bcc..4154378ec 100644
    --- a/xs/src/libslic3r/Print.cpp
    +++ b/xs/src/libslic3r/Print.cpp
    @@ -201,6 +201,7 @@ bool Print::invalidate_state_by_config_options(const std::vectorconfig.first_layer_temperature.get_at(i),
                 this->config.filament_loading_speed.get_at(i),
                 this->config.filament_unloading_speed.get_at(i),
    +            this->config.filament_unloading_speed_start.get_at(i),
                 this->config.filament_toolchange_delay.get_at(i),
                 this->config.filament_cooling_moves.get_at(i),
                 this->config.filament_cooling_initial_speed.get_at(i),
    diff --git a/xs/src/libslic3r/PrintConfig.cpp b/xs/src/libslic3r/PrintConfig.cpp
    index d8f2d85a6..cb09a9d09 100644
    --- a/xs/src/libslic3r/PrintConfig.cpp
    +++ b/xs/src/libslic3r/PrintConfig.cpp
    @@ -482,6 +482,14 @@ PrintConfigDef::PrintConfigDef()
         def->min = 0;
         def->default_value = new ConfigOptionFloats { 90. };
     
    +    def = this->add("filament_unloading_speed_start", coFloats);
    +    def->label = L("EXPERIMENTAL: Unloading speed at the start");
    +    def->tooltip = L("Speed used for unloading the tip of the filament immediately after ramming. ");
    +    def->sidetext = L("mm/s");
    +    def->cli = "filament-unloading-speed-start=f@";
    +    def->min = 0;
    +    def->default_value = new ConfigOptionFloats { 83. };
    +
         def = this->add("filament_toolchange_delay", coFloats);
         def->label = L("Delay after unloading");
         def->tooltip = L("Time to wait after the filament is unloaded. "
    diff --git a/xs/src/libslic3r/PrintConfig.hpp b/xs/src/libslic3r/PrintConfig.hpp
    index b18603d87..edf7756e8 100644
    --- a/xs/src/libslic3r/PrintConfig.hpp
    +++ b/xs/src/libslic3r/PrintConfig.hpp
    @@ -530,6 +530,7 @@ public:
         ConfigOptionFloats              filament_loading_speed;
         ConfigOptionFloats              filament_load_time;
         ConfigOptionFloats              filament_unloading_speed;
    +    ConfigOptionFloats              filament_unloading_speed_start;
         ConfigOptionFloats              filament_toolchange_delay;
         ConfigOptionFloats              filament_unload_time;
         ConfigOptionInts                filament_cooling_moves;
    @@ -596,6 +597,7 @@ protected:
             OPT_PTR(filament_loading_speed);
             OPT_PTR(filament_load_time);
             OPT_PTR(filament_unloading_speed);
    +        OPT_PTR(filament_unloading_speed_start);
             OPT_PTR(filament_unload_time);
             OPT_PTR(filament_toolchange_delay);
             OPT_PTR(filament_cooling_moves);
    diff --git a/xs/src/slic3r/GUI/Preset.cpp b/xs/src/slic3r/GUI/Preset.cpp
    index 9f51f7b97..ba27bea8a 100644
    --- a/xs/src/slic3r/GUI/Preset.cpp
    +++ b/xs/src/slic3r/GUI/Preset.cpp
    @@ -314,8 +314,8 @@ const std::vector& Preset::filament_options()
         static std::vector s_opts {
             "filament_colour", "filament_diameter", "filament_type", "filament_soluble", "filament_notes", "filament_max_volumetric_speed",
             "extrusion_multiplier", "filament_density", "filament_cost", 
    -        "filament_loading_speed", "filament_load_time", "filament_unloading_speed", "filament_unload_time", "filament_toolchange_delay",
    -        "filament_cooling_moves", "filament_cooling_initial_speed", "filament_cooling_final_speed", "filament_ramming_parameters",
    +        "filament_loading_speed", "filament_load_time", "filament_unloading_speed", "filament_unloading_speed_start", "filament_unload_time",
    +        "filament_toolchange_delay", "filament_cooling_moves", "filament_cooling_initial_speed", "filament_cooling_final_speed", "filament_ramming_parameters",
             "filament_minimal_purge_on_wipe_tower", "temperature", "first_layer_temperature", "bed_temperature", "first_layer_bed_temperature",
             "fan_always_on", "cooling", "min_fan_speed", "max_fan_speed", "bridge_fan_speed", "disable_fan_first_layers", "fan_below_layer_time",
             "slowdown_below_layer_time", "min_print_speed", "start_filament_gcode", "end_filament_gcode","compatible_printers", "compatible_printers_condition",
    diff --git a/xs/src/slic3r/GUI/Tab.cpp b/xs/src/slic3r/GUI/Tab.cpp
    index 7c4322c5a..d3307f1f5 100644
    --- a/xs/src/slic3r/GUI/Tab.cpp
    +++ b/xs/src/slic3r/GUI/Tab.cpp
    @@ -1292,6 +1292,7 @@ void TabFilament::build()
             optgroup = page->new_optgroup(_(L("Toolchange parameters with single extruder MM printers")));
     		optgroup->append_single_option_line("filament_loading_speed");
             optgroup->append_single_option_line("filament_unloading_speed");
    +        optgroup->append_single_option_line("filament_unloading_speed_start");
     		optgroup->append_single_option_line("filament_load_time");
     		optgroup->append_single_option_line("filament_unload_time");
             optgroup->append_single_option_line("filament_toolchange_delay");
    
    From 48b9793d3d2f5a9b4163f4927563ffd4266e4f12 Mon Sep 17 00:00:00 2001
    From: Lukas Matena 
    Date: Fri, 17 Aug 2018 15:20:35 +0200
    Subject: [PATCH 07/21] Templated convex_hull function in Geometry.cpp
    
    ---
     xs/src/libslic3r/Geometry.cpp | 53 ++++++++++++++++++++++-------------
     xs/src/libslic3r/Geometry.hpp |  2 ++
     xs/src/libslic3r/Point.cpp    |  6 ++++
     xs/src/libslic3r/Point.hpp    |  1 +
     4 files changed, 43 insertions(+), 19 deletions(-)
    
    diff --git a/xs/src/libslic3r/Geometry.cpp b/xs/src/libslic3r/Geometry.cpp
    index 39b626ee3..aaf0352c9 100644
    --- a/xs/src/libslic3r/Geometry.cpp
    +++ b/xs/src/libslic3r/Geometry.cpp
    @@ -195,47 +195,62 @@ using namespace boost::polygon;  // provides also high() and low()
     
     namespace Slic3r { namespace Geometry {
     
    -static bool
    -sort_points (Point a, Point b)
    -{
    -    return (a.x < b.x) || (a.x == b.x && a.y < b.y);
    -}
    +struct SortPoints {
    +    template 
    +    bool operator()(const T& a, const T& b) const {
    +        return (b.x > a.x) || (a.x == b.x && b.y > a.y);
    +    }
    +};
     
    -/* This implementation is based on Andrew's monotone chain 2D convex hull algorithm */
    -Polygon
    -convex_hull(Points points)
    +// This implementation is based on Andrew's monotone chain 2D convex hull algorithm
    +template
    +static T raw_convex_hull(T& points)
     {
         assert(points.size() >= 3);
         // sort input points
    -    std::sort(points.begin(), points.end(), sort_points);
    +    std::sort(points.begin(), points.end(), SortPoints());
         
         int n = points.size(), k = 0;
    -    Polygon hull;
    +    T hull;
     
         if (n >= 3) {
    -        hull.points.resize(2*n);
    +        hull.resize(2*n);
     
             // Build lower hull
             for (int i = 0; i < n; i++) {
    -            while (k >= 2 && points[i].ccw(hull.points[k-2], hull.points[k-1]) <= 0) k--;
    -            hull.points[k++] = points[i];
    +            while (k >= 2 && points[i].ccw(hull[k-2], hull[k-1]) <= 0) k--;
    +            hull[k++] = points[i];
             }
     
             // Build upper hull
             for (int i = n-2, t = k+1; i >= 0; i--) {
    -            while (k >= t && points[i].ccw(hull.points[k-2], hull.points[k-1]) <= 0) k--;
    -            hull.points[k++] = points[i];
    +            while (k >= t && points[i].ccw(hull[k-2], hull[k-1]) <= 0) k--;
    +            hull[k++] = points[i];
             }
     
    -        hull.points.resize(k);
    +        hull.resize(k);
             
    -        assert( hull.points.front().coincides_with(hull.points.back()) );
    -        hull.points.pop_back();
    +        assert( hull.front().coincides_with(hull.back()) );
    +        hull.pop_back();
         }
         
         return hull;
     }
     
    +Pointf3s
    +convex_hull(Pointf3s points)
    +{
    +    return raw_convex_hull(points);
    +}
    +
    +Polygon
    +convex_hull(Points points)
    +{
    +    Polygon hull;
    +    hull.points = raw_convex_hull(points);
    +    return hull;
    +}
    +
     Polygon
     convex_hull(const Polygons &polygons)
     {
    @@ -243,7 +258,7 @@ convex_hull(const Polygons &polygons)
         for (Polygons::const_iterator p = polygons.begin(); p != polygons.end(); ++p) {
             pp.insert(pp.end(), p->points.begin(), p->points.end());
         }
    -    return convex_hull(pp);
    +    return convex_hull(std::move(pp));
     }
     
     /* accepts an arrayref of points and returns a list of indices
    diff --git a/xs/src/libslic3r/Geometry.hpp b/xs/src/libslic3r/Geometry.hpp
    index c2c3dc8b7..956ef82aa 100644
    --- a/xs/src/libslic3r/Geometry.hpp
    +++ b/xs/src/libslic3r/Geometry.hpp
    @@ -108,8 +108,10 @@ inline bool segment_segment_intersection(const Pointf &p1, const Vectorf &v1, co
         return true;
     }
     
    +Pointf3s convex_hull(Pointf3s points);
     Polygon convex_hull(Points points);
     Polygon convex_hull(const Polygons &polygons);
    +
     void chained_path(const Points &points, std::vector &retval, Point start_near);
     void chained_path(const Points &points, std::vector &retval);
     template void chained_path_items(Points &points, T &items, T &retval);
    diff --git a/xs/src/libslic3r/Point.cpp b/xs/src/libslic3r/Point.cpp
    index 2abcd26af..349b00bb6 100644
    --- a/xs/src/libslic3r/Point.cpp
    +++ b/xs/src/libslic3r/Point.cpp
    @@ -263,6 +263,12 @@ operator<<(std::ostream &stm, const Pointf &pointf)
         return stm << pointf.x << "," << pointf.y;
     }
     
    +double
    +Pointf::ccw(const Pointf &p1, const Pointf &p2) const
    +{
    +    return (double)(p2.x - p1.x)*(double)(this->y - p1.y) - (double)(p2.y - p1.y)*(double)(this->x - p1.x);
    +}
    +
     std::string
     Pointf::wkt() const
     {
    diff --git a/xs/src/libslic3r/Point.hpp b/xs/src/libslic3r/Point.hpp
    index 87104674f..347966c03 100644
    --- a/xs/src/libslic3r/Point.hpp
    +++ b/xs/src/libslic3r/Point.hpp
    @@ -221,6 +221,7 @@ public:
         static Pointf new_unscale(const Point &p) {
             return Pointf(unscale(p.x), unscale(p.y));
         };
    +    double ccw(const Pointf &p1, const Pointf &p2) const;
         std::string wkt() const;
         std::string dump_perl() const;
         void scale(double factor);
    
    From f9efcc36b64d631bfece0a207fbb74388c3bb514 Mon Sep 17 00:00:00 2001
    From: Lukas Matena 
    Date: Fri, 17 Aug 2018 15:40:47 +0200
    Subject: [PATCH 08/21] Lay flat gizmo improvements - merge adjacent faces,
     compute and cache convex hull for entire ModelObject, refresh when moved,
     etc.
    
    ---
     xs/src/libslic3r/TriangleMesh.cpp |   1 +
     xs/src/slic3r/GUI/GLCanvas3D.cpp  |  63 +-------
     xs/src/slic3r/GUI/GLCanvas3D.hpp  |   2 +-
     xs/src/slic3r/GUI/GLGizmo.cpp     | 257 ++++++++++++++++++++++++++----
     xs/src/slic3r/GUI/GLGizmo.hpp     |  31 +++-
     5 files changed, 256 insertions(+), 98 deletions(-)
    
    diff --git a/xs/src/libslic3r/TriangleMesh.cpp b/xs/src/libslic3r/TriangleMesh.cpp
    index 957515db3..19dcc6a07 100644
    --- a/xs/src/libslic3r/TriangleMesh.cpp
    +++ b/xs/src/libslic3r/TriangleMesh.cpp
    @@ -728,6 +728,7 @@ TriangleMesh TriangleMesh::convex_hull_3d() const
     
         TriangleMesh output_mesh(det_vertices, facets);
         output_mesh.repair();
    +    output_mesh.require_shared_vertices();
         return output_mesh;
     }
     
    diff --git a/xs/src/slic3r/GUI/GLCanvas3D.cpp b/xs/src/slic3r/GUI/GLCanvas3D.cpp
    index 2bf516e3f..947af6754 100644
    --- a/xs/src/slic3r/GUI/GLCanvas3D.cpp
    +++ b/xs/src/slic3r/GUI/GLCanvas3D.cpp
    @@ -1409,14 +1409,14 @@ Pointf3 GLCanvas3D::Gizmos::get_flattening_normal() const
         return (it != m_gizmos.end()) ? reinterpret_cast(it->second)->get_flattening_normal() : Pointf3(0.f, 0.f, 0.f);
     }
     
    -void GLCanvas3D::Gizmos::set_flattening_data(std::vector vertices_list)
    +void GLCanvas3D::Gizmos::set_flattening_data(const ModelObject* model_object)
     {
         if (!m_enabled)
             return;
     
         GizmosMap::const_iterator it = m_gizmos.find(Flatten);
         if (it != m_gizmos.end())
    -        reinterpret_cast(it->second)->set_flattening_data(vertices_list);
    +        reinterpret_cast(it->second)->set_flattening_data(model_object);
     }
     
     void GLCanvas3D::Gizmos::render(const GLCanvas3D& canvas, const BoundingBoxf3& box) const
    @@ -2202,62 +2202,7 @@ void GLCanvas3D::update_gizmos_data()
                 {
                     m_gizmos.set_scale(model_instance->scaling_factor);
                     m_gizmos.set_angle_z(model_instance->rotation);
    -
    -                /////////////////////////////////////////////////////////////////////////
    -                // Following block provides convex hull data to the Flatten gizmo
    -                // It is temporary, it should be optimized and moved elsewhere later
    -                TriangleMesh ch = model_object->mesh().convex_hull_3d();
    -                stl_facet* facet_ptr = ch.stl.facet_start;
    -                std::vector points;
    -                const unsigned int k = 20;
    -                const float ratio = 0.2f;
    -                const unsigned int N = 3; // 3 - triangle
    -
    -                while (facet_ptr < ch.stl.facet_start+ch.stl.stats.number_of_facets) {
    -                    Pointf3 a = Pointf3(facet_ptr->vertex[1].x - facet_ptr->vertex[0].x, facet_ptr->vertex[1].y - facet_ptr->vertex[0].y, facet_ptr->vertex[1].z - facet_ptr->vertex[0].z);
    -                    Pointf3 b = Pointf3(facet_ptr->vertex[2].x - facet_ptr->vertex[0].x, facet_ptr->vertex[2].y - facet_ptr->vertex[0].y, facet_ptr->vertex[2].z - facet_ptr->vertex[0].z);
    -
    -
    -                    if (0.5 * sqrt(dot(cross(a, b), cross(a,b))) > 50.f) {
    -                        points.emplace_back(Pointf3s(2*k*N));
    -
    -                        std::vector> neighbours;
    -                        if (k != 0) {
    -                            for (unsigned int j=0; jvertex[j].x, facet_ptr->vertex[j].y, facet_ptr->vertex[j].z);
    -                                neighbours.push_back(std::make_pair((int)(j*2*k-k) < 0 ? (N-1)*2*k+k : j*2*k-k, j*2*k+k));
    -                            }
    -
    -                            for (unsigned int i=0; ivertex[j].x, facet_ptr->vertex[j].y, facet_ptr->vertex[j].z));
    -
    -                        points.back().emplace_back(Pointf3(facet_ptr->normal.x, facet_ptr->normal.y, facet_ptr->normal.z));
    -                    }
    -                    facet_ptr+=1;
    -                }
    -                m_gizmos.set_flattening_data(points);
    -                ////////////////////////////////////////////////////////////////////////
    +                m_gizmos.set_flattening_data(model_object);
                 }
             }
         }
    @@ -2265,7 +2210,7 @@ void GLCanvas3D::update_gizmos_data()
         {
             m_gizmos.set_scale(1.0f);
             m_gizmos.set_angle_z(0.0f);
    -        m_gizmos.set_flattening_data(std::vector());
    +        m_gizmos.set_flattening_data(nullptr);
         }
     }
     
    diff --git a/xs/src/slic3r/GUI/GLCanvas3D.hpp b/xs/src/slic3r/GUI/GLCanvas3D.hpp
    index 09a7de823..5f955cce2 100644
    --- a/xs/src/slic3r/GUI/GLCanvas3D.hpp
    +++ b/xs/src/slic3r/GUI/GLCanvas3D.hpp
    @@ -383,7 +383,7 @@ public:
             float get_angle_z() const;
             void set_angle_z(float angle_z);
     
    -        void set_flattening_data(std::vector vertices_list);
    +        void set_flattening_data(const ModelObject* model_object);
             Pointf3 get_flattening_normal() const;
     
             void render(const GLCanvas3D& canvas, const BoundingBoxf3& box) const;
    diff --git a/xs/src/slic3r/GUI/GLGizmo.cpp b/xs/src/slic3r/GUI/GLGizmo.cpp
    index 5a69bc9b7..f0a8f2e71 100644
    --- a/xs/src/slic3r/GUI/GLGizmo.cpp
    +++ b/xs/src/slic3r/GUI/GLGizmo.cpp
    @@ -2,6 +2,8 @@
     
     #include "../../libslic3r/Utils.hpp"
     #include "../../libslic3r/BoundingBox.hpp"
    +#include "../../libslic3r/Model.hpp"
    +#include "../../libslic3r/Geometry.hpp"
     
     #include 
     
    @@ -534,22 +536,19 @@ void GLGizmoFlatten::on_start_dragging()
             m_normal = m_planes[m_hover_id].normal;
     }
     
    -void GLGizmoFlatten::on_update(const Pointf& mouse_pos)
    -{
    -    /*Pointf center(0.5 * (m_grabbers[1].center.x + m_grabbers[0].center.x), 0.5 * (m_grabbers[3].center.y + m_grabbers[0].center.y));
    -
    -    coordf_t orig_len = length(m_starting_drag_position - center);
    -    coordf_t new_len = length(mouse_pos - center);
    -    coordf_t ratio = (orig_len != 0.0) ? new_len / orig_len : 1.0;
    -
    -    m_scale = m_starting_scale * (float)ratio;*/
    -}
    -
     void GLGizmoFlatten::on_render(const BoundingBoxf3& box) const
     {
    +    // the dragged_offset is a vector measuring where was the object moved
    +    // with the gizmo being on. This is reset in set_flattening_data and
    +    // does not work correctly when there are multiple copies.
    +    if (!m_center) // this is the first bounding box that we see
    +        m_center.reset(new Pointf3(box.center().x, box.center().y));
    +    Pointf3 dragged_offset = box.center() - *m_center;
    +
         bool blending_was_enabled = ::glIsEnabled(GL_BLEND);
    +    bool depth_test_was_enabled = ::glIsEnabled(GL_DEPTH_TEST);
         ::glEnable(GL_BLEND);
    -    ::glDisable(GL_DEPTH_TEST);
    +    ::glEnable(GL_DEPTH_TEST);
     
         for (int i=0; i<(int)m_planes.size(); ++i) {
             if (i == m_hover_id)
    @@ -557,14 +556,19 @@ void GLGizmoFlatten::on_render(const BoundingBoxf3& box) const
                 else
                     ::glColor4f(0.9f, 0.9f, 0.9f, 0.5f);
     
    -        ::glBegin(GL_POLYGON);
    -        for (const auto& vertex : m_planes[i].vertices)
    -            ::glVertex3f((GLfloat)vertex.x, (GLfloat)vertex.y, (GLfloat)vertex.z);
    -        ::glEnd();
    +        for (Pointf offset : m_instances_positions) {
    +            offset += dragged_offset;
    +            ::glBegin(GL_POLYGON);
    +            for (const auto& vertex : m_planes[i].vertices)
    +                ::glVertex3f((GLfloat)vertex.x + offset.x, (GLfloat)vertex.y + offset.y, (GLfloat)vertex.z);
    +            ::glEnd();
    +        }
         }
     
         if (!blending_was_enabled)
             ::glDisable(GL_BLEND);
    +    if (!depth_test_was_enabled)
    +        ::glDisable(GL_DEPTH_TEST);
     }
     
     void GLGizmoFlatten::on_render_for_picking(const BoundingBoxf3& box) const
    @@ -576,27 +580,220 @@ void GLGizmoFlatten::on_render_for_picking(const BoundingBoxf3& box) const
         for (unsigned int i = 0; i < m_planes.size(); ++i)
         {
             ::glColor3f(1.f, 1.f, (254.0f - (float)i) * INV_255);
    -        ::glBegin(GL_POLYGON);
    -        for (const auto& vertex : m_planes[i].vertices)
    -            ::glVertex3f((GLfloat)vertex.x, (GLfloat)vertex.y, (GLfloat)vertex.z);
    -        ::glEnd();
    +        for (const Pointf& offset : m_instances_positions) {
    +            ::glBegin(GL_POLYGON);
    +            for (const auto& vertex : m_planes[i].vertices)
    +                ::glVertex3f((GLfloat)vertex.x + offset.x, (GLfloat)vertex.y + offset.y, (GLfloat)vertex.z);
    +            ::glEnd();
    +        }
         }
     }
     
    -void GLGizmoFlatten::set_flattening_data(std::vector vertices_list)
    +
    +// TODO - remove and use Eigen instead
    +static Pointf3 super_rotation(const Pointf3& axis, float angle, const Pointf3& point)
     {
    -    // Each entry in vertices_list describe one polygon that can be laid flat.
    -    // All points but the last one are vertices of the polygon, the last "point" is the outer normal vector.
    +    float axis_length = axis.distance_to(Pointf3(0.f, 0.f, 0.f));
    +    float x = axis.x / axis_length;
    +    float y = axis.y / axis_length;
    +    float z = axis.z / axis_length;
    +    float s = sin(angle);
    +    float c = cos(angle);
    +    float D = 1-c;
    +    float matrix[3][3] = { { c + x*x*D, x*y*D-z*s, x*z*D+y*s },
    +                           { y*x*D+z*s, c+y*y*D,   y*z*D-x*s },
    +                           { z*x*D-y*s, z*y*D+x*s, c+z*z*D   } };
    +    float in[3] = { (float)point.x, (float)point.y, (float)point.z };
    +    float out[3] = { 0, 0, 0 };
    +
    +    for (unsigned char i=0; i<3; ++i)
    +        for (unsigned char j=0; j<3; ++j)
    +            out[i] += matrix[i][j] * in[j];
    +
    +    return Pointf3(out[0], out[1], out[2]);
    +}
    +
    +
    +void GLGizmoFlatten::set_flattening_data(const ModelObject* model_object)
    +{
    +    m_center.release(); // object is not being dragged (this would not be called otherwise) - we must forget about the bounding box position...
    +    m_model_object = model_object;
    +
    +    // ...and save the updated positions of the object instances:
    +    if (m_model_object && !m_model_object->instances.empty()) {
    +        m_instances_positions.clear();
    +        for (const auto* instance : m_model_object->instances)
    +            m_instances_positions.emplace_back(instance->offset);
    +    }
    +
    +    if (is_plane_update_necessary())
    +        update_planes();
    +}
    +
    +void GLGizmoFlatten::update_planes()
    +{
    +    TriangleMesh ch;
    +    for (const ModelVolume* vol : m_model_object->volumes)
    +        ch.merge(vol->get_convex_hull());
    +    ch = ch.convex_hull_3d();
    +    ch.scale(m_model_object->instances.front()->scaling_factor);
    +    ch.rotate_z(m_model_object->instances.front()->rotation);
     
         m_planes.clear();
    -    m_planes.reserve(vertices_list.size());
     
    -    for (const auto& plane_data : vertices_list) {
    -        m_planes.emplace_back(PlaneData());
    -        for (unsigned int i=0; i  facet_queue(num_of_facets, 0);
    +    std::vector facet_visited(num_of_facets, false);
    +    int               facet_queue_cnt = 0;
    +    const stl_normal* normal_ptr = nullptr;
    +    while (1) {
    +        // Find next unvisited triangle:
    +        int facet_idx = 0;
    +        for (; facet_idx < num_of_facets; ++ facet_idx)
    +            if (!facet_visited[facet_idx]) {
    +                facet_queue[facet_queue_cnt ++] = facet_idx;
    +                facet_visited[facet_idx] = true;
    +                normal_ptr = &ch.stl.facet_start[facet_idx].normal;
    +                m_planes.emplace_back();
    +                break;
    +            }
    +        if (facet_idx == num_of_facets)
    +            break; // Everything was visited already
    +
    +        while (facet_queue_cnt > 0) {
    +            int facet_idx = facet_queue[-- facet_queue_cnt];
    +            const stl_normal* this_normal_ptr = &ch.stl.facet_start[facet_idx].normal;
    +            //if (this_normal_ptr->x == normal_ptr->x && this_normal_ptr->y == normal_ptr->y && this_normal_ptr->z == normal_ptr->z) {
    +            if (std::abs(this_normal_ptr->x-normal_ptr->x) < 0.001 && std::abs(this_normal_ptr->y-normal_ptr->y) < 0.001 && std::abs(this_normal_ptr->z-normal_ptr->z) < 0.001) {
    +                stl_vertex* first_vertex = ch.stl.facet_start[facet_idx].vertex;
    +                for (int j=0; j<3; ++j)
    +                    m_planes.back().vertices.emplace_back(first_vertex[j].x, first_vertex[j].y, first_vertex[j].z);
    +
    +                facet_visited[facet_idx] = true;
    +                for (int j = 0; j < 3; ++ j) {
    +                    int neighbor_idx = ch.stl.neighbors_start[facet_idx].neighbor[j];
    +                    if (! facet_visited[neighbor_idx])
    +                        facet_queue[facet_queue_cnt ++] = neighbor_idx;
    +                }
    +            }
    +        }
    +        m_planes.back().normal = Pointf3(normal_ptr->x, normal_ptr->y, normal_ptr->z);
         }
    +
    +    // Now we'll go through all the polygons, transform the points into xy plane to process them:
    +    for (unsigned int polygon_id=0; polygon_id < m_planes.size(); ++polygon_id) {
    +        Pointf3s& polygon = m_planes[polygon_id].vertices;
    +        const Pointf3& normal = m_planes[polygon_id].normal;
    +
    +        // We are going to rotate about z and y to flatten the plane
    +        float angle_z = 0.f;
    +        float angle_y = 0.f;
    +        if (std::abs(normal.y) > 0.001)
    +            angle_z = -atan2(normal.y, normal.x); // angle to rotate so that normal ends up in xz-plane
    +        if (std::abs(normal.x*cos(angle_z)-normal.y*sin(angle_z)) > 0.001)
    +            angle_y = - atan2(normal.x*cos(angle_z)-normal.y*sin(angle_z), normal.z); // angle to rotate to make normal point upwards
    +        else {
    +            // In case it already was in z-direction, we must ensure it is not the wrong way:
    +            angle_y = normal.z > 0.f ? 0 : -M_PI;
    +        }
    +
    +        // Rotate all points to the xy plane:
    +        for (auto& vertex : polygon) {
    +            vertex = super_rotation(Pointf3(0,0,1), angle_z, vertex);
    +            vertex = super_rotation(Pointf3(0,1,0), angle_y, vertex);
    +        }
    +        polygon = Slic3r::Geometry::convex_hull(polygon); // To remove the inner points
    +
    +        // Calculate area of the polygon and discard ones that are too small
    +        float area = 0.f;
    +        for (unsigned int i = 0; i < polygon.size(); i++) // Shoelace formula
    +            area += polygon[i].x*polygon[i+1 < polygon.size() ? i+1 : 0 ].y - polygon[i+1 < polygon.size() ? i+1 : 0].x*polygon[i].y;
    +        area = std::abs(area/2.f);
    +        if (area < 20.f) {
    +            m_planes.erase(m_planes.begin()+(polygon_id--));
    +            continue;
    +        }
    +
    +        // We will shrink the polygon a little bit so it does not touch the object edges:
    +        Pointf3 centroid = std::accumulate(polygon.begin(), polygon.end(), Pointf3(0.f, 0.f, 0.f));
    +        centroid.scale(1.f/polygon.size());
    +        for (auto& vertex : polygon)
    +            vertex = 0.9f*vertex + 0.1f*centroid;
    +
    +        // Polygon is now simple and convex, we'll round the corners to make them look nicer.
    +        // The algorithm takes a vertex, calculates middles of respective sides and moves the vertex
    +        // towards their average (controlled by 'aggressivity'). This is repeated k times.
    +        // In next iterations, the neighbours are not always taken at the middle (to increase the
    +        // rounding effect at the corners, where we need it most).
    +        const unsigned int k = 10; // number of iterations
    +        const float aggressivity = 0.2f;  // agressivity
    +        const unsigned int N = polygon.size();
    +        std::vector> neighbours;
    +        if (k != 0) {
    +            Pointf3s points_out(2*k*N); // vector long enough to store the future vertices
    +            for (unsigned int j=0; jvolumes)
    +        m_source_data.bounding_boxes.push_back(vol->get_convex_hull().bounding_box());
    +    m_source_data.scaling_factor = m_model_object->instances.front()->scaling_factor;
    +    m_source_data.rotation = m_model_object->instances.front()->rotation;
    +}
    +
    +// Check if the bounding boxes of each volume's convex hull is the same as before
    +// and that scaling and rotation has not changed. In that case we don't have to recalculate it.
    +bool GLGizmoFlatten::is_plane_update_necessary() const
    +{
    +    if (m_state != On || !m_model_object || m_model_object->instances.empty())
    +        return false;
    +
    +    if (m_model_object->volumes.size() != m_source_data.bounding_boxes.size()
    +     || m_model_object->instances.front()->scaling_factor != m_source_data.scaling_factor
    +     || m_model_object->instances.front()->rotation != m_source_data.rotation)
    +        return true;
    +
    +    // now compare the bounding boxes:
    +    for (unsigned int i=0; ivolumes.size(); ++i)
    +        if (m_model_object->volumes[i]->get_convex_hull().bounding_box() != m_source_data.bounding_boxes[i])
    +            return true;
    +
    +    return false;
     }
     
     Pointf3 GLGizmoFlatten::get_flattening_normal() const {
    @@ -607,7 +804,5 @@ Pointf3 GLGizmoFlatten::get_flattening_normal() const {
     
     
     
    -
    -
     } // namespace GUI
     } // namespace Slic3r
    diff --git a/xs/src/slic3r/GUI/GLGizmo.hpp b/xs/src/slic3r/GUI/GLGizmo.hpp
    index d1ef6452a..0cab603eb 100644
    --- a/xs/src/slic3r/GUI/GLGizmo.hpp
    +++ b/xs/src/slic3r/GUI/GLGizmo.hpp
    @@ -10,6 +10,7 @@ namespace Slic3r {
     
     class BoundingBoxf3;
     class Pointf3;
    +class ModelObject;
     
     namespace GUI {
     
    @@ -160,23 +161,39 @@ private:
         struct PlaneData {
             std::vector vertices;
             Pointf3 normal;
    -        float color[3];
    +    };
    +    struct SourceDataSummary {
    +        std::vector bounding_boxes; // bounding boxes of convex hulls of individual volumes
    +        float scaling_factor;
    +        float rotation;
         };
     
    +    // This holds information to decide whether recalculation is necessary:
    +    SourceDataSummary m_source_data;
    +
         std::vector m_planes;
    +    std::vector m_instances_positions;
    +    mutable std::unique_ptr m_center = nullptr;
    +    const ModelObject* m_model_object = nullptr;
    +    void update_planes();
    +    bool is_plane_update_necessary() const;
     
     public:
         GLGizmoFlatten();
     
    -    void set_flattening_data(std::vector vertices_list);
    +    void set_flattening_data(const ModelObject* model_object);
         Pointf3 get_flattening_normal() const;
     
     protected:
    -    virtual bool on_init();
    -    virtual void on_start_dragging();
    -    virtual void on_update(const Pointf& mouse_pos);
    -    virtual void on_render(const BoundingBoxf3& box) const;
    -    virtual void on_render_for_picking(const BoundingBoxf3& box) const;
    +    bool on_init() override;
    +    void on_start_dragging() override;
    +    void on_update(const Pointf& mouse_pos) override {};
    +    void on_render(const BoundingBoxf3& box) const override;
    +    void on_render_for_picking(const BoundingBoxf3& box) const override;
    +    void on_set_state() override {
    +        if (m_state == On && is_plane_update_necessary())
    +            update_planes();
    +    }
     };
     
     
    
    From b0dd328fdec094cd072ece2cbfff87dcf07d51c2 Mon Sep 17 00:00:00 2001
    From: Lukas Matena 
    Date: Mon, 20 Aug 2018 11:27:25 +0200
    Subject: [PATCH 09/21] Lay flat - icons and invalidation improvement
    
    ---
     lib/Slic3r/GUI/Plater.pm                  |   2 +-
     resources/icons/overlay/layflat_hover.png | Bin 0 -> 2410 bytes
     resources/icons/overlay/layflat_off.png   | Bin 0 -> 2864 bytes
     resources/icons/overlay/layflat_on.png    | Bin 0 -> 3816 bytes
     xs/src/libslic3r/TriangleMesh.cpp         |   5 +++++
     xs/src/libslic3r/TriangleMesh.hpp         |   1 +
     xs/src/slic3r/GUI/GLGizmo.cpp             |  23 ++++++++++++++--------
     xs/src/slic3r/GUI/GLGizmo.hpp             |   1 +
     8 files changed, 23 insertions(+), 9 deletions(-)
     create mode 100644 resources/icons/overlay/layflat_hover.png
     create mode 100644 resources/icons/overlay/layflat_off.png
     create mode 100644 resources/icons/overlay/layflat_on.png
    
    diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm
    index 8ab749e24..98f31fb8b 100644
    --- a/lib/Slic3r/GUI/Plater.pm
    +++ b/lib/Slic3r/GUI/Plater.pm
    @@ -143,7 +143,7 @@ sub new {
         my $on_gizmo_rotate = sub {
             my ($angle_z, $angle_y) = @_;
             $self->rotate(rad2deg($angle_z), Z, 'absolute');
    -        $self->rotate(rad2deg($angle_y), Y, 'absolute');
    +        $self->rotate(rad2deg($angle_y), Y, 'absolute') if $angle_y != 0;
         };
     
         # callback to update object's geometry info while using gizmos
    diff --git a/resources/icons/overlay/layflat_hover.png b/resources/icons/overlay/layflat_hover.png
    new file mode 100644
    index 0000000000000000000000000000000000000000..afce81d19ee2a26916a1e7684f1bef13e42e486c
    GIT binary patch
    literal 2410
    zcmV-w36=JVP)004R=004l4008;_004mL004C`008P>0026e000+nl3&F}00002
    zVoOIv0RM-N%)bBt010qNS#tmYE+YT{E+YYWr9XB6000McNliru;s_BDBn{m#KTZGu
    z2wh1;K~!koz1i<`TxAsp@Xx)w*`%8^Buzrv6bellHUy|hX~iFrDhL#b&M3?{1Ecst
    zLB#Qe<6q&7zHpS0869WT8Aq!{iq%3H8Y~5E(;v2_?b0@-r3q=;w9Stu&1U!dBHLsW
    z(`J)(Jo{p2?!D(Z-*e9MoO7OY0!2_e1Y%;!q^!}R*{vEaR*7MQ&N}0S13IIl!I-S9
    zh|NW;9TX*;P@VPGyH}${5)#V9ghJ%xWMyPrF``$e4%?lVV{Q=fB0yHS+gE%>Q?w+i
    zj>4n{b&`@QRbnvQ7xu)n<@v8yXt6=J9scZq(UL6CPMP~W=~L0lsG&T#(F(U{QYWcQ
    zT$#8)*6-{zJbkH1pA+7((JqMRy3kTa^&X2*=pw$ugt
    z{LQNlUmHSj4bfD1&=a|bgQi+3cWQNu@)_S{b$i=Rr;T4*lWwouXq(a5_rolFj}uxN
    zJQjUB`>~qDDyw~5vrr_OWC$?iuy^b>(yzS6Zt1|Qm>!+ty8iyA;e}8-|RWxuUN8DyGJcYBoa;P9=I&+Z?-rq
    zEKWRC)(V|)DHWx!4WOOCDnIaSd2+RGH(Bv84uSnkSY0CuD
    z#D^HQ)$biNnzzaVAgngzeS=dF3L{~;4bk%zRrmOsrPIVu=ziSa?bk0mKhsaAD&NuV
    zw^I=^Z-EWsq7BgxVyWAF-3md$rVE9JykVn51}2}E0HMig>8mz~Uo}Lo#aDA{mV3-9
    z3N~ISqsyE2=vGATDu(X$gkz2rSfHJlXL3)~RJVCV*`$X9p?*94)o$nH1O=g}1PV0j
    z_pekgJ!JvwJQ{0i@qlFB_kqBe54`Jbr^F<+YLIqj_NYHUTINx2+nKjOJ8>^$zu2_s
    zA#3y6hXVaNZLz~Sae+GD@>!KWFz~-{M6D5TrgG`L1@7|cu=Xx%CUws^Ylm&Rg<>Yk
    zp&L|c)8&#uVW|LWe9GPS5^E>$LhhmZst2q#;o(7P8@=h`WT=gdOIWE&onAeqp9RW|
    zMB8tV;sUkWG`6}OWcAx_tMk*&d*F)Kta7hgt=BKTP_a{Vqc*krLop9%N@{frMjY~z
    zjrwF~d>p-Z_|pH)!`fHEsW^)n08
    zxhZncHY2J$qG|r@QxsDls-FL83Jln17ixV{a^XQKP!+0N$Z-yJ+ipnQIyWqQAB2(%
    zj3t4L9!CWl)rpr7m_x(Uir;zAsEtW-r7Ok+-j4qPMs|9=1+3GJC*R*kOnCO2P)jOYv<);phb
    zkhsR7izGFup3jNM>2)-e(P^Mqo?5L|shZ`@BKLX9=jM}~2JA5wm(^i|nqoKtkNSeF
    zSG{3y+M6<;_qdxJcF6H!0rcB08%EivLr<|hWy&=AvUb&z&$1#9ddyA6^yx3&0^PRD
    zh9c+uqqvqwlCqf5n#@3X^sQ7kPLx#WH~6~i^`v0iroI!V3Sqx|3qF#}uY9s{$38B4Sad19;vv>T=$<6W&iWJ@I-DPnxyrGLe
    zlCxB0{>dRPJ7BE%%BII}Y!s!h0!T|m&Rb@6aVmrc^oT5zM3+C=Z?t&vIh*`S-$c(e
    zAJ$Twmvy`4#jOz<_MxmQeKy%+WIlCWrx)#%zhUPAq@_3|;}cV1?)p7|(3oT1v(wQz
    zn>N!{>h&X=rh3MOcY;w{H2L9G2o)9=G^}Q~<&Z$#b;SoV2BtFpke}O9n12g+^R3$F
    zeNQ=&hdpS?!mE`@4bR%@tqDO_{M65N6;2KcEU;NSC%vTFHzpt~wsfHd<0OSfuGV~4
    z{Mt)SP9I!M%dKfCE;%f+I!K(=qe6qS{ET4w@4rnw^A`$?_@_+{8fVyxe(J=`p~Z~k
    zo)64vgIY(c&N=CV8a3CrIY*N}lP%0C0FmSVYO51Q1-*Xamrl+(uqc3VI%pbixDY$z
    zi~$MN!Wp_>6>V~kV)A{)aUHhV?W|lT*y~6B;KSKQHnWsBX{pGd_nZ#0!Hpw}dz{jv
    z&$tSSX)c&(WQY0xiRW4U4%?x_ySj`DyMw=Y(aSD~iX2*9rFQP{xDD>f)lzMhW=$F_
    zQLS>u1TAvKkW0?%^P%HTx{Pq4W4i(f6NAf7YK|Im0gElQT%&q568Y_!lQHIkJ|Fqe
    zS;Im>6!ht`v!uc0B#4R8ZJTA5sE)!&ByQ0R0HekyoRBl
    zIx;yrH90FVGCD9Yc~-5}0000bbVXQnWMOn=I&E)cX=ZrEh@wEzGB
    
    literal 0
    HcmV?d00001
    
    diff --git a/resources/icons/overlay/layflat_off.png b/resources/icons/overlay/layflat_off.png
    new file mode 100644
    index 0000000000000000000000000000000000000000..70d198112910a86d34a96bd7e536bfd79a75ae5c
    GIT binary patch
    literal 2864
    zcmV-03(xe4P)004R=004l4008;_004mL004C`008P>0026e000+nl3&F}00002
    zVoOIv0RM-N%)bBt010qNS#tmYE+YT{E+YYWr9XB6000McNliru;s_BDBnC?2=EeX3
    z3L{BGK~!kotyybuRn-;#*52oF&wVDjAt5m#JQ5z_8;V#9sMJoyIzE5YN43>)I-_=~
    zqti~E{;;h-`q0`It4^INwa!$jfG8>`;y_V(3@-@~Eha!nB7r3L-rVPT?A0IlCfr~k
    zH@RJB?vH!US!;cJeQU3M_S%Hfg)}Hq223)cvjz=92*prfsn}6Aaxz!x^eG0oWTRuf
    zV1keZ013b5S3VX@AR;Cj)@263U@$>=UIrOt;QpTg_&~xly^v=@g8+dS2XCyKRT~Le
    zL4Vj60;l8efBej`&b&h+W(7}pBFlqbcI-%KV*$*X2*HA;iCNYU>o0F^m>HVttCb<4
    zLl*`a06aUr@nmNLR0xKEWOxSqkdet~HplUa0gxcW3$h=&PrIe+{!5$Z2dA2KMy-g@
    zX)RXi*p|Z`?YR-Ec^DQ9_VuNZl(shk3J87*u_d*8C*5&z^ZcrL)+{ndzR#|GwEMjc
    z??3b97tZLGh~Of{NuS%|M{Mo{A6d{_=yz
    zH*8FJG)69E5l3qDv>Y9`u0@=1wRwN-9al}i$$udP7#=f-2m}JL;~YP=<-Jvp92y+U
    zd0srkjD&QVvtt4v=!l4l%dCfIF1@w-S}h8O#~2zr36M&r4;+5$m+RjdEXjF+FD;
    z(w`WOg$kF)P9(r8-TlV8)yq5Do$R>14_oR-9Bu%qQ6fMHAhIy<x)i+*Ubu|d8Kna1|-u}&d*8gSihPbC7p-|eqKezbFK5rnwi6H<2
    zu$15O-F@+t8$@u}z2I!-aQ6r6)<1F>azWr%^C#DD+xM4_L%Fgc=m_*?I5QjpMHGbo
    z#FPt`T64i*CL)mA8{fX~-R0Zgj6>7tJJCfC_-52^OuYE>_VQ_f=>}C6JVb%PK4{m@
    zuUd@4lw-#^-u>>&tABkI0vaF?(6lNUo^<{9+P3$tjzg4*K(Yk`IY0scDquZaf87n?
    zh55%`EV*yz3y;0E;v{5#v>q#Gi5ZzK!lBxRm`Qxpvc99e6apUbvW}4^elfzm|=b97c0w6&^D#ubM5%5A+Mee(`VhIVA
    zPVL>k{L!_m1_so~V&|dDZtiNiM4#lF5>2PR*p(_10x{?8oFf&f2+ZXRW-l`uoUY@q
    zzx@2u2X|z0qmU)KR5ojsR_%=V^PiPZ0u<5`0#(rNYiYR5?%ez0Z`Zxhd)(ns
    zO*00(&Ld0f15>qXOP}Ahva8$}02q`o5r!rcbZFm}Kkr=|bI(y|7++-Po@$SjUhp?`R2Lzn$1p6$1C$!B-KQhMF1dA^ht^+6Eu8{
    zDYobK&I^ZTYEge=Vb{CIGUdd8*_A2+eOz49p2VezrW$Le7OAet?b(~O$D#Z+V@gtV
    zsMwS&a^0!#x|65tb$V4(YIoZaXI$DNM35BKJnpipRIG0vqVr@Z+$gr~=ueiB2$HHN
    zpiOP*mh{XTe+wB^gNF~bW+pfx2`o7}G1dKp-^@%|B>cWe_vZIw6Al1PbXB9S-0q{v
    zeFURn_T*`L>HUC_B1njd@<%=CR3|g(72%q(_Sk0^B_@g;NPBJy0BO28(UQzc33ew6
    zfl;WR<%m}X04i*2c;Z21as|pMWMzxy3Sm!t0A&lq7Fu?kOrAaB3^h($%E@T94G(&$
    z8WPQB!s{8W<-^wmE(!N!--tV<_Xs`M8ZokLl=hh|@Z;)bE%Ef4Bdg8Uj*U4y>8rtP^C(C1_dn*!LT5WACz8)SA_?(+Z3xPQkwYRj~X-?;CwtGjfS1JXrmjTNl
    z1ZQ-VM1n%JYfiPVQHzGAFAVh@p4C)&Ir!-M8|Tix-8TzvY}0EmJlmZv4SK=V*u{Z*36S8
    zj8fKg2OIlqI;Tm0qh4b*nsqWn!knztkRJc&KPw*HQ*!r2Q4)hd0e}XflnJ9GRod7W
    z?VQGe1}!RsAP|`(2Dg6t%1?K++a+#V2^qxm%_^DaR=JR+Eb+V$cSpKQyNx@9LIi3Gee
    z02rPC8>LQ1h)_HO0a(-@R@Fr@VU38+UZ7me
    z(jRI{I)CfxaL|sH_^gcZIdMB4T*wm{u;w91THTJ4fbA
    z5wkxW*i{&@U>@LDao>D|Y5-Xsm0YP90Pnf2AKIFRq5+O$6go!%5P+_v<2kG!SL;rt
    zj#PCR1G-PEgc!MnXC595of!qTGY7tUd)FJ!ZvI2p2@f{-7u$Q+-vsCV-skw~u67(_gs-8W
    zWE|4w!cT$WiJVI353d98kR8M9xVl4tAZ5UW0UbId2x7=BJlMz~2V1Zj$MOCD0Jax!
    z2OL;Z!T05UK!I4v+ZEipG#F*!OjFgh?dD=;!TFfd#a
    zEam_J03~!qSaf7zbY(hiZ)9m^c>ppnF*q$SI4vP)004R=004l4008;_004mL004C`008P>0026e000+nl3&F}00006
    zVoOIv0RI600RN!9r;`8x010qNS#tmYE+YT{E+YYWr9XB6000McNliru;s_BDBP&3B
    zEOY<>4cAFTK~#9!<(qqORrhhnKl?jZ_v#ILfIwj00SpGjBR00dxD-?4f@5N5>Pej<
    zH&c_4dODp>-
    zadq!G+duYP=}Jf-OIK26x-(}aU7dS=XCJ@a{q25N$Pj5|$FBl-0pbaoB`^b6s4x?l
    zA}|Rk0}2%K#JVFFMgBK)F*bMkxdw*?CZM3lxomoDq;y0J%kwI}%i(@7{rfF|(B!i~7Xe=a?n0SNAft#yg_4Od?lu^I8PNC2L<)f<*4W`a>rGzY=_y
    z>0ph(Dgr7%iYP3GX?Mf)B`{_>6yE|x;~}pYydIWPh4@wYU>lsThQv)3D!}OQngLW4
    zc!_O-Yz->qnA@zMnU_BMfHWtU4g9vkBNDI@p`4LrFy#)IevdIfelA3_Z~S;a0d>EI
    zeXqd#ubKdo?fvmewZL}Z72v-Kw?$ulL!Xw5uM+?#FeXd%0pJOI=21`*Wzu|@wH#)z
    zgb5YkJkzX(-a({u84>%er2$UthW)=V=3AQv>W&k@Vc-Seb>IRJ^hF3aT{hIbr!XOa
    z$2{d5cwQMr<6z!uxcd)a(xOhr10OUnW<@}pEhAzYu0L%iziTs`IRX-7AQp8{9q?RI
    zLt-^dbreTBq2q1E@KAd;LW@Mj>EL~~-yS`Mq8f+@E{
    zBx)KbW%B7y7PAW9hvtiB>Q$TIlMe@r@pOs|iGtJSpG{Byh
    zVfPl(dRw#IH$2Rz-foP*`W%``4b
    zpBCm4aog*Aeg+@B0d=Q}db3$LxA%IY~w>!8WPs*8k
    z2Q2=w9bkfh4wFx5y}b?29fbq0S%^D#+?L7CwRE^-BAnc7TOkquO%TFZ;QrD``>%lA
    zsShL2q5X)$T72eHJ`LtSXr`Y^X#rfA)is`n>bK$De}n3s&~n9W2Z4f7u;wpe+$`At
    z8f^O~sQu_SK>z>?1l9`ro>r2S&0T%KZ31`WC}R3jn6oPUSUa_zY==vyVc&niJ3ocG
    z)24MVyj8HHZs91ioW+mACm%r5MY!77S^gM48Y$>qz#QNpfEQ|CfImiAK~Z@=cYYae
    zUEawQSQ2w`FKqog*!?mz)fq$SnpGfZhpcS4Wv&%HipE3zDLeV;@J57?VnADiAFKN2
    zyog3ZUtQ#2DM}fiTVhAs6i0p%uAH|VzUoD2_{0hrJuxC!VPnUOwteQ@598;+osU67
    zEu7jn+{^=vR#=+rl10F$kwC=*mN~SX+i-;}!`P1-HDX;?Y99F4agi=T}$255CZTQcp
    zVEeOh@nm00Ro7E84tswNM_z~4Mkt*G3m$?QOVVn(!$yU`LLlaCtY9wiI6^V4jqvdy
    zII;~+?1h%bfthRk8Z=#iF*BiT8jKigrLvlRW>LdOqL4_c+u=PuPabr_3Lu|&D_s8A
    z;(W4w;9*O!Xc38omEm&8FNS!lMU-LO4+3!!J??FsFOLgU0686LYbMiIRcJV8J%Dky
    zSp_Mt7%rcIi#5Y3gm!^*&Y@E_OXZLr?0h$}(!|hfdM9d#7dEKcTv^t<^$ORDicF;=I%)IDughLg$>ICXBWX4dgO&6ID&*{(y>K$+n
    zNDl4V-BZo9vfI#60_3a%P6ES{i3~Hf2XM+M9Krf%|KF$`sCEHz0B9SuW%^^;DHk!g
    zyQ0mZ`<;g#!+-^Y6A7|npDoo@3`JHl9tuhrBo7&cJRf%$yktKX4l_$5##obN(n5>%
    zeWC0;nEL>%{TsOJD-g>Y3;{F+$bkT9k0e!mV23BVN(_#c84?Hgu7`WRYBj>`--BIS
    ztYF&Rwo`vbvf%D@u;NKOHJq1-VdYjl1f;OvUJHVxkEujJI{0SW1
    zYDLlF3GiEAgWvrN$Qfy6v5;SFE;vgcq@H^BdnWphaIx
    z@qN_`91-{z;9U3K;9*7f2zo0pA~UN6)*asdtd-X9{Czw5Em#Zj<`7QfVD^0w%Qxl^
    zZ?%5J>G!NFo!KDJDApN1och26c;*oNRfPmVv%syuNG1j&0`38x18Pzzk&YvnLviT47}Byfqsuzr!by00ll2YjYp%cN
    z_)d!y$KUBMWcFrs9@qxFoBm-(E%2E`u(1Ng@c3haxn%M+9EKyPI|2CCD9ITCs5N6=
    z|3~=Wx6Cgw+QxK6|E>l_Jn**Pxxn84p+6CYhk38Aq4CAIvZY+8sPt}#Ij$l!X-
    z73zSefu8~K?qJCtlk{AdyoULLa^TN_{4O`dav{Ie%31vlQyN%SD9$27e+4Se8nIXF
    z4Y$-)Bv*mw#dzuQ#M>)W*2lvUOa?XpPYN3A5cEBNXkh80^02AVpO^2hv3@=y5;Y5|
    z+63?Zd-`MIihLh<-p8Ge;P(iX?a=~cGX#|Z0j|cw--NZ3*J1DyTR6YO01&|tSJS$a
    z^9+d|bSY}Zfz5($@`==Vy)X|q=munh5-am`#6qH_6+dGlBvluWSwoIV%O
    z>qVM|dfKyTsM8x=E(Bv=YlcI=gg5><%pKw=v2n6DpFO;;s-?+$e{N}W{b^{w211>}
    za{?~|XSm6%K%WEv(7w(CaG+y6^Z_Tj9%UYo#fZ@`bur9Z0W+3aKJU62Ryx*D)g0h;
    z&NIcV>nenF=a>v3xyRmy>MA(>J~Y*XkU$1$WTu0M$2)$_Mp>bTy}gfsa`CdDc&v@i
    zOkHe0$IUiB(VH-Og3hN~K#Ke%T)6=ao
    z%4%nYlnMfhz(K(Xn6SXmVX4u%k>yZOYPi8@&JCoj(B5i&!={S{thju}Cdz6K80fOC
    z)dc8h01hc^Q`ia|^ZLm2fCT^@7LkRL8l1cnn1V72#pY@AN7&(K%nbWke2aC2qdC?M
    za^dnpVBxgA)wKQTSvx$PuZH@w(Ats$WvU97unCaeq1N{SiT;kz4mj5jon*Ema}>TP
    z=u)w>L@`ikXDm}1kqDZdi$rt4i-p)@ER-hWmdM20Ael^u>I6VDc1Af5>v%tEJdz>#_hAh)^aH4o_<`Ds4>>+L}BpvZNHCSgR@kl6bTe
    z4o?Gh3J1k%cvV2X6Ady2W6IcRulx8sHfxwAHu$`daC{z^fK8Ye0=eOLTCu8h1E%GZ
    zSi|OhpjsgzgET(>e-bezr^hiGC;$KeC3HntbYx+4WjbSWWnpw>05UK!I4v+ZEipG#
    zF*!OiIXW~mD=;!TFfb8}@&y0@03~!qSaf7zbY(hiZ)9m^c>ppnF*q$SI4vvertex[0].x : nullptr;
    +}
    +
     void
     TriangleMesh::require_shared_vertices()
     {
    diff --git a/xs/src/libslic3r/TriangleMesh.hpp b/xs/src/libslic3r/TriangleMesh.hpp
    index 6ab52efe2..be151f062 100644
    --- a/xs/src/libslic3r/TriangleMesh.hpp
    +++ b/xs/src/libslic3r/TriangleMesh.hpp
    @@ -53,6 +53,7 @@ public:
         TriangleMeshPtrs split() const;
         void merge(const TriangleMesh &mesh);
         ExPolygons horizontal_projection() const;
    +    const float* first_vertex() const;
         Polygon convex_hull();
         BoundingBoxf3 bounding_box() const;
         // Returns the bbox of this TriangleMesh transformed by the given matrix
    diff --git a/xs/src/slic3r/GUI/GLGizmo.cpp b/xs/src/slic3r/GUI/GLGizmo.cpp
    index f0a8f2e71..2b91afe41 100644
    --- a/xs/src/slic3r/GUI/GLGizmo.cpp
    +++ b/xs/src/slic3r/GUI/GLGizmo.cpp
    @@ -515,15 +515,15 @@ bool GLGizmoFlatten::on_init()
     {
         std::string path = resources_dir() + "/icons/overlay/";
     
    -    std::string filename = path + "scale_off.png";
    +    std::string filename = path + "layflat_off.png";
         if (!m_textures[Off].load_from_file(filename, false))
             return false;
     
    -    filename = path + "scale_hover.png";
    +    filename = path + "layflat_hover.png";
         if (!m_textures[Hover].load_from_file(filename, false))
             return false;
     
    -    filename = path + "scale_on.png";
    +    filename = path + "layflat_on.png";
         if (!m_textures[On].load_from_file(filename, false))
             return false;
     
    @@ -591,12 +591,12 @@ void GLGizmoFlatten::on_render_for_picking(const BoundingBoxf3& box) const
     
     
     // TODO - remove and use Eigen instead
    -static Pointf3 super_rotation(const Pointf3& axis, float angle, const Pointf3& point)
    +static Pointf3 super_rotation(Pointf3 axis, float angle, const Pointf3& point)
     {
    -    float axis_length = axis.distance_to(Pointf3(0.f, 0.f, 0.f));
    -    float x = axis.x / axis_length;
    -    float y = axis.y / axis_length;
    -    float z = axis.z / axis_length;
    +    axis = normalize(axis);
    +    const float& x = axis.x;
    +    const float& y = axis.y;
    +    const float& z = axis.z;
         float s = sin(angle);
         float c = cos(angle);
         float D = 1-c;
    @@ -774,6 +774,8 @@ void GLGizmoFlatten::update_planes()
             m_source_data.bounding_boxes.push_back(vol->get_convex_hull().bounding_box());
         m_source_data.scaling_factor = m_model_object->instances.front()->scaling_factor;
         m_source_data.rotation = m_model_object->instances.front()->rotation;
    +    const float* first_vertex = m_model_object->volumes.front()->get_convex_hull().first_vertex();
    +    m_source_data.mesh_first_point = Pointf3(first_vertex[0], first_vertex[1], first_vertex[3]);
     }
     
     // Check if the bounding boxes of each volume's convex hull is the same as before
    @@ -793,6 +795,11 @@ bool GLGizmoFlatten::is_plane_update_necessary() const
             if (m_model_object->volumes[i]->get_convex_hull().bounding_box() != m_source_data.bounding_boxes[i])
                 return true;
     
    +    const float* first_vertex = m_model_object->volumes.front()->get_convex_hull().first_vertex();
    +    Pointf3 first_point(first_vertex[0], first_vertex[1], first_vertex[2]);
    +    if (first_point != m_source_data.mesh_first_point)
    +        return true;
    +
         return false;
     }
     
    diff --git a/xs/src/slic3r/GUI/GLGizmo.hpp b/xs/src/slic3r/GUI/GLGizmo.hpp
    index 0cab603eb..2c82f73f3 100644
    --- a/xs/src/slic3r/GUI/GLGizmo.hpp
    +++ b/xs/src/slic3r/GUI/GLGizmo.hpp
    @@ -166,6 +166,7 @@ private:
             std::vector bounding_boxes; // bounding boxes of convex hulls of individual volumes
             float scaling_factor;
             float rotation;
    +        Pointf3 mesh_first_point;
         };
     
         // This holds information to decide whether recalculation is necessary:
    
    From 3b86c57c8f917c3d3ef0d2c65528b398808146eb Mon Sep 17 00:00:00 2001
    From: Lukas Matena 
    Date: Mon, 20 Aug 2018 12:56:01 +0200
    Subject: [PATCH 10/21] Lay flat gizmo is rendered before the bed, so the
     surfaces are visible from below, and a rotation-related bugfix
    
    ---
     xs/src/slic3r/GUI/GLCanvas3D.cpp | 26 ++++++++++++++++----------
     xs/src/slic3r/GUI/GLCanvas3D.hpp |  8 ++++++--
     xs/src/slic3r/GUI/GLGizmo.cpp    |  5 +++--
     3 files changed, 25 insertions(+), 14 deletions(-)
    
    diff --git a/xs/src/slic3r/GUI/GLCanvas3D.cpp b/xs/src/slic3r/GUI/GLCanvas3D.cpp
    index 01a8b80f9..88dd88ebb 100644
    --- a/xs/src/slic3r/GUI/GLCanvas3D.cpp
    +++ b/xs/src/slic3r/GUI/GLCanvas3D.cpp
    @@ -1419,22 +1419,27 @@ void GLCanvas3D::Gizmos::set_flattening_data(const ModelObject* model_object)
             reinterpret_cast(it->second)->set_flattening_data(model_object);
     }
     
    -void GLCanvas3D::Gizmos::render(const GLCanvas3D& canvas, const BoundingBoxf3& box) const
    +void GLCanvas3D::Gizmos::render(const GLCanvas3D& canvas, const BoundingBoxf3& box, RenderOrder render_order) const
     {
         if (!m_enabled)
             return;
     
         ::glDisable(GL_DEPTH_TEST);
     
    -    if (box.radius() > 0.0)
    -        _render_current_gizmo(box);
    +    if ((render_order == BeforeBed && dynamic_cast(_get_current()))
    +     || (render_order == AfterBed && !dynamic_cast(_get_current()))) {
    +        if (box.radius() > 0.0)
    +            _render_current_gizmo(box);
    +     }
     
    -    ::glPushMatrix();
    -    ::glLoadIdentity();
    +    if  (render_order == AfterBed) {
    +        ::glPushMatrix();
    +        ::glLoadIdentity();
     
    -    _render_overlay(canvas);
    +        _render_overlay(canvas);
     
    -    ::glPopMatrix();
    +        ::glPopMatrix();
    +    }
     }
     
     void GLCanvas3D::Gizmos::render_current_gizmo_for_picking_pass(const BoundingBoxf3& box) const
    @@ -2249,6 +2254,7 @@ void GLCanvas3D::render()
             _render_axes(false);
         }
         _render_objects();
    +    _render_gizmo(Gizmos::RenderOrder::BeforeBed);
         // textured bed needs to be rendered after objects
         if (!is_custom_bed)
         {
    @@ -2258,7 +2264,7 @@ void GLCanvas3D::render()
         _render_cutting_plane();
         _render_warning_texture();
         _render_legend_texture();
    -    _render_gizmo();
    +    _render_gizmo(Gizmos::RenderOrder::AfterBed);
         _render_layer_editing_overlay();
     
         m_canvas->SwapBuffers();
    @@ -3756,9 +3762,9 @@ void GLCanvas3D::_render_volumes(bool fake_colors) const
             ::glDisable(GL_LIGHTING);
     }
     
    -void GLCanvas3D::_render_gizmo() const
    +void GLCanvas3D::_render_gizmo(Gizmos::RenderOrder render_order) const
     {
    -    m_gizmos.render(*this, _selected_volumes_bounding_box());
    +    m_gizmos.render(*this, _selected_volumes_bounding_box(), render_order);
     }
     
     float GLCanvas3D::_get_layers_editing_cursor_z_relative() const
    diff --git a/xs/src/slic3r/GUI/GLCanvas3D.hpp b/xs/src/slic3r/GUI/GLCanvas3D.hpp
    index 5f955cce2..f09bd4b20 100644
    --- a/xs/src/slic3r/GUI/GLCanvas3D.hpp
    +++ b/xs/src/slic3r/GUI/GLCanvas3D.hpp
    @@ -341,6 +341,10 @@ public:
                 Flatten,
                 Num_Types
             };
    +        enum RenderOrder : unsigned char {
    +            BeforeBed,
    +            AfterBed
    +        };
     
         private:
             bool m_enabled;
    @@ -386,7 +390,7 @@ public:
             void set_flattening_data(const ModelObject* model_object);
             Pointf3 get_flattening_normal() const;
     
    -        void render(const GLCanvas3D& canvas, const BoundingBoxf3& box) const;
    +        void render(const GLCanvas3D& canvas, const BoundingBoxf3& box, RenderOrder render_order) const;
             void render_current_gizmo_for_picking_pass(const BoundingBoxf3& box) const;
     
         private:
    @@ -633,7 +637,7 @@ private:
         void _render_legend_texture() const;
         void _render_layer_editing_overlay() const;
         void _render_volumes(bool fake_colors) const;
    -    void _render_gizmo() const;
    +    void _render_gizmo(Gizmos::RenderOrder render_order) const;
     
         float _get_layers_editing_cursor_z_relative() const;
         void _perform_layer_editing_action(wxMouseEvent* evt = nullptr);
    diff --git a/xs/src/slic3r/GUI/GLGizmo.cpp b/xs/src/slic3r/GUI/GLGizmo.cpp
    index 2b91afe41..c75c365b2 100644
    --- a/xs/src/slic3r/GUI/GLGizmo.cpp
    +++ b/xs/src/slic3r/GUI/GLGizmo.cpp
    @@ -775,7 +775,7 @@ void GLGizmoFlatten::update_planes()
         m_source_data.scaling_factor = m_model_object->instances.front()->scaling_factor;
         m_source_data.rotation = m_model_object->instances.front()->rotation;
         const float* first_vertex = m_model_object->volumes.front()->get_convex_hull().first_vertex();
    -    m_source_data.mesh_first_point = Pointf3(first_vertex[0], first_vertex[1], first_vertex[3]);
    +    m_source_data.mesh_first_point = Pointf3(first_vertex[0], first_vertex[1], first_vertex[2]);
     }
     
     // Check if the bounding boxes of each volume's convex hull is the same as before
    @@ -788,7 +788,7 @@ bool GLGizmoFlatten::is_plane_update_necessary() const
         if (m_model_object->volumes.size() != m_source_data.bounding_boxes.size()
          || m_model_object->instances.front()->scaling_factor != m_source_data.scaling_factor
          || m_model_object->instances.front()->rotation != m_source_data.rotation)
    -        return true;
    +         return true;
     
         // now compare the bounding boxes:
         for (unsigned int i=0; ivolumes.size(); ++i)
    @@ -805,6 +805,7 @@ bool GLGizmoFlatten::is_plane_update_necessary() const
     
     Pointf3 GLGizmoFlatten::get_flattening_normal() const {
         Pointf3 normal = m_normal;
    +    normal.rotate(-m_model_object->instances.front()->rotation);
         m_normal = Pointf3(0.f, 0.f, 0.f);
         return normal;
     }
    
    From d197a5149ae86800ca84fe62af3d2b42e90c0d92 Mon Sep 17 00:00:00 2001
    From: Lukas Matena 
    Date: Mon, 20 Aug 2018 13:02:54 +0200
    Subject: [PATCH 11/21] Added a missing header (numeric for std::accumulate)
    
    ---
     xs/src/slic3r/GUI/GLGizmo.cpp | 1 +
     1 file changed, 1 insertion(+)
    
    diff --git a/xs/src/slic3r/GUI/GLGizmo.cpp b/xs/src/slic3r/GUI/GLGizmo.cpp
    index c75c365b2..72bb7ee7c 100644
    --- a/xs/src/slic3r/GUI/GLGizmo.cpp
    +++ b/xs/src/slic3r/GUI/GLGizmo.cpp
    @@ -8,6 +8,7 @@
     #include 
     
     #include 
    +#include 
     
     namespace Slic3r {
     namespace GUI {
    
    From 28c8e176b56e3fbff11a1f7b7f07bc87bcc252b4 Mon Sep 17 00:00:00 2001
    From: Lukas Matena 
    Date: Tue, 21 Aug 2018 14:36:24 +0200
    Subject: [PATCH 12/21] Yet another experimental parameter to adjust the
     initial loading speed of a newly loaded filament
    
    ---
     xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp |  9 +++++++--
     xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp |  4 +++-
     xs/src/libslic3r/Print.cpp                  |  2 ++
     xs/src/libslic3r/PrintConfig.cpp            |  8 ++++++++
     xs/src/libslic3r/PrintConfig.hpp            |  2 ++
     xs/src/slic3r/GUI/Preset.cpp                | 13 ++++++-------
     xs/src/slic3r/GUI/Tab.cpp                   |  5 +++--
     7 files changed, 31 insertions(+), 12 deletions(-)
    
    diff --git a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
    index de1f9a59b..42c06252b 100644
    --- a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
    +++ b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
    @@ -879,10 +879,15 @@ void WipeTowerPrusaMM::toolchange_Load(
     
         writer.append("; CP TOOLCHANGE LOAD\n")
     		  .suppress_preview()
    -		  .load_move_x_advanced(turning_point, 0.2f * edist, 0.3f * m_filpar[m_current_tool].loading_speed)  // Acceleration
    +		  /*.load_move_x_advanced(turning_point, 0.2f * edist, 0.3f * m_filpar[m_current_tool].loading_speed)  // Acceleration
     		  .load_move_x_advanced(oldx,          0.5f * edist,        m_filpar[m_current_tool].loading_speed)  // Fast phase
     		  .load_move_x_advanced(turning_point, 0.2f * edist, 0.3f * m_filpar[m_current_tool].loading_speed)  // Slowing down
    -		  .load_move_x_advanced(oldx,          0.1f * edist, 0.1f * m_filpar[m_current_tool].loading_speed)  // Super slow
    +		  .load_move_x_advanced(oldx,          0.1f * edist, 0.1f * m_filpar[m_current_tool].loading_speed)  // Super slow*/
    +
    +          .load(0.2f * edist, 60.f * m_filpar[m_current_tool].loading_speed_start)
    +          .load_move_x_advanced(turning_point, 0.7f * edist,        m_filpar[m_current_tool].loading_speed)  // Fast phase
    +		  .load_move_x_advanced(oldx,          0.1f * edist, 0.1f * m_filpar[m_current_tool].loading_speed)  // Super slow*/
    +
               .travel(oldx, writer.y()) // in case last move was shortened to limit x feedrate
     		  .resume_preview();
     
    diff --git a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp
    index 4b96ce17c..305dbc40a 100644
    --- a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp
    +++ b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp
    @@ -65,7 +65,7 @@ public:
     
     
     	// Set the extruder properties.
    -	void set_extruder(size_t idx, material_type material, int temp, int first_layer_temp, float loading_speed,
    +	void set_extruder(size_t idx, material_type material, int temp, int first_layer_temp, float loading_speed, float loading_speed_start,
                           float unloading_speed, float unloading_speed_start, float delay, int cooling_moves,
                           float cooling_initial_speed, float cooling_final_speed, std::string ramming_parameters, float nozzle_diameter)
     	{
    @@ -76,6 +76,7 @@ public:
             m_filpar[idx].temperature = temp;
             m_filpar[idx].first_layer_temperature = first_layer_temp;
             m_filpar[idx].loading_speed = loading_speed;
    +        m_filpar[idx].loading_speed_start = loading_speed_start;
             m_filpar[idx].unloading_speed = unloading_speed;
             m_filpar[idx].unloading_speed_start = unloading_speed_start;
             m_filpar[idx].delay = delay;
    @@ -217,6 +218,7 @@ private:
             int  			    temperature = 0;
             int  			    first_layer_temperature = 0;
             float               loading_speed = 0.f;
    +        float               loading_speed_start = 0.f;
             float               unloading_speed = 0.f;
             float               unloading_speed_start = 0.f;
             float               delay = 0.f ;
    diff --git a/xs/src/libslic3r/Print.cpp b/xs/src/libslic3r/Print.cpp
    index 4154378ec..bd14837d9 100644
    --- a/xs/src/libslic3r/Print.cpp
    +++ b/xs/src/libslic3r/Print.cpp
    @@ -200,6 +200,7 @@ bool Print::invalidate_state_by_config_options(const std::vectorconfig.temperature.get_at(i),
                 this->config.first_layer_temperature.get_at(i),
                 this->config.filament_loading_speed.get_at(i),
    +            this->config.filament_loading_speed_start.get_at(i),
                 this->config.filament_unloading_speed.get_at(i),
                 this->config.filament_unloading_speed_start.get_at(i),
                 this->config.filament_toolchange_delay.get_at(i),
    diff --git a/xs/src/libslic3r/PrintConfig.cpp b/xs/src/libslic3r/PrintConfig.cpp
    index cd933deaf..860283fbd 100644
    --- a/xs/src/libslic3r/PrintConfig.cpp
    +++ b/xs/src/libslic3r/PrintConfig.cpp
    @@ -473,6 +473,14 @@ PrintConfigDef::PrintConfigDef()
         def->min = 0;
         def->default_value = new ConfigOptionFloats { 28. };
     
    +    def = this->add("filament_loading_speed_start", coFloats);
    +    def->label = L("EXPERIMENTAL: Loading speed at the start");
    +    def->tooltip = L("Speed used at the very beginning of loading phase. ");
    +    def->sidetext = L("mm/s");
    +    def->cli = "filament-loading-speed-start=f@";
    +    def->min = 0;
    +    def->default_value = new ConfigOptionFloats { 9. };
    +
         def = this->add("filament_unloading_speed", coFloats);
         def->label = L("Unloading speed");
         def->tooltip = L("Speed used for unloading the filament on the wipe tower (does not affect "
    diff --git a/xs/src/libslic3r/PrintConfig.hpp b/xs/src/libslic3r/PrintConfig.hpp
    index edf7756e8..fd5392cab 100644
    --- a/xs/src/libslic3r/PrintConfig.hpp
    +++ b/xs/src/libslic3r/PrintConfig.hpp
    @@ -528,6 +528,7 @@ public:
         ConfigOptionFloats              filament_cost;
         ConfigOptionFloats              filament_max_volumetric_speed;
         ConfigOptionFloats              filament_loading_speed;
    +    ConfigOptionFloats              filament_loading_speed_start;
         ConfigOptionFloats              filament_load_time;
         ConfigOptionFloats              filament_unloading_speed;
         ConfigOptionFloats              filament_unloading_speed_start;
    @@ -595,6 +596,7 @@ protected:
             OPT_PTR(filament_cost);
             OPT_PTR(filament_max_volumetric_speed);
             OPT_PTR(filament_loading_speed);
    +        OPT_PTR(filament_loading_speed_start);
             OPT_PTR(filament_load_time);
             OPT_PTR(filament_unloading_speed);
             OPT_PTR(filament_unloading_speed_start);
    diff --git a/xs/src/slic3r/GUI/Preset.cpp b/xs/src/slic3r/GUI/Preset.cpp
    index ba27bea8a..abf7e7d43 100644
    --- a/xs/src/slic3r/GUI/Preset.cpp
    +++ b/xs/src/slic3r/GUI/Preset.cpp
    @@ -313,13 +313,12 @@ const std::vector& Preset::filament_options()
     {    
         static std::vector s_opts {
             "filament_colour", "filament_diameter", "filament_type", "filament_soluble", "filament_notes", "filament_max_volumetric_speed",
    -        "extrusion_multiplier", "filament_density", "filament_cost", 
    -        "filament_loading_speed", "filament_load_time", "filament_unloading_speed", "filament_unloading_speed_start", "filament_unload_time",
    -        "filament_toolchange_delay", "filament_cooling_moves", "filament_cooling_initial_speed", "filament_cooling_final_speed", "filament_ramming_parameters",
    -        "filament_minimal_purge_on_wipe_tower", "temperature", "first_layer_temperature", "bed_temperature", "first_layer_bed_temperature",
    -        "fan_always_on", "cooling", "min_fan_speed", "max_fan_speed", "bridge_fan_speed", "disable_fan_first_layers", "fan_below_layer_time",
    -        "slowdown_below_layer_time", "min_print_speed", "start_filament_gcode", "end_filament_gcode","compatible_printers", "compatible_printers_condition",
    -        "inherits"
    +        "extrusion_multiplier", "filament_density", "filament_cost", "filament_loading_speed", "filament_loading_speed_start", "filament_load_time",
    +        "filament_unloading_speed", "filament_unloading_speed_start", "filament_unload_time", "filament_toolchange_delay", "filament_cooling_moves",
    +        "filament_cooling_initial_speed", "filament_cooling_final_speed", "filament_ramming_parameters", "filament_minimal_purge_on_wipe_tower",
    +        "temperature", "first_layer_temperature", "bed_temperature", "first_layer_bed_temperature", "fan_always_on", "cooling", "min_fan_speed",
    +        "max_fan_speed", "bridge_fan_speed", "disable_fan_first_layers", "fan_below_layer_time", "slowdown_below_layer_time", "min_print_speed",
    +        "start_filament_gcode", "end_filament_gcode","compatible_printers", "compatible_printers_condition", "inherits"
         };
         return s_opts;
     }
    diff --git a/xs/src/slic3r/GUI/Tab.cpp b/xs/src/slic3r/GUI/Tab.cpp
    index d3307f1f5..081c1d249 100644
    --- a/xs/src/slic3r/GUI/Tab.cpp
    +++ b/xs/src/slic3r/GUI/Tab.cpp
    @@ -1290,9 +1290,10 @@ void TabFilament::build()
     		optgroup->append_line(line);
     
             optgroup = page->new_optgroup(_(L("Toolchange parameters with single extruder MM printers")));
    -		optgroup->append_single_option_line("filament_loading_speed");
    -        optgroup->append_single_option_line("filament_unloading_speed");
    +		optgroup->append_single_option_line("filament_loading_speed_start");
    +        optgroup->append_single_option_line("filament_loading_speed");
             optgroup->append_single_option_line("filament_unloading_speed_start");
    +        optgroup->append_single_option_line("filament_unloading_speed");
     		optgroup->append_single_option_line("filament_load_time");
     		optgroup->append_single_option_line("filament_unload_time");
             optgroup->append_single_option_line("filament_toolchange_delay");
    
    From 86b67bbd4282016fbbbbc94306e37eada582daf1 Mon Sep 17 00:00:00 2001
    From: Lukas Matena 
    Date: Tue, 21 Aug 2018 15:40:11 +0200
    Subject: [PATCH 13/21] Lay flat - rotation is now done in one go directly
     about the necessary axis
    
    ---
     lib/Slic3r/GUI/Plater.pm          | 56 +++++++++++++++++++++----------
     xs/src/libslic3r/Model.cpp        |  2 +-
     xs/src/libslic3r/Model.hpp        |  2 +-
     xs/src/libslic3r/TriangleMesh.cpp | 11 ++++++
     xs/src/libslic3r/TriangleMesh.hpp |  1 +
     xs/src/slic3r/GUI/GLCanvas3D.cpp  |  8 ++---
     xs/src/slic3r/GUI/GLGizmo.cpp     |  6 ++--
     xs/xsp/Model.xsp                  |  3 +-
     8 files changed, 62 insertions(+), 27 deletions(-)
    
    diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm
    index 98f31fb8b..7000d16d7 100644
    --- a/lib/Slic3r/GUI/Plater.pm
    +++ b/lib/Slic3r/GUI/Plater.pm
    @@ -140,10 +140,16 @@ sub new {
         };
         
         # callback to react to gizmo rotate
    +    # omitting last three parameters means rotation around Z
    +    # otherwise they are the components of the rotation axis vector
         my $on_gizmo_rotate = sub {
    -        my ($angle_z, $angle_y) = @_;
    -        $self->rotate(rad2deg($angle_z), Z, 'absolute');
    -        $self->rotate(rad2deg($angle_y), Y, 'absolute') if $angle_y != 0;
    +        my ($angle, $axis_x, $axis_y, $axis_z) = @_;
    +	if (!defined $axis_x) {
    +            $self->rotate(rad2deg($angle), Z, 'absolute');
    +        }
    +        else {
    +            $self->rotate(rad2deg($angle), undef, 'absolute', $axis_x, $axis_y, $axis_z) if $angle != 0;
    +        }
         };
     
         # callback to update object's geometry info while using gizmos
    @@ -1031,28 +1037,40 @@ sub _get_number_from_user {
     }
     
     sub rotate {
    -    my ($self, $angle, $axis, $relative_key) = @_;
    +    my ($self, $angle, $axis, $relative_key, $axis_x, $axis_y, $axis_z) = @_;
         $relative_key //= 'absolute'; # relative or absolute coordinates
    -    $axis //= Z; # angle is in degrees
    -
    +    $axis_x //= 0;
    +    $axis_y //= 0;
    +    $axis_z //= 0;
         my $relative = $relative_key eq 'relative';    
    -    
    +
         my ($obj_idx, $object) = $self->selected_object;
         return if !defined $obj_idx;
    -    
    +
         my $model_object = $self->{model}->objects->[$obj_idx];
         my $model_instance = $model_object->instances->[0];
    -        
    +
         if (!defined $angle) {
             my $axis_name = $axis == X ? 'X' : $axis == Y ? 'Y' : 'Z';
             my $default = $axis == Z ? rad2deg($model_instance->rotation) : 0;
             $angle = $self->_get_number_from_user(L("Enter the rotation angle:"), L("Rotate around ").$axis_name.(" axis"), L("Invalid rotation angle entered"), $default);
             return if $angle eq '';
         }
    +
    +    # Let's calculate vector of rotation axis (if we don't have it already)
    +    # The minus is there so that the direction is the same as was established
    +    if (defined $axis) {
    +        if ($axis == X) {
    +            $axis_x = -1;
    +        }
    +        if ($axis == Y) {
    +            $axis_y = -1;
    +        }
    +    }
         
         $self->stop_background_process;
         
    -    if ($axis == Z) {
    +    if (defined $axis && $axis == Z) {
             my $new_angle = deg2rad($angle);
             foreach my $inst (@{ $model_object->instances }) {
                 my $rotation = ($relative ? $inst->rotation : 0.) + $new_angle;
    @@ -1067,13 +1085,15 @@ sub rotate {
             }
     #        $object->transform_thumbnail($self->{model}, $obj_idx);
         } else {
    -        # rotation around X and Y needs to be performed on mesh
    -        # so we first apply any Z rotation
    -        if ($model_instance->rotation != 0) {
    -            $model_object->rotate($model_instance->rotation, Z);
    -            $_->set_rotation(0) for @{ $model_object->instances };
    +        if (defined $axis) {
    +            # rotation around X and Y needs to be performed on mesh
    +            # so we first apply any Z rotation
    +            if ($model_instance->rotation != 0) {
    +                $model_object->rotate($model_instance->rotation, Slic3r::Pointf3->new(0, 0, -1));
    +                $_->set_rotation(0) for @{ $model_object->instances };
    +            }
             }
    -        $model_object->rotate(deg2rad($angle), $axis);
    +        $model_object->rotate(deg2rad($angle), Slic3r::Pointf3->new($axis_x, $axis_y, $axis_z));
             
     #        # realign object to Z = 0
     #        $model_object->center_around_origin;
    @@ -1099,7 +1119,7 @@ sub mirror {
         
         # apply Z rotation before mirroring
         if ($model_instance->rotation != 0) {
    -        $model_object->rotate($model_instance->rotation, Z);
    +        $model_object->rotate($model_instance->rotation, Slic3r::Pointf3->new(0, 0, 1));
             $_->set_rotation(0) for @{ $model_object->instances };
         }
         
    @@ -1146,7 +1166,7 @@ sub changescale {
             
             # apply Z rotation before scaling
             if ($model_instance->rotation != 0) {
    -            $model_object->rotate($model_instance->rotation, Z);
    +            $model_object->rotate($model_instance->rotation, Slic3r::Pointf3->new(0, 0, 1));
                 $_->set_rotation(0) for @{ $model_object->instances };
             }
             
    diff --git a/xs/src/libslic3r/Model.cpp b/xs/src/libslic3r/Model.cpp
    index 23d447748..09b515c2f 100644
    --- a/xs/src/libslic3r/Model.cpp
    +++ b/xs/src/libslic3r/Model.cpp
    @@ -725,7 +725,7 @@ void ModelObject::scale(const Pointf3 &versor)
         this->invalidate_bounding_box();
     }
     
    -void ModelObject::rotate(float angle, const Axis &axis)
    +void ModelObject::rotate(float angle, const Pointf3& axis)
     {
         for (ModelVolume *v : this->volumes)
         {
    diff --git a/xs/src/libslic3r/Model.hpp b/xs/src/libslic3r/Model.hpp
    index 23af9fb1c..dadd515de 100644
    --- a/xs/src/libslic3r/Model.hpp
    +++ b/xs/src/libslic3r/Model.hpp
    @@ -120,7 +120,7 @@ public:
         void translate(const Vectorf3 &vector) { this->translate(vector.x, vector.y, vector.z); }
         void translate(coordf_t x, coordf_t y, coordf_t z);
         void scale(const Pointf3 &versor);
    -    void rotate(float angle, const Axis &axis);
    +    void rotate(float angle, const Pointf3& axis);
         void transform(const float* matrix3x4);
         void mirror(const Axis &axis);
         size_t materials_count() const;
    diff --git a/xs/src/libslic3r/TriangleMesh.cpp b/xs/src/libslic3r/TriangleMesh.cpp
    index 008679e6c..4c45680b6 100644
    --- a/xs/src/libslic3r/TriangleMesh.cpp
    +++ b/xs/src/libslic3r/TriangleMesh.cpp
    @@ -324,6 +324,17 @@ void TriangleMesh::translate(float x, float y, float z)
         stl_invalidate_shared_vertices(&this->stl);
     }
     
    +void TriangleMesh::rotate(float angle, Pointf3 axis)
    +{
    +    if (angle == 0.f)
    +        return;
    +
    +    axis = normalize(axis);
    +    Eigen::Transform m = Eigen::Transform::Identity();
    +    m.rotate(Eigen::AngleAxisf(angle, Eigen::Vector3f(axis.x, axis.y, axis.z)));
    +    stl_transform(&stl, (float*)m.data());
    +}
    +
     void TriangleMesh::rotate(float angle, const Axis &axis)
     {
         if (angle == 0.f)
    diff --git a/xs/src/libslic3r/TriangleMesh.hpp b/xs/src/libslic3r/TriangleMesh.hpp
    index be151f062..72e541afc 100644
    --- a/xs/src/libslic3r/TriangleMesh.hpp
    +++ b/xs/src/libslic3r/TriangleMesh.hpp
    @@ -40,6 +40,7 @@ public:
         void scale(const Pointf3 &versor);
         void translate(float x, float y, float z);
         void rotate(float angle, const Axis &axis);
    +    void rotate(float angle, Pointf3 axis);
         void rotate_x(float angle);
         void rotate_y(float angle);
         void rotate_z(float angle);
    diff --git a/xs/src/slic3r/GUI/GLCanvas3D.cpp b/xs/src/slic3r/GUI/GLCanvas3D.cpp
    index 88dd88ebb..ea9fd9086 100644
    --- a/xs/src/slic3r/GUI/GLCanvas3D.cpp
    +++ b/xs/src/slic3r/GUI/GLCanvas3D.cpp
    @@ -2805,9 +2805,9 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
                     // Rotate the object so the normal points downward:
                     Pointf3 normal = m_gizmos.get_flattening_normal();
                     if (normal.x != 0.f || normal.y != 0.f || normal.z != 0.f) {
    -                    float angle_z = -atan2(normal.y, normal.x);
    -                    float angle_y = M_PI - atan2(normal.x*cos(angle_z)-normal.y*sin(angle_z), normal.z);
    -                    m_on_gizmo_rotate_callback.call((double)angle_z, (double)angle_y);
    +                    Pointf3 axis = normal.z > 0.999f ? Pointf3(1, 0, 0) : cross(normal, Pointf3(0.f, 0.f, -1.f));
    +                    float angle = -acos(-normal.z);
    +                    m_on_gizmo_rotate_callback.call(angle, axis.x, axis.y, axis.z);
                     }
                 }
             }
    @@ -3093,7 +3093,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
                 }
                 case Gizmos::Rotate:
                 {
    -                m_on_gizmo_rotate_callback.call((double)m_gizmos.get_angle_z(), 0.);
    +                m_on_gizmo_rotate_callback.call((double)m_gizmos.get_angle_z());
                     break;
                 }
                 default:
    diff --git a/xs/src/slic3r/GUI/GLGizmo.cpp b/xs/src/slic3r/GUI/GLGizmo.cpp
    index 72bb7ee7c..fd4d205fb 100644
    --- a/xs/src/slic3r/GUI/GLGizmo.cpp
    +++ b/xs/src/slic3r/GUI/GLGizmo.cpp
    @@ -706,12 +706,14 @@ void GLGizmoFlatten::update_planes()
             }
             polygon = Slic3r::Geometry::convex_hull(polygon); // To remove the inner points
     
    -        // Calculate area of the polygon and discard ones that are too small
    +        // We will calculate area of the polygon and discard ones that are too small
    +        // The limit is more forgiving in case the normal is in the direction of the coordinate axes
    +        const float minimal_area = (std::abs(normal.x) > 0.999f || std::abs(normal.y) > 0.999f || std::abs(normal.z) > 0.999f) ? 1.f : 20.f;
             float area = 0.f;
             for (unsigned int i = 0; i < polygon.size(); i++) // Shoelace formula
                 area += polygon[i].x*polygon[i+1 < polygon.size() ? i+1 : 0 ].y - polygon[i+1 < polygon.size() ? i+1 : 0].x*polygon[i].y;
             area = std::abs(area/2.f);
    -        if (area < 20.f) {
    +        if (area < minimal_area) {
                 m_planes.erase(m_planes.begin()+(polygon_id--));
                 continue;
             }
    diff --git a/xs/xsp/Model.xsp b/xs/xsp/Model.xsp
    index 25c26c380..182963257 100644
    --- a/xs/xsp/Model.xsp
    +++ b/xs/xsp/Model.xsp
    @@ -301,7 +301,8 @@ ModelMaterial::attributes()
         void translate(double x, double y, double z);
         void scale_xyz(Pointf3* versor)
             %code{% THIS->scale(*versor); %};
    -    void rotate(float angle, Axis axis);
    +    void rotate(float angle, Pointf3* axis)
    +        %code{% THIS->rotate(angle, *axis); %};
         void mirror(Axis axis);
         
         Model* cut(double z)
    
    From 9e6234fe3964696740caf3c9743076a5f43bf681 Mon Sep 17 00:00:00 2001
    From: Lukas Matena 
    Date: Tue, 21 Aug 2018 15:56:40 +0200
    Subject: [PATCH 14/21] Lay flat - limit number of active surfaces to 255 (to
     avoid problems with picking pass)
    
    ---
     xs/src/slic3r/GUI/GLGizmo.cpp | 7 ++++++-
     xs/src/slic3r/GUI/GLGizmo.hpp | 1 +
     2 files changed, 7 insertions(+), 1 deletion(-)
    
    diff --git a/xs/src/slic3r/GUI/GLGizmo.cpp b/xs/src/slic3r/GUI/GLGizmo.cpp
    index fd4d205fb..9ef12c813 100644
    --- a/xs/src/slic3r/GUI/GLGizmo.cpp
    +++ b/xs/src/slic3r/GUI/GLGizmo.cpp
    @@ -709,7 +709,8 @@ void GLGizmoFlatten::update_planes()
             // We will calculate area of the polygon and discard ones that are too small
             // The limit is more forgiving in case the normal is in the direction of the coordinate axes
             const float minimal_area = (std::abs(normal.x) > 0.999f || std::abs(normal.y) > 0.999f || std::abs(normal.z) > 0.999f) ? 1.f : 20.f;
    -        float area = 0.f;
    +        float& area = m_planes[polygon_id].area;
    +        area = 0.f;
             for (unsigned int i = 0; i < polygon.size(); i++) // Shoelace formula
                 area += polygon[i].x*polygon[i+1 < polygon.size() ? i+1 : 0 ].y - polygon[i+1 < polygon.size() ? i+1 : 0].x*polygon[i].y;
             area = std::abs(area/2.f);
    @@ -771,6 +772,10 @@ void GLGizmoFlatten::update_planes()
             }
         }
     
    +    // We'll sort the planes by area and only keep the 255 largest ones (because of the picking pass limitations):
    +    std::sort(m_planes.rbegin(), m_planes.rend(), [](const PlaneData& a, const PlaneData& b) { return a.area < b.area; });
    +    m_planes.resize(std::min((int)m_planes.size(), 255));
    +
         // Planes are finished - let's save what we calculated it from:
         m_source_data.bounding_boxes.clear();
         for (const auto& vol : m_model_object->volumes)
    diff --git a/xs/src/slic3r/GUI/GLGizmo.hpp b/xs/src/slic3r/GUI/GLGizmo.hpp
    index 2c82f73f3..aad31349c 100644
    --- a/xs/src/slic3r/GUI/GLGizmo.hpp
    +++ b/xs/src/slic3r/GUI/GLGizmo.hpp
    @@ -161,6 +161,7 @@ private:
         struct PlaneData {
             std::vector vertices;
             Pointf3 normal;
    +        float area;
         };
         struct SourceDataSummary {
             std::vector bounding_boxes; // bounding boxes of convex hulls of individual volumes
    
    From 0b210426065f02b46be2d0a2c62ee25435b272f3 Mon Sep 17 00:00:00 2001
    From: Lukas Matena 
    Date: Wed, 22 Aug 2018 14:02:32 +0200
    Subject: [PATCH 15/21] Lay flat minor bugfix (ObjectCutDialog called a changed
     function using the old signature)
    
    ---
     lib/Slic3r/GUI/Plater/ObjectCutDialog.pm | 2 +-
     1 file changed, 1 insertion(+), 1 deletion(-)
    
    diff --git a/lib/Slic3r/GUI/Plater/ObjectCutDialog.pm b/lib/Slic3r/GUI/Plater/ObjectCutDialog.pm
    index 35aa28818..26a6fdec3 100644
    --- a/lib/Slic3r/GUI/Plater/ObjectCutDialog.pm
    +++ b/lib/Slic3r/GUI/Plater/ObjectCutDialog.pm
    @@ -137,7 +137,7 @@ sub new {
             # Adjust position / orientation of the split object halves.
             if ($self->{new_model_objects}{lower}) {
                 if ($self->{cut_options}{rotate_lower}) {
    -                $self->{new_model_objects}{lower}->rotate(PI, X);
    +                $self->{new_model_objects}{lower}->rotate(PI, Slic3r::Pointf3->new(1,0,0));
                     $self->{new_model_objects}{lower}->center_around_origin;  # align to Z = 0
                 }
             }
    
    From 60a0375ff99ae2d7608006ebda19d41bcf517b86 Mon Sep 17 00:00:00 2001
    From: Vojtech Kral 
    Date: Tue, 7 Aug 2018 11:28:39 +0200
    Subject: [PATCH 16/21] Firmware updater: Fix a race condition avrdude: Handle
     OOM with configurable handler
    
    ---
     xs/src/avrdude/avrdude-slic3r.cpp    | 95 ++++++++++++++++++++++------
     xs/src/avrdude/avrdude-slic3r.hpp    | 15 ++++-
     xs/src/avrdude/avrdude.h             |  6 ++
     xs/src/avrdude/avrpart.c             | 34 ++++++----
     xs/src/avrdude/buspirate.c           |  7 +-
     xs/src/avrdude/butterfly.c           |  7 +-
     xs/src/avrdude/lexer.c               |  8 ++-
     xs/src/avrdude/main.c                | 27 ++++++++
     xs/src/avrdude/pgm.c                 |  7 +-
     xs/src/avrdude/ser_win32.c           |  9 +--
     xs/src/avrdude/stk500v2.c            |  7 +-
     xs/src/avrdude/update.c              | 25 +++++---
     xs/src/avrdude/wiring.c              |  7 +-
     xs/src/slic3r/GUI/FirmwareDialog.cpp |  6 +-
     14 files changed, 193 insertions(+), 67 deletions(-)
    
    diff --git a/xs/src/avrdude/avrdude-slic3r.cpp b/xs/src/avrdude/avrdude-slic3r.cpp
    index 0577fe6d0..3037f5284 100644
    --- a/xs/src/avrdude/avrdude-slic3r.cpp
    +++ b/xs/src/avrdude/avrdude-slic3r.cpp
    @@ -2,6 +2,10 @@
     
     #include 
     #include 
    +#include 
    +#include 
    +#include 
    +#include 
     
     extern "C" {
     #include "ac_cfg.h"
    @@ -28,6 +32,11 @@ static void avrdude_progress_handler_closure(const char *task, unsigned progress
     	(*progress_fn)(task, progress);
     }
     
    +static void avrdude_oom_handler(const char *context, void *user_p)
    +{
    +	throw std::bad_alloc();
    +}
    +
     
     // Private
     
    @@ -47,16 +56,22 @@ struct AvrDude::priv
     
     	priv(std::string &&sys_config) : sys_config(sys_config) {}
     
    +	void set_handlers();
    +	void unset_handlers();
     	int run_one(const std::vector &args);
     	int run();
    +
    +	struct HandlerGuard
    +	{
    +		priv &p;
    +
    +		HandlerGuard(priv &p) : p(p) { p.set_handlers(); }
    +		~HandlerGuard() { p.unset_handlers(); }
    +	};
     };
     
    -int AvrDude::priv::run_one(const std::vector &args) {
    -	std::vector c_args {{ const_cast(PACKAGE_NAME) }};
    -	for (const auto &arg : args) {
    -		c_args.push_back(const_cast(arg.data()));
    -	}
    -
    +void AvrDude::priv::set_handlers()
    +{
     	if (message_fn) {
     		::avrdude_message_handler_set(avrdude_message_handler_closure, reinterpret_cast(&message_fn));
     	} else {
    @@ -69,10 +84,27 @@ int AvrDude::priv::run_one(const std::vector &args) {
     		::avrdude_progress_handler_set(nullptr, nullptr);
     	}
     
    -	const auto res = ::avrdude_main(static_cast(c_args.size()), c_args.data(), sys_config.c_str());
    +	::avrdude_oom_handler_set(avrdude_oom_handler, nullptr);
    +}
     
    +void AvrDude::priv::unset_handlers()
    +{
     	::avrdude_message_handler_set(nullptr, nullptr);
     	::avrdude_progress_handler_set(nullptr, nullptr);
    +	::avrdude_oom_handler_set(nullptr, nullptr);
    +}
    +
    +
    +int AvrDude::priv::run_one(const std::vector &args) {
    +	std::vector c_args {{ const_cast(PACKAGE_NAME) }};
    +	for (const auto &arg : args) {
    +		c_args.push_back(const_cast(arg.data()));
    +	}
    +
    +	HandlerGuard guard(*this);
    +
    +	const auto res = ::avrdude_main(static_cast(c_args.size()), c_args.data(), sys_config.c_str());
    +
     	return res;
     }
     
    @@ -134,7 +166,7 @@ AvrDude& AvrDude::on_complete(CompleteFn fn)
     
     int AvrDude::run_sync()
     {
    -	return p->run();
    +	return p ? p->run() : -1;
     }
     
     AvrDude::Ptr AvrDude::run()
    @@ -143,19 +175,46 @@ AvrDude::Ptr AvrDude::run()
     
     	if (self->p) {
     		auto avrdude_thread = std::thread([self]() {
    -			bool cancel = false;
    -			int res = -1;
    +			try {
    +				if (self->p->run_fn) {
    +					self->p->run_fn(self);
    +				}
     
    -			if (self->p->run_fn) {
    -				self->p->run_fn();
    -			}
    +				if (! self->p->cancelled) {
    +					self->p->exit_code = self->p->run();
    +				}
     
    -			if (! self->p->cancelled) {
    -				self->p->exit_code = self->p->run();
    -			}
    +				if (self->p->complete_fn) {
    +					self->p->complete_fn();
    +				}
    +			} catch (const std::exception &ex) {
    +				self->p->exit_code = EXIT_EXCEPTION;
     
    -			if (self->p->complete_fn) {
    -				self->p->complete_fn();
    +				static const char *msg = "An exception was thrown in the background thread:\n";
    +
    +				const char *what = ex.what();
    +				auto &message_fn = self->p->message_fn;
    +				if (message_fn) {
    +					message_fn(msg, sizeof(msg));
    +					message_fn(what, std::strlen(what));
    +					message_fn("\n", 1);
    +				}
    +
    +				if (self->p->complete_fn) {
    +					self->p->complete_fn();
    +				}
    +			} catch (...) {
    +				self->p->exit_code = EXIT_EXCEPTION;
    +
    +				static const char *msg = "An unkown exception was thrown in the background thread.\n";
    +
    +				if (self->p->message_fn) {
    +					self->p->message_fn(msg, sizeof(msg));
    +				}
    +
    +				if (self->p->complete_fn) {
    +					self->p->complete_fn();
    +				}
     			}
     		});
     
    diff --git a/xs/src/avrdude/avrdude-slic3r.hpp b/xs/src/avrdude/avrdude-slic3r.hpp
    index 86e097034..754e1e345 100644
    --- a/xs/src/avrdude/avrdude-slic3r.hpp
    +++ b/xs/src/avrdude/avrdude-slic3r.hpp
    @@ -11,8 +11,13 @@ namespace Slic3r {
     class AvrDude
     {
     public:
    +	enum {
    +		EXIT_SUCCEESS   = 0,
    +		EXIT_EXCEPTION  = -1000,
    +	};
    +
     	typedef std::shared_ptr Ptr;
    -	typedef std::function RunFn;
    +	typedef std::function RunFn;
     	typedef std::function MessageFn;
     	typedef std::function ProgressFn;
     	typedef std::function CompleteFn;
    @@ -49,10 +54,18 @@ public:
     	// This has no effect when using run_sync().
     	AvrDude& on_complete(CompleteFn fn);
     
    +	// Perform AvrDude invocation(s) synchronously on the current thread
     	int run_sync();
    +
    +	// Perform AvrDude invocation(s) on a background thread.
    +	// Current instance is moved into a shared_ptr which is returned (and also passed in on_run, if any).
     	Ptr run();
     
    +	// Cancel current operation
     	void cancel();
    +
    +	// If there is a background thread and it is joinable, join() it,
    +	// that is, wait for it to finish.
     	void join();
     
     	bool cancelled();          // Whether avrdude run was cancelled
    diff --git a/xs/src/avrdude/avrdude.h b/xs/src/avrdude/avrdude.h
    index 9f724433f..f4c92a75d 100644
    --- a/xs/src/avrdude/avrdude.h
    +++ b/xs/src/avrdude/avrdude.h
    @@ -39,6 +39,12 @@ typedef void (*avrdude_progress_handler_t)(const char *task, unsigned progress,
     void avrdude_progress_handler_set(avrdude_progress_handler_t newhandler, void *user_p);
     void avrdude_progress_external(const char *task, unsigned progress);
     
    +// OOM handler
    +typedef void (*avrdude_oom_handler_t)(const char *context, void *user_p);
    +void avrdude_oom_handler_set(avrdude_oom_handler_t newhandler, void *user_p);
    +void avrdude_oom(const char *context);
    +
    +
     // Cancellation
     void avrdude_cancel();
     
    diff --git a/xs/src/avrdude/avrpart.c b/xs/src/avrdude/avrpart.c
    index b04851ac1..d0bb951ee 100644
    --- a/xs/src/avrdude/avrpart.c
    +++ b/xs/src/avrdude/avrpart.c
    @@ -36,8 +36,9 @@ OPCODE * avr_new_opcode(void)
     
       m = (OPCODE *)malloc(sizeof(*m));
       if (m == NULL) {
    -    avrdude_message(MSG_INFO, "avr_new_opcode(): out of memory\n");
    -    exit(1);
    +    // avrdude_message(MSG_INFO, "avr_new_opcode(): out of memory\n");
    +    // exit(1);
    +    avrdude_oom("avr_new_opcode(): out of memory\n");
       }
     
       memset(m, 0, sizeof(*m));
    @@ -56,8 +57,9 @@ static OPCODE * avr_dup_opcode(OPCODE * op)
     
       m = (OPCODE *)malloc(sizeof(*m));
       if (m == NULL) {
    -    avrdude_message(MSG_INFO, "avr_dup_opcode(): out of memory\n");
    -    exit(1);
    +    // avrdude_message(MSG_INFO, "avr_dup_opcode(): out of memory\n");
    +    // exit(1);
    +    avrdude_oom("avr_dup_opcode(): out of memory\n");
       }
     
       memcpy(m, op, sizeof(*m));
    @@ -249,8 +251,9 @@ AVRMEM * avr_new_memtype(void)
     
       m = (AVRMEM *)malloc(sizeof(*m));
       if (m == NULL) {
    -    avrdude_message(MSG_INFO, "avr_new_memtype(): out of memory\n");
    -    exit(1);
    +    // avrdude_message(MSG_INFO, "avr_new_memtype(): out of memory\n");
    +    // exit(1);
    +    avrdude_oom("avr_new_memtype(): out of memory\n");
       }
     
       memset(m, 0, sizeof(*m));
    @@ -300,9 +303,10 @@ AVRMEM * avr_dup_mem(AVRMEM * m)
       if (m->buf != NULL) {
         n->buf = (unsigned char *)malloc(n->size);
         if (n->buf == NULL) {
    -      avrdude_message(MSG_INFO, "avr_dup_mem(): out of memory (memsize=%d)\n",
    -                      n->size);
    -      exit(1);
    +      // avrdude_message(MSG_INFO, "avr_dup_mem(): out of memory (memsize=%d)\n",
    +      //                 n->size);
    +      // exit(1);
    +      avrdude_oom("avr_dup_mem(): out of memory");
         }
         memcpy(n->buf, m->buf, n->size);
       }
    @@ -310,9 +314,10 @@ AVRMEM * avr_dup_mem(AVRMEM * m)
       if (m->tags != NULL) {
         n->tags = (unsigned char *)malloc(n->size);
         if (n->tags == NULL) {
    -      avrdude_message(MSG_INFO, "avr_dup_mem(): out of memory (memsize=%d)\n",
    -                      n->size);
    -      exit(1);
    +      // avrdude_message(MSG_INFO, "avr_dup_mem(): out of memory (memsize=%d)\n",
    +      //                 n->size);
    +      // exit(1);
    +      avrdude_oom("avr_dup_mem(): out of memory");
         }
         memcpy(n->tags, m->tags, n->size);
       }
    @@ -441,8 +446,9 @@ AVRPART * avr_new_part(void)
     
       p = (AVRPART *)malloc(sizeof(AVRPART));
       if (p == NULL) {
    -    avrdude_message(MSG_INFO, "new_part(): out of memory\n");
    -    exit(1);
    +    // avrdude_message(MSG_INFO, "new_part(): out of memory\n");
    +    // exit(1);
    +    avrdude_oom("new_part(): out of memory\n");
       }
     
       memset(p, 0, sizeof(*p));
    diff --git a/xs/src/avrdude/buspirate.c b/xs/src/avrdude/buspirate.c
    index 435c4ce53..5875d4283 100644
    --- a/xs/src/avrdude/buspirate.c
    +++ b/xs/src/avrdude/buspirate.c
    @@ -1135,9 +1135,10 @@ static void buspirate_setup(struct programmer_t *pgm)
     {
     	/* Allocate private data */
     	if ((pgm->cookie = calloc(1, sizeof(struct pdata))) == 0) {
    -		avrdude_message(MSG_INFO, "%s: buspirate_initpgm(): Out of memory allocating private data\n",
    -		                progname);
    -		exit(1);
    +		// avrdude_message(MSG_INFO, "%s: buspirate_initpgm(): Out of memory allocating private data\n",
    +		//                 progname);
    +		// exit(1);
    +		avrdude_oom("buspirate_initpgm(): Out of memory allocating private data\n");
     	}
     	PDATA(pgm)->serial_recv_timeout = 100;
     }
    diff --git a/xs/src/avrdude/butterfly.c b/xs/src/avrdude/butterfly.c
    index de9a3175f..beb5e04de 100644
    --- a/xs/src/avrdude/butterfly.c
    +++ b/xs/src/avrdude/butterfly.c
    @@ -63,9 +63,10 @@ struct pdata
     static void butterfly_setup(PROGRAMMER * pgm)
     {
       if ((pgm->cookie = malloc(sizeof(struct pdata))) == 0) {
    -    avrdude_message(MSG_INFO, "%s: butterfly_setup(): Out of memory allocating private data\n",
    -                    progname);
    -    exit(1);
    +    // avrdude_message(MSG_INFO, "%s: butterfly_setup(): Out of memory allocating private data\n",
    +    //                 progname);
    +    // exit(1);
    +    avrdude_oom("butterfly_setup(): Out of memory allocating private data\n");
       }
       memset(pgm->cookie, 0, sizeof(struct pdata));
     }
    diff --git a/xs/src/avrdude/lexer.c b/xs/src/avrdude/lexer.c
    index 93249a9ab..f2d8adb4b 100644
    --- a/xs/src/avrdude/lexer.c
    +++ b/xs/src/avrdude/lexer.c
    @@ -2834,7 +2834,8 @@ YY_BUFFER_STATE yy_scan_bytes  (const char * yybytes, int  _yybytes_len )
     	n = (yy_size_t) (_yybytes_len + 2);
     	buf = (char *) yyalloc( n  );
     	if ( ! buf )
    -		YY_FATAL_ERROR( "out of dynamic memory in yy_scan_bytes()" );
    +		// YY_FATAL_ERROR( "out of dynamic memory in yy_scan_bytes()" );
    +		avrdude_oom("out of dynamic memory in yy_scan_bytes()");
     
     	for ( i = 0; i < _yybytes_len; ++i )
     		buf[i] = yybytes[i];
    @@ -2859,8 +2860,9 @@ YY_BUFFER_STATE yy_scan_bytes  (const char * yybytes, int  _yybytes_len )
     
     static void yynoreturn yy_fatal_error (const char* msg )
     {
    -			fprintf( stderr, "%s\n", msg );
    -	exit( YY_EXIT_FAILURE );
    +	fprintf( stderr, "%s\n", msg );
    +	// exit( YY_EXIT_FAILURE );
    +	abort();
     }
     
     /* Redefine yyless() so it works in section 3 code. */
    diff --git a/xs/src/avrdude/main.c b/xs/src/avrdude/main.c
    index 5d73403b0..ebda0ba19 100644
    --- a/xs/src/avrdude/main.c
    +++ b/xs/src/avrdude/main.c
    @@ -144,6 +144,33 @@ void avrdude_progress_external(const char *task, unsigned progress)
         avrdude_progress_handler(task, progress, avrdude_progress_handler_user_p);
     }
     
    +static void avrdude_oom_handler_null(const char *context, void *user_p)
    +{
    +    // Output a message and just exit
    +    fputs("avrdude: Out of memory: ", stderr);
    +    fputs(context, stderr);
    +    exit(99);
    +}
    +
    +static void *avrdude_oom_handler_user_p = NULL;
    +static avrdude_oom_handler_t avrdude_oom_handler = avrdude_oom_handler_null;
    +
    +void avrdude_oom_handler_set(avrdude_oom_handler_t newhandler, void *user_p)
    +{
    +    if (newhandler != NULL) {
    +        avrdude_oom_handler = newhandler;
    +        avrdude_oom_handler_user_p = user_p;
    +    } else {
    +        avrdude_oom_handler = avrdude_oom_handler_null;
    +        avrdude_oom_handler_user_p = NULL;
    +    }
    +}
    +
    +void avrdude_oom(const char *context)
    +{
    +    avrdude_oom_handler(context, avrdude_oom_handler_user_p);
    +}
    +
     void avrdude_cancel()
     {
         cancel_flag = true;
    diff --git a/xs/src/avrdude/pgm.c b/xs/src/avrdude/pgm.c
    index 851ac5a87..b8a93f104 100644
    --- a/xs/src/avrdude/pgm.c
    +++ b/xs/src/avrdude/pgm.c
    @@ -172,9 +172,10 @@ PROGRAMMER * pgm_dup(const PROGRAMMER * const src)
       for (ln = lfirst(src->usbpid); ln; ln = lnext(ln)) {
         int *ip = malloc(sizeof(int));
         if (ip == NULL) {
    -      avrdude_message(MSG_INFO, "%s: out of memory allocating programmer structure\n",
    -              progname);
    -      exit(1);
    +      // avrdude_message(MSG_INFO, "%s: out of memory allocating programmer structure\n",
    +      //         progname);
    +      // exit(1);
    +      avrdude_oom("out of memory allocating programmer structure\n");
         }
         *ip = *(int *) ldata(ln);
         ladd(pgm->usbpid, ip);
    diff --git a/xs/src/avrdude/ser_win32.c b/xs/src/avrdude/ser_win32.c
    index 3a05cfa90..4e1713128 100644
    --- a/xs/src/avrdude/ser_win32.c
    +++ b/xs/src/avrdude/ser_win32.c
    @@ -246,10 +246,11 @@ static int ser_open(char * port, union pinfo pinfo, union filedescriptor *fdp)
     	    newname = malloc(strlen("\\\\.\\") + strlen(port) + 1);
     
     	    if (newname == 0) {
    -		avrdude_message(MSG_INFO, "%s: ser_open(): out of memory\n",
    -                                progname);
    -		exit(1);
    -	    }
    +		// avrdude_message(MSG_INFO, "%s: ser_open(): out of memory\n",
    +		//                         progname);
    +		// exit(1);
    +			avrdude_oom("ser_open(): out of memory\n");
    +		}
     	    strcpy(newname, "\\\\.\\");
     	    strcat(newname, port);
     
    diff --git a/xs/src/avrdude/stk500v2.c b/xs/src/avrdude/stk500v2.c
    index 4d62640c0..691152b46 100644
    --- a/xs/src/avrdude/stk500v2.c
    +++ b/xs/src/avrdude/stk500v2.c
    @@ -295,9 +295,10 @@ static int stk600_xprog_program_enable(PROGRAMMER * pgm, AVRPART * p);
     void stk500v2_setup(PROGRAMMER * pgm)
     {
       if ((pgm->cookie = malloc(sizeof(struct pdata))) == 0) {
    -    avrdude_message(MSG_INFO, "%s: stk500v2_setup(): Out of memory allocating private data\n",
    -                    progname);
    -    exit(1);
    +    // avrdude_message(MSG_INFO, "%s: stk500v2_setup(): Out of memory allocating private data\n",
    +    //                 progname);
    +    // exit(1);
    +    avrdude_oom("stk500v2_setup(): Out of memory allocating private data\n");
       }
       memset(pgm->cookie, 0, sizeof(struct pdata));
       PDATA(pgm)->command_sequence = 1;
    diff --git a/xs/src/avrdude/update.c b/xs/src/avrdude/update.c
    index 417cbf71d..a255ab4f9 100644
    --- a/xs/src/avrdude/update.c
    +++ b/xs/src/avrdude/update.c
    @@ -38,8 +38,9 @@ UPDATE * parse_op(char * s)
     
       upd = (UPDATE *)malloc(sizeof(UPDATE));
       if (upd == NULL) {
    -    avrdude_message(MSG_INFO, "%s: out of memory\n", progname);
    -    exit(1);
    +    // avrdude_message(MSG_INFO, "%s: out of memory\n", progname);
    +    // exit(1);
    +    avrdude_oom("parse_op: out of memory\n");
       }
     
       i = 0;
    @@ -53,8 +54,9 @@ UPDATE * parse_op(char * s)
         upd->op = DEVICE_WRITE;
         upd->filename = (char *)malloc(strlen(buf) + 1);
         if (upd->filename == NULL) {
    -        avrdude_message(MSG_INFO, "%s: out of memory\n", progname);
    -        exit(1);
    +        // avrdude_message(MSG_INFO, "%s: out of memory\n", progname);
    +        // exit(1);
    +        avrdude_oom("parse_op: out of memory\n");
         }
         strcpy(upd->filename, buf);
         upd->format = FMT_AUTO;
    @@ -63,8 +65,9 @@ UPDATE * parse_op(char * s)
     
       upd->memtype = (char *)malloc(strlen(buf)+1);
       if (upd->memtype == NULL) {
    -    avrdude_message(MSG_INFO, "%s: out of memory\n", progname);
    -    exit(1);
    +    // avrdude_message(MSG_INFO, "%s: out of memory\n", progname);
    +    // exit(1);
    +    avrdude_oom("parse_op: out of memory\n");
       }
       strcpy(upd->memtype, buf);
     
    @@ -179,8 +182,9 @@ UPDATE * dup_update(UPDATE * upd)
     
       u = (UPDATE *)malloc(sizeof(UPDATE));
       if (u == NULL) {
    -    avrdude_message(MSG_INFO, "%s: out of memory\n", progname);
    -    exit(1);
    +    // avrdude_message(MSG_INFO, "%s: out of memory\n", progname);
    +    // exit(1);
    +    avrdude_oom("dup_update: out of memory\n");
       }
     
       memcpy(u, upd, sizeof(UPDATE));
    @@ -200,8 +204,9 @@ UPDATE * new_update(int op, char * memtype, int filefmt, char * filename, unsign
     
       u = (UPDATE *)malloc(sizeof(UPDATE));
       if (u == NULL) {
    -    avrdude_message(MSG_INFO, "%s: out of memory\n", progname);
    -    exit(1);
    +    // avrdude_message(MSG_INFO, "%s: out of memory\n", progname);
    +    // exit(1);
    +    avrdude_oom("new_update: out of memory\n");
       }
     
       u->memtype = strdup(memtype);
    diff --git a/xs/src/avrdude/wiring.c b/xs/src/avrdude/wiring.c
    index 395459762..562a3f17c 100644
    --- a/xs/src/avrdude/wiring.c
    +++ b/xs/src/avrdude/wiring.c
    @@ -85,9 +85,10 @@ static void wiring_setup(PROGRAMMER * pgm)
        * Now prepare our data
        */
       if ((mycookie = malloc(sizeof(struct wiringpdata))) == 0) {
    -    avrdude_message(MSG_INFO, "%s: wiring_setup(): Out of memory allocating private data\n",
    -                    progname);
    -    exit(1);
    +    // avrdude_message(MSG_INFO, "%s: wiring_setup(): Out of memory allocating private data\n",
    +    //                 progname);
    +    // exit(1);
    +    avrdude_oom("wiring_setup(): Out of memory allocating private data\n");
       }
       memset(mycookie, 0, sizeof(struct wiringpdata));
       WIRINGPDATA(mycookie)->snoozetime = 0;
    diff --git a/xs/src/slic3r/GUI/FirmwareDialog.cpp b/xs/src/slic3r/GUI/FirmwareDialog.cpp
    index 77e70c49b..d0cd9f8cf 100644
    --- a/xs/src/slic3r/GUI/FirmwareDialog.cpp
    +++ b/xs/src/slic3r/GUI/FirmwareDialog.cpp
    @@ -551,8 +551,10 @@ void FirmwareDialog::priv::perform_upload()
     	// because the dialog ensures it doesn't exit before the background thread is done.
     	auto q = this->q;
     
    -	this->avrdude = avrdude
    -		.on_run([this]() {
    +	avrdude
    +		.on_run([this](AvrDude::Ptr avrdude) {
    +			this->avrdude = std::move(avrdude);
    +
     			try {
     				switch (this->hex_file.device) {
     				case HexFile::DEV_MK3:
    
    From e8aafd3c830cca2df628a2d330640f825e0067c1 Mon Sep 17 00:00:00 2001
    From: Lukas Matena 
    Date: Fri, 24 Aug 2018 11:46:54 +0200
    Subject: [PATCH 17/21] Lay flat - simple rejection of very small surfaces
    
    ---
     xs/src/slic3r/GUI/GLGizmo.cpp | 6 ++++++
     1 file changed, 6 insertions(+)
    
    diff --git a/xs/src/slic3r/GUI/GLGizmo.cpp b/xs/src/slic3r/GUI/GLGizmo.cpp
    index 9ef12c813..bbd8f44eb 100644
    --- a/xs/src/slic3r/GUI/GLGizmo.cpp
    +++ b/xs/src/slic3r/GUI/GLGizmo.cpp
    @@ -680,6 +680,12 @@ void GLGizmoFlatten::update_planes()
                 }
             }
             m_planes.back().normal = Pointf3(normal_ptr->x, normal_ptr->y, normal_ptr->z);
    +
    +        // if this is a just a very small triangle, remove it to speed up further calculations (it would be rejected anyway):
    +        if (m_planes.back().vertices.size() == 3 &&
    +             ( m_planes.back().vertices[0].distance_to(m_planes.back().vertices[1]) < 1.f
    +            || m_planes.back().vertices[0].distance_to(m_planes.back().vertices[2]) < 1.f))
    +            m_planes.pop_back();
         }
     
         // Now we'll go through all the polygons, transform the points into xy plane to process them:
    
    From be3b8e98daab21fda63bf126a1bcd6d80869706b Mon Sep 17 00:00:00 2001
    From: Lukas Matena 
    Date: Fri, 24 Aug 2018 12:46:32 +0200
    Subject: [PATCH 18/21] Edited captions and default values of the new
     parameters (initial loading and unloading speed)
    
    ---
     xs/src/libslic3r/PrintConfig.cpp | 8 ++++----
     1 file changed, 4 insertions(+), 4 deletions(-)
    
    diff --git a/xs/src/libslic3r/PrintConfig.cpp b/xs/src/libslic3r/PrintConfig.cpp
    index 860283fbd..c2baba027 100644
    --- a/xs/src/libslic3r/PrintConfig.cpp
    +++ b/xs/src/libslic3r/PrintConfig.cpp
    @@ -474,12 +474,12 @@ PrintConfigDef::PrintConfigDef()
         def->default_value = new ConfigOptionFloats { 28. };
     
         def = this->add("filament_loading_speed_start", coFloats);
    -    def->label = L("EXPERIMENTAL: Loading speed at the start");
    +    def->label = L("Loading speed at the start");
         def->tooltip = L("Speed used at the very beginning of loading phase. ");
         def->sidetext = L("mm/s");
         def->cli = "filament-loading-speed-start=f@";
         def->min = 0;
    -    def->default_value = new ConfigOptionFloats { 9. };
    +    def->default_value = new ConfigOptionFloats { 3. };
     
         def = this->add("filament_unloading_speed", coFloats);
         def->label = L("Unloading speed");
    @@ -491,12 +491,12 @@ PrintConfigDef::PrintConfigDef()
         def->default_value = new ConfigOptionFloats { 90. };
     
         def = this->add("filament_unloading_speed_start", coFloats);
    -    def->label = L("EXPERIMENTAL: Unloading speed at the start");
    +    def->label = L("Unloading speed at the start");
         def->tooltip = L("Speed used for unloading the tip of the filament immediately after ramming. ");
         def->sidetext = L("mm/s");
         def->cli = "filament-unloading-speed-start=f@";
         def->min = 0;
    -    def->default_value = new ConfigOptionFloats { 83. };
    +    def->default_value = new ConfigOptionFloats { 100. };
     
         def = this->add("filament_toolchange_delay", coFloats);
         def->label = L("Delay after unloading");
    
    From a4176ef9338e7a1984fd983661351275c7ff19f1 Mon Sep 17 00:00:00 2001
    From: Lukas Matena 
    Date: Fri, 24 Aug 2018 16:52:06 +0200
    Subject: [PATCH 19/21] Bugfix - dialog that changes number of copies deleted
     the object when cancelled
    
    ---
     lib/Slic3r/GUI/Plater.pm | 5 +++--
     1 file changed, 3 insertions(+), 2 deletions(-)
    
    diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm
    index dbdf0be27..cae167f28 100644
    --- a/lib/Slic3r/GUI/Plater.pm
    +++ b/lib/Slic3r/GUI/Plater.pm
    @@ -995,9 +995,10 @@ sub set_number_of_copies {
         my $model_object = $self->{model}->objects->[$obj_idx];
         
         # prompt user
    -    my $copies = Wx::GetNumberFromUser("", L("Enter the number of copies of the selected object:"), L("Copies"), $model_object->instances_count, 0, 1000, $self);
    +    my $copies = -1;
    +    $copies = Wx::GetNumberFromUser("", L("Enter the number of copies of the selected object:"), L("Copies"), $model_object->instances_count, 0, 1000, $self);
         my $diff = $copies - $model_object->instances_count;
    -    if ($diff == 0) {
    +    if ($diff == 0 || $copies == -1) {
             # no variation
             $self->resume_background_process;
         } elsif ($diff > 0) {
    
    From 78a7104994b671e622c6e2bb00718d3c0bd6fa60 Mon Sep 17 00:00:00 2001
    From: bubnikv 
    Date: Sat, 25 Aug 2018 22:09:55 +0200
    Subject: [PATCH 20/21] Changed the wording of "Purge into this object's ..."
     to "Wipe ..."
    
    ---
     xs/src/libslic3r/PrintConfig.cpp | 4 ++--
     1 file changed, 2 insertions(+), 2 deletions(-)
    
    diff --git a/xs/src/libslic3r/PrintConfig.cpp b/xs/src/libslic3r/PrintConfig.cpp
    index b582b501b..f9f0b2056 100644
    --- a/xs/src/libslic3r/PrintConfig.cpp
    +++ b/xs/src/libslic3r/PrintConfig.cpp
    @@ -2059,7 +2059,7 @@ PrintConfigDef::PrintConfigDef()
     
         def = this->add("wipe_into_infill", coBool);
         def->category = L("Extruders");
    -    def->label = L("Purge into this object's infill");
    +    def->label = L("Wipe into this object's infill");
         def->tooltip = L("Purging after toolchange will done inside this object's infills. "
                          "This lowers the amount of waste but may result in longer print time "
                          " due to additional travel moves.");
    @@ -2068,7 +2068,7 @@ PrintConfigDef::PrintConfigDef()
     
         def = this->add("wipe_into_objects", coBool);
         def->category = L("Extruders");
    -    def->label = L("Purge into this object");
    +    def->label = L("Wipe into this object");
         def->tooltip = L("Object will be used to purge the nozzle after a toolchange to save material "
                          "that would otherwise end up in the wipe tower and decrease print time. "
                          "Colours of the objects will be mixed as a result.");
    
    From 4522811f5bae9ca3a66270ad7f51ac39e0cb6e47 Mon Sep 17 00:00:00 2001
    From: bubnikv 
    Date: Sat, 25 Aug 2018 22:11:04 +0200
    Subject: [PATCH 21/21] Bumped up the version number to 1.41.0-beta2
    
    ---
     xs/src/libslic3r/libslic3r.h | 2 +-
     1 file changed, 1 insertion(+), 1 deletion(-)
    
    diff --git a/xs/src/libslic3r/libslic3r.h b/xs/src/libslic3r/libslic3r.h
    index 34f61cb12..6db60440b 100644
    --- a/xs/src/libslic3r/libslic3r.h
    +++ b/xs/src/libslic3r/libslic3r.h
    @@ -14,7 +14,7 @@
     #include 
     
     #define SLIC3R_FORK_NAME "Slic3r Prusa Edition"
    -#define SLIC3R_VERSION "1.41.0-beta"
    +#define SLIC3R_VERSION "1.41.0-beta2"
     #define SLIC3R_BUILD "UNKNOWN"
     
     typedef int32_t coord_t;