Просмотр исходного кода

Move dstool_oss to ydb/apps/dstool.

yuryalekseev 2 лет назад
Родитель
Сommit
48aaf60ffa

+ 7 - 0
contrib/libs/python/Include/cStringIO.h

@@ -0,0 +1,7 @@
+#pragma once
+
+#ifdef USE_PYTHON3
+#error "No <cStringIO.h> in Python3"
+#else
+#include <contrib/tools/python/src/Include/cStringIO.h>
+#endif

+ 39 - 0
contrib/libs/qhull/COPYING.txt

@@ -0,0 +1,39 @@
+                    Qhull, Copyright (c) 1993-2020
+                    
+                            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.  
+Files derived from Qhull 1.0 are copyrighted by the Geometry Center.  The
+remaining files are copyrighted by C.B. Barber.  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.

+ 720 - 0
contrib/libs/qhull/README.txt

@@ -0,0 +1,720 @@
+Name
+
+      qhull, rbox        2020.2        2020/08/31        (8.0.2)
+  
+Convex hull, Delaunay triangulation, Voronoi diagrams, Halfspace intersection
+
+      Documentation:
+        html/index.htm
+        <http://www.qhull.org/html>
+
+      Available from:
+        <http://www.qhull.org>
+        <http://www.qhull.org/download>
+        <http://github.com/qhull/qhull/wiki> (git@github.com:qhull/qhull.git)
+
+      News and a paper:
+        <http://www.qhull.org/news>
+        <http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.117.405>
+
+     Version 1 (simplicial only):
+        <http://www.qhull.org/download/qhull-1.0.tar.gz>
+
+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. <http://www.geomview.org>
+
+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 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/wiki, 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.
+      (<https://github.com/qhull/qhull/wiki/Qhull-build-systems>)
+
+  Install and build instructions follow.  
+
+  See the end of this document for a list of distributed files.
+
+------------------
+Index
+
+Installing Qhull on Windows 10, 8, 7 (32- or 64-bit), Windows XP, and Windows NT
+Installing Qhull on Unix with gcc
+Installing Qhull with CMake 2.6 or later
+Installing Qhull with Qt
+Working with Qhull's C++ interface
+Calling Qhull from C programs
+Compiling Qhull with Microsoft Visual C++
+Compiling Qhull with Qt Creator
+Compiling Qhull with mingw/gcc on Windows
+Compiling Qhull with cygwin on Windows
+Compiling from Makfile without gcc
+Compiling on other machines and compilers
+Distributed files
+Authors
+
+------------------
+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.
+    Or 'qconvex --help' or 'qconvex -?'
+  - 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 regularly use qhull on a Windows host, install a bash shell such as
+    https://gitforwindows.org/     # based on MSYS2
+      https://github.com/git-for-windows/git/wiki
+      http://www.msys2.org/
+      https://github.com/msys2/msys2/wiki
+      [mar'19] Git for Windows v2.21 requires 'qhull --help'
+      Install in C:\Git\... # Not 'Program Files\...' otherwise './configure && make' fails
+    www.cygwin.com 
+    www.mingw.org/wiki/msys        # for Windows XP
+    Road Bash (www.qhull.org/bash) # based on MSYS
+
+------------------
+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
+  - make test
+  
+  'make install' installs Qhull at '/usr/local/'.  It installs pkg-config files 
+  at '/usr/local/lib/pkgconfig'.  Change the install directory with DESTDIR and PREFIX.
+  
+  To build 32-bit Qhull on a 64-bit host (uses 33% less memory in 4-d)
+  - make new M32=-m32
+
+  To build 32-bit Qhull without -fpic (may be faster, but shared library may fail)
+  - make new M32=-m32  FPIC=
+
+  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
+  - cmake -G "<generator>" ..  # e.g., for MINGW-w64 -- cmake -G "MSYS Makefiles" ..
+  - cmake ..                   
+  - make
+  - ctest
+  - make install		# If MSYS or UNIX, default CMAKE_INSTALL_PREFIX is '/usr/local'
+  				# otherwise if WINDOWS, installs to ../bin, ../include, and ../lib
+  - make uninstall		# Delete the files in install_manifest.txt
+
+  The ".." is important.  It refers to the parent directory (i.e., qhull/)
+  
+  CMake installs lib/pkgconfig/qhull*.pc for use with pkg-config
+
+  If CMAKE_INSTALL_PREFIX is C:/Program Files/qhull, you may need to give 'Users' "full control" 
+  to qhull's sub-directories: bin, doc, include, lib, and man (folder > Properties > Security > Edit > Users).
+  
+  On Windows, CMake's 64-bit generators have a "Win64" tag.  Qhull's data structures 
+  are substantial larger as 64-bit code than as 32-bit code.  This may slow down Qhull.
+
+  If cmake fails with "No CMAKE_C_COMPILER could be found"
+  - cmake was not able to find the build environment specified by -G "..."
+
+  If cmake's gcc smoketest fails after a Windows update
+  - Reinstall MINGW-w64 and delete CMakeCache.txt.  A Windows update can break gcc process creation for cc1.
+
+------------------
+Installing Qhull with Qt
+
+  To build Qhull, including its C++ test program (qhulltest)
+  - Download and extract Qhull (either GitHub, .tgz file, or .zip file)
+  - Load src/qhull-all.pro into QtCreator
+  - Configure the project to use a Shadow build at the same level as 'src', 'bin', and 'lib'
+    If, instead, the shadow build is a subdirectory of 'build', Qt Creator will install Qhull in 'build/bin' and 'build/lib'
+  - Build
+  
+  - Build qhulltest with a C++11 or later compiler
+  - qhulltest depends on shared libraries QtCore.a and QtTest.a.  They may need to be copied 
+    into the bin directory.  On Windows, copy Qt5Core.dll and Qt5Test.dll, e.g., /qt/5.11.2/msvc2017_64/bin
+  - If qhulltest fails with exit status 127 and no error message, 
+    check for missing Q5Core.dll and Qt5Test.dll
+
+------------------
+Working with Qhull's C++ interface
+
+  See html/qh-code.htm#cpp for calling Qhull from C++ programs
+  
+  Class and method documentation is limited
+
+  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/wiki
+    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.
+
+  Qhull provides pkg-config support with build/qhull.pc.in and lib/pkgconfig/qhull*.pc
+  With back-ticks, you can compile your C++ program with the Qhull libraries:
+      g++ `pkg-config --cflags --libs qhullcpp qhullstatic_r` -o my_app my_app.cpp
+  or
+      g++ `pkg-config --cflags --libs qhullcpp qhull_r` -o my_app my_app.cpp
+
+  qhullcpp must be linked before qhull_r, otherwise the linker reports
+  an error -- "QhullUser ... multiple definition of `qh_fprintf'"
+
+------------------
+Calling Qhull from C programs
+
+  See html/qh-code.htm#library for calling Qhull from C programs
+
+  Qhull provides pkg-config support with build/qhull.pc.in and lib/pkgconfig/qhull*.pc
+  With back-ticks, you can compile your C program with the Qhull library
+      gcc `pkg-config --cflags --libs qhull_r` -o my_app my_app.c
+
+  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 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	
+  - Right-click 'Retarget solution' from toolset v110 to your Platform Toolset
+    File > Save All
+  - 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'
+  - Copy Qt shared libraries, QtCore.dll and QtTest.dll, into the bin directory
+
+  To compile 64-bit Qhull with Microsoft Visual C++ 2010 and later
+  - 64-bit Qhull has larger data structures due to 64-bit pointers.  This may slow down Qhull.
+  - Download and extract Qhull (either GitHub, .tgz file, or .zip file)
+  - Load solution build/qhull-64.sln 
+  - Right-click 'Retarget solution' from toolset v110 to your Platform Toolset
+    File > Save All
+  - Build target 'x64'
+  - If build as 32-bit fails, use solution build/qhull-32.sln
+  - 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'
+  
+  If error -- MSB8020: The build tools for Visual Studio 2012 (Platform Toolset = 'v110') cannot be found.
+  - 'Project > Retarget solution' for both qhull-32.sln and qhull-64.sln
+  - 'File > Open' your preferred solution (qhull-32.sln or qhull-64.sln)
+  - 'Save All' both projects
+  - DevStudio may need a restart
+
+  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
+  - Configure the project to use a Shadow build at the same level as 'src', 'bin', and 'lib'
+    If, instead, the shadow build is a subdirectory of 'build', Qt Creator will install Qhull in 'build/bin' and 'build/lib'
+  - Build
+  
+  - Build qhulltest with a C++11 or later compiler
+  - qhulltest depends on shared libraries QtCore.a and QtTest.a.  They may need to be copied 
+    into the bin directory.  On Windows, copy Qt5Core.dll and Qt5Test.dll, e.g., /qt/5.11.2/msvc2017_64/bin
+  - If qhulltest fails with exit status 127 and no error message, 
+    check for missing Q5Core.dll and Qt5Test.dll
+
+------------------
+Compiling Qhull with mingw/gcc on Windows
+
+  To compile Qhull with MINGW
+  - Download and extract Qhull (either GitHub, .tgz file, or .zip file)
+  - Install GitForWindows (https://gitforwindows.org/)
+    or MSYS2 (http://www.msys2.org/)
+    Install in C:\Git\... # Not 'Program Files\...' otherwise './configure && make' will not work
+  - Install MINGW-w64 with gcc (https://mingw-w64.org/)
+    1) Goto sourceforge -- https://sourceforge.net/projects/mingw-w64/files/
+    2) in folder -- mingw-w64
+    3) download installer -- MinGW-W64-install.exe
+    Run the installer
+    1) Select i686/posix/dwarf
+    2) Install in 'C:\mingw-w64' # Not 'Program Files\...'
+    Rename /c/mingw-w64/mingw32/bin/mingw32-make.exe to make.exe
+    Add the 'C:\mingw-w64\mingw32\bin' directory to your $PATH environment variable
+    Execute 'which make' to check that 'make' is mingw-w64's make
+  - Compile Qhull from the home directory
+    make help
+    make
+
+  Notes
+  - Mingw is included with Qt SDK in qt/Tools/mingw53_32
+  - If you use Windows XP
+    Install Road Bash (http://www.qhull.org/bash) or MSYS (http://www.mingw.org/wiki/msys)
+    Install MINGW (http://mingw.org/)
+
+------------------
+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)
+  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/CMakeModules/CheckLFS.cmake // enables Large File Support in CMake
+  build/config.cmake.in // extract target variables
+  build/qhull.pc.in    // pkg-config template for creating lib/pkgconfig/qhull*.pc
+  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
+  build/qhulltest/     // DevStudio project files for qhulltest (C++ and Qt)
+  build/README-build.txt // Contents of build/	
+  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_benchmark          // shell script for precision and performance benchmark
+  q_benchmark-ok.txt   // reviewed output from q_benchmark
+  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.bat           // Windows batch test for QHULL-GO.bat
+                       // cd bin; ..\eg\q_test.bat >q_test.x 2>&1
+  q_test-ok.txt        // reviewed output from q_test
+  qhulltest-ok.txt     // reviewed output from qhulltest (Qt only)
+  make-qhull_qh.sh     // shell script to create non-reentrant qhull_qh from reentrant Qhull
+  make-vcproj.sh       // shell script to create vcproj and vcxprog files
+  qhull-zip.sh         // shell script to create distribution files
+  qtest.sh             // shell script for testing and logging qhull
+
+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
+  qh_findbestfacet-drielsma.pdf
+  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 files for Visual C++
+  qhull-nomerge-exports.def
+  qhull_p-exports.def
+  qhull_p-nomerge-exports.def
+  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_r-exports.def  // Export Definition files for Visual C++
+  qhull_r-nomerge-exports.def
+  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
+  QhullUser.cpp
+  QhullUser.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.

+ 32 - 0
contrib/libs/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!

+ 2302 - 0
contrib/libs/qhull/libqhull_r/geom2_r.c

@@ -0,0 +1,2302 @@
+/*<html><pre>  -<a                             href="qh-geom_r.htm"
+  >-------------------------------</a><a name="TOP">-</a>
+
+
+   geom2_r.c
+   infrequently used geometric routines of qhull
+
+   see qh-geom_r.htm and geom_r.h
+
+   Copyright (c) 1993-2020 The Geometry Center.
+   $Id: //main/2019/qhull/src/libqhull_r/geom2_r.c#17 $$Change: 3037 $
+   $DateTime: 2020/09/03 17:28:32 $$Author: bbarber $
+
+   frequently used code goes into geom_r.c
+*/
+
+#include "qhull_ra.h"
+
+/*================== functions in alphabetic order ============*/
+
+/*-<a                             href="qh-geom_r.htm#TOC"
+  >-------------------------------</a><a name="copypoints">-</a>
+
+  qh_copypoints(qh, points, numpoints, dimension )
+    return qh_malloc'd copy of points
+
+  notes:
+    qh_free the returned points to avoid a memory leak
+*/
+coordT *qh_copypoints(qhT *qh, coordT *points, int numpoints, int dimension)
+{
+  int size;
+  coordT *newpoints;
+
+  size= numpoints * dimension * (int)sizeof(coordT);
+  if (!(newpoints= (coordT *)qh_malloc((size_t)size))) {
+    qh_fprintf(qh, qh->ferr, 6004, "qhull error: insufficient memory to copy %d points\n",
+        numpoints);
+    qh_errexit(qh, qh_ERRmem, NULL, NULL);
+  }
+  memcpy((char *)newpoints, (char *)points, (size_t)size); /* newpoints!=0 by QH6004 */
+  return newpoints;
+} /* copypoints */
+
+/*-<a                             href="qh-geom_r.htm#TOC"
+  >-------------------------------</a><a name="crossproduct">-</a>
+
+  qh_crossproduct( dim, vecA, vecB, vecC )
+    crossproduct of 2 dim vectors
+    C= A x B
+
+  notes:
+    from Glasner, Graphics Gems I, p. 639
+    only defined for dim==3
+*/
+void qh_crossproduct(int dim, realT vecA[3], realT vecB[3], realT vecC[3]){
+
+  if (dim == 3) {
+    vecC[0]=   det2_(vecA[1], vecA[2],
+                     vecB[1], vecB[2]);
+    vecC[1]= - det2_(vecA[0], vecA[2],
+                     vecB[0], vecB[2]);
+    vecC[2]=   det2_(vecA[0], vecA[1],
+                     vecB[0], vecB[1]);
+  }
+} /* vcross */
+
+/*-<a                             href="qh-geom_r.htm#TOC"
+  >-------------------------------</a><a name="determinant">-</a>
+
+  qh_determinant(qh, rows, dim, nearzero )
+    compute signed determinant of a square matrix
+    uses qh.NEARzero to test for degenerate matrices
+
+  returns:
+    determinant
+    overwrites rows and the matrix
+    if dim == 2 or 3
+      nearzero iff determinant < qh->NEARzero[dim-1]
+      (!quite correct, not critical)
+    if dim >= 4
+      nearzero iff diagonal[k] < qh->NEARzero[k]
+*/
+realT qh_determinant(qhT *qh, realT **rows, int dim, boolT *nearzero) {
+  realT det=0;
+  int i;
+  boolT sign= False;
+
+  *nearzero= False;
+  if (dim < 2) {
+    qh_fprintf(qh, qh->ferr, 6005, "qhull internal error (qh_determinate): only implemented for dimension >= 2\n");
+    qh_errexit(qh, qh_ERRqhull, NULL, NULL);
+  }else if (dim == 2) {
+    det= det2_(rows[0][0], rows[0][1],
+                 rows[1][0], rows[1][1]);
+    if (fabs_(det) < 10*qh->NEARzero[1])  /* QH11031 FIX: 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])  /* QH11031 FIX: what should this be?  det 5.5e-12 was flat for qh_maxsimplex of qdelaunay 0,0 27,27 -36,36 -9,63 */
+      *nearzero= True;
+  }else {
+    qh_gausselim(qh, rows, dim, dim, &sign, nearzero);  /* if nearzero, diagonal still ok */
+    det= 1.0;
+    for (i=dim; i--; )
+      det *= (rows[i])[i];
+    if (sign)
+      det= -det;
+  }
+  return det;
+} /* determinant */
+
+/*-<a                             href="qh-geom_r.htm#TOC"
+  >-------------------------------</a><a name="detjoggle">-</a>
+
+  qh_detjoggle(qh, points, numpoints, dimension )
+    determine default max joggle for point array
+      as qh_distround * qh_JOGGLEdefault
+
+  returns:
+    initial value for JOGGLEmax from points and REALepsilon
+
+  notes:
+    computes DISTround since qh_maxmin not called yet
+    if qh->SCALElast, last dimension will be scaled later to MAXwidth
+
+    loop duplicated from qh_maxmin
+*/
+realT qh_detjoggle(qhT *qh, pointT *points, int numpoints, int dimension) {
+  realT abscoord, distround, joggle, maxcoord, mincoord;
+  pointT *point, *pointtemp;
+  realT maxabs= -REALmax;
+  realT sumabs= 0;
+  realT maxwidth= 0;
+  int k;
+
+  if (qh->SETroundoff)
+    distround= qh->DISTround; /* 'En' */
+  else{
+    for (k=0; k < dimension; k++) {
+      if (qh->SCALElast && k == dimension-1)
+        abscoord= maxwidth;
+      else if (qh->DELAUNAY && k == dimension-1) /* will qh_setdelaunay() */
+        abscoord= 2 * maxabs * maxabs;  /* may be low by qh->hull_dim/2 */
+      else {
+        maxcoord= -REALmax;
+        mincoord= REALmax;
+        FORALLpoint_(qh, points, numpoints) {
+          maximize_(maxcoord, point[k]);
+          minimize_(mincoord, point[k]);
+        }
+        maximize_(maxwidth, maxcoord-mincoord);
+        abscoord= fmax_(maxcoord, -mincoord);
+      }
+      sumabs += abscoord;
+      maximize_(maxabs, abscoord);
+    } /* for k */
+    distround= qh_distround(qh, qh->hull_dim, maxabs, sumabs);
+  }
+  joggle= distround * qh_JOGGLEdefault;
+  maximize_(joggle, REALepsilon * qh_JOGGLEdefault);
+  trace2((qh, qh->ferr, 2001, "qh_detjoggle: joggle=%2.2g maxwidth=%2.2g\n", joggle, maxwidth));
+  return joggle;
+} /* detjoggle */
+
+/*-<a                             href="qh-geom_r.htm#TOC"
+  >-------------------------------</a><a name="detmaxoutside">-</a>
+
+  qh_detmaxoutside(qh);
+    determine qh.MAXoutside target for qh_RATIO... tests of distance
+    updates option '_max-outside'
+
+  notes:
+    called from qh_addpoint and qh_detroundoff
+    accounts for qh.ONEmerge, qh.DISTround, qh.MINoutside ('Wn'), qh.max_outside
+    see qh_maxout for qh.max_outside with qh.DISTround
+*/
+
+void qh_detmaxoutside(qhT *qh) {
+  realT maxoutside;
+
+  maxoutside= fmax_(qh->max_outside, qh->ONEmerge + qh->DISTround);
+  maximize_(maxoutside, qh->MINoutside);
+  qh->MAXoutside= maxoutside;
+  trace3((qh, qh->ferr, 3056, "qh_detmaxoutside: MAXoutside %2.2g from qh.max_outside %2.2g, ONEmerge %2.2g, MINoutside %2.2g, DISTround %2.2g\n",
+      qh->MAXoutside, qh->max_outside, qh->ONEmerge, qh->MINoutside, qh->DISTround));
+} /* detmaxoutside */
+
+/*-<a                             href="qh-geom_r.htm#TOC"
+  >-------------------------------</a><a name="detroundoff">-</a>
+
+  qh_detroundoff(qh)
+    determine maximum roundoff errors from
+      REALepsilon, REALmax, REALmin, qh.hull_dim, qh.MAXabs_coord,
+      qh.MAXsumcoord, qh.MAXwidth, qh.MINdenom_1
+
+    accounts for qh.SETroundoff, qh.RANDOMdist, qh->MERGEexact
+      qh.premerge_cos, qh.postmerge_cos, qh.premerge_centrum,
+      qh.postmerge_centrum, qh.MINoutside,
+      qh_RATIOnearinside, qh_COPLANARratio, qh_WIDEcoplanar
+
+  returns:
+    sets qh.DISTround, etc. (see below)
+    appends precision constants to qh.qhull_options
+
+  see:
+    qh_maxmin() for qh.NEARzero
+
+  design:
+    determine qh.DISTround for distance computations
+    determine minimum denominators for qh_divzero
+    determine qh.ANGLEround for angle computations
+    adjust qh.premerge_cos,... for roundoff error
+    determine qh.ONEmerge for maximum error due to a single merge
+    determine qh.NEARinside, qh.MAXcoplanar, qh.MINvisible,
+      qh.MINoutside, qh.WIDEfacet
+    initialize qh.max_vertex and qh.minvertex
+*/
+void qh_detroundoff(qhT *qh) {
+
+  qh_option(qh, "_max-width", NULL, &qh->MAXwidth);
+  if (!qh->SETroundoff) {
+    qh->DISTround= qh_distround(qh, qh->hull_dim, qh->MAXabs_coord, qh->MAXsumcoord);
+    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;
+    trace4((qh, qh->ferr, 4096, "qh_detroundoff: increase qh.ANGLEround by option 'R%2.2g'\n", 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 option 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-max-coplanar", 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 */
+  qh_detmaxoutside(qh);
+} /* detroundoff */
+
+/*-<a                             href="qh-geom_r.htm#TOC"
+  >-------------------------------</a><a name="detsimplex">-</a>
+
+  qh_detsimplex(qh, apex, points, dim, nearzero )
+    compute determinant of a simplex with point apex and base points
+
+  returns:
+     signed determinant and nearzero from qh_determinant
+
+  notes:
+     called by qh_maxsimplex and qh_initialvertices
+     uses qh.gm_matrix/qh.gm_row (assumes they're big enough)
+
+  design:
+    construct qm_matrix by subtracting apex from points
+    compute determinate
+*/
+realT qh_detsimplex(qhT *qh, pointT *apex, setT *points, int dim, boolT *nearzero) {
+  pointT *coorda, *coordp, *gmcoord, *point, **pointp;
+  coordT **rows;
+  int k,  i=0;
+  realT det;
+
+  zinc_(Zdetsimplex);
+  gmcoord= qh->gm_matrix;
+  rows= qh->gm_row;
+  FOREACHpoint_(points) {
+    if (i == dim)
+      break;
+    rows[i++]= gmcoord;
+    coordp= point;
+    coorda= apex;
+    for (k=dim; k--; )
+      *(gmcoord++)= *coordp++ - *coorda++;
+  }
+  if (i < dim) {
+    qh_fprintf(qh, qh->ferr, 6007, "qhull internal error (qh_detsimplex): #points %d < dimension %d\n",
+               i, dim);
+    qh_errexit(qh, qh_ERRqhull, NULL, NULL);
+  }
+  det= qh_determinant(qh, rows, dim, nearzero);
+  trace2((qh, qh->ferr, 2002, "qh_detsimplex: det=%2.2g for point p%d, dim %d, nearzero? %d\n",
+          det, qh_pointid(qh, apex), dim, *nearzero));
+  return det;
+} /* detsimplex */
+
+/*-<a                             href="qh-geom_r.htm#TOC"
+  >-------------------------------</a><a name="distnorm">-</a>
+
+  qh_distnorm( dim, point, normal, offset )
+    return distance from point to hyperplane at normal/offset
+
+  returns:
+    dist
+
+  notes:
+    dist > 0 if point is outside of hyperplane
+
+  see:
+    qh_distplane in geom_r.c
+*/
+realT qh_distnorm(int dim, pointT *point, pointT *normal, realT *offsetp) {
+  coordT *normalp= normal, *coordp= point;
+  realT dist;
+  int k;
+
+  dist= *offsetp;
+  for (k=dim; k--; )
+    dist += *(coordp++) * *(normalp++);
+  return dist;
+} /* distnorm */
+
+/*-<a                             href="qh-geom_r.htm#TOC"
+  >-------------------------------</a><a name="distround">-</a>
+
+  qh_distround(qh, dimension, maxabs, maxsumabs )
+    compute maximum round-off error for a distance computation
+      to a normalized hyperplane
+    maxabs is the maximum absolute value of a coordinate
+    maxsumabs is the maximum possible sum of absolute coordinate values
+    if qh.RANDOMdist ('Qr'), adjusts qh_distround
+
+  returns:
+    max dist round for qh.REALepsilon and qh.RANDOMdist
+
+  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, delta;
+
+  maxdistsum= sqrt((realT)dimension) * maxabs;
+  minimize_( maxdistsum, maxsumabs);
+  maxround= REALepsilon * (dimension * maxdistsum * 1.01 + maxabs);
+              /* adds maxabs for offset */
+  if (qh->RANDOMdist) {
+    delta= qh->RANDOMfactor * maxabs;
+    maxround += delta;
+    trace4((qh, qh->ferr, 4092, "qh_distround: increase roundoff by random delta %2.2g for option 'R%2.2g'\n", delta, qh->RANDOMfactor));
+  }
+  trace4((qh, qh->ferr, 4008, "qh_distround: %2.2g, maxabs %2.2g, maxsumabs %2.2g, maxdistsum %2.2g\n",
+            maxround, maxabs, maxsumabs, maxdistsum));
+  return maxround;
+} /* distround */
+
+/*-<a                             href="qh-geom_r.htm#TOC"
+  >-------------------------------</a><a name="divzero">-</a>
+
+  qh_divzero( numer, denom, mindenom1, zerodiv )
+    divide by a number that's nearly zero
+    mindenom1= minimum denominator for dividing into 1.0
+
+  returns:
+    quotient
+    sets zerodiv and returns 0.0 if it would overflow
+
+  design:
+    if numer is nearly zero and abs(numer) < abs(denom)
+      return numer/denom
+    else if numer is nearly zero
+      return 0 and zerodiv
+    else if denom/numer non-zero
+      return numer/denom
+    else
+      return 0 and zerodiv
+*/
+realT qh_divzero(realT numer, realT denom, realT mindenom1, boolT *zerodiv) {
+  realT temp, numerx, denomx;
+
+
+  if (numer < mindenom1 && numer > -mindenom1) {
+    numerx= fabs_(numer);
+    denomx= fabs_(denom);
+    if (numerx < denomx) {
+      *zerodiv= False;
+      return numer/denom;
+    }else {
+      *zerodiv= True;
+      return 0.0;
+    }
+  }
+  temp= denom/numer;
+  if (temp > mindenom1 || temp < -mindenom1) {
+    *zerodiv= False;
+    return numer/denom;
+  }else {
+    *zerodiv= True;
+    return 0.0;
+  }
+} /* divzero */
+
+
+/*-<a                             href="qh-geom_r.htm#TOC"
+  >-------------------------------</a><a name="facetarea">-</a>
+
+  qh_facetarea(qh, facet )
+    return area for a facet
+
+  notes:
+    if non-simplicial,
+      uses centrum to triangulate facet and sums the projected areas.
+    if (qh->DELAUNAY),
+      computes projected area instead for last coordinate
+    assumes facet->normal exists
+    projecting tricoplanar facets to the hyperplane does not appear to make a difference
+
+  design:
+    if simplicial
+      compute area
+    else
+      for each ridge
+        compute area from centrum to ridge
+    negate area if upper Delaunay facet
+*/
+realT qh_facetarea(qhT *qh, facetT *facet) {
+  vertexT *apex;
+  pointT *centrum;
+  realT area= 0.0;
+  ridgeT *ridge, **ridgep;
+
+  if (facet->simplicial) {
+    apex= SETfirstt_(facet->vertices, vertexT);
+    area= qh_facetarea_simplex(qh, qh->hull_dim, apex->point, facet->vertices,
+                    apex, facet->toporient, facet->normal, &facet->offset);
+  }else {
+    if (qh->CENTERtype == qh_AScentrum)
+      centrum= facet->center;
+    else
+      centrum= qh_getcentrum(qh, facet);
+    FOREACHridge_(facet->ridges)
+      area += qh_facetarea_simplex(qh, qh->hull_dim, centrum, ridge->vertices,
+                 NULL, (boolT)(ridge->top == facet),  facet->normal, &facet->offset);
+    if (qh->CENTERtype != qh_AScentrum)
+      qh_memfree(qh, centrum, qh->normal_size);
+  }
+  if (facet->upperdelaunay && qh->DELAUNAY)
+    area= -area;  /* the normal should be [0,...,1] */
+  trace4((qh, qh->ferr, 4009, "qh_facetarea: f%d area %2.2g\n", facet->id, area));
+  return area;
+} /* facetarea */
+
+/*-<a                             href="qh-geom_r.htm#TOC"
+  >-------------------------------</a><a name="facetarea_simplex">-</a>
+
+  qh_facetarea_simplex(qh, dim, apex, vertices, notvertex, toporient, normal, offset )
+    return area for a simplex defined by
+      an apex, a base of vertices, an orientation, and a unit normal
+    if simplicial or tricoplanar facet,
+      notvertex is defined and it is skipped in vertices
+
+  returns:
+    computes area of simplex projected to plane [normal,offset]
+    returns 0 if vertex too far below plane (qh->WIDEfacet)
+      vertex can't be apex of tricoplanar facet
+
+  notes:
+    if (qh->DELAUNAY),
+      computes projected area instead for last coordinate
+    uses qh->gm_matrix/gm_row and qh->hull_dim
+    helper function for qh_facetarea
+
+  design:
+    if Notvertex
+      translate simplex to apex
+    else
+      project simplex to normal/offset
+      translate simplex to apex
+    if Delaunay
+      set last row/column to 0 with -1 on diagonal
+    else
+      set last row to Normal
+    compute determinate
+    scale and flip sign for area
+*/
+realT qh_facetarea_simplex(qhT *qh, int dim, coordT *apex, setT *vertices,
+        vertexT *notvertex,  boolT toporient, coordT *normal, realT *offset) {
+  pointT *coorda, *coordp, *gmcoord;
+  coordT **rows, *normalp;
+  int k,  i=0;
+  realT area, dist;
+  vertexT *vertex, **vertexp;
+  boolT nearzero;
+
+  gmcoord= qh->gm_matrix;
+  rows= qh->gm_row;
+  FOREACHvertex_(vertices) {
+    if (vertex == notvertex)
+      continue;
+    rows[i++]= gmcoord;
+    coorda= apex;
+    coordp= vertex->point;
+    normalp= normal;
+    if (notvertex) {
+      for (k=dim; k--; )
+        *(gmcoord++)= *coordp++ - *coorda++;
+    }else {
+      dist= *offset;
+      for (k=dim; k--; )
+        dist += *coordp++ * *normalp++;
+      if (dist < -qh->WIDEfacet) {
+        zinc_(Znoarea);
+        return 0.0;
+      }
+      coordp= vertex->point;
+      normalp= normal;
+      for (k=dim; k--; )
+        *(gmcoord++)= (*coordp++ - dist * *normalp++) - *coorda++;
+    }
+  }
+  if (i != dim-1) {
+    qh_fprintf(qh, qh->ferr, 6008, "qhull internal error (qh_facetarea_simplex): #points %d != dim %d -1\n",
+               i, dim);
+    qh_errexit(qh, qh_ERRqhull, NULL, NULL);
+  }
+  rows[i]= gmcoord;
+  if (qh->DELAUNAY) {
+    for (i=0; i < dim-1; i++)
+      rows[i][dim-1]= 0.0;
+    for (k=dim; k--; )
+      *(gmcoord++)= 0.0;
+    rows[dim-1][dim-1]= -1.0;
+  }else {
+    normalp= normal;
+    for (k=dim; k--; )
+      *(gmcoord++)= *normalp++;
+  }
+  zinc_(Zdetfacetarea);
+  area= qh_determinant(qh, rows, dim, &nearzero);
+  if (toporient)
+    area= -area;
+  area *= qh->AREAfactor;
+  trace4((qh, qh->ferr, 4010, "qh_facetarea_simplex: area=%2.2g for point p%d, toporient %d, nearzero? %d\n",
+          area, qh_pointid(qh, apex), toporient, nearzero));
+  return area;
+} /* facetarea_simplex */
+
+/*-<a                             href="qh-geom_r.htm#TOC"
+  >-------------------------------</a><a name="facetcenter">-</a>
+
+  qh_facetcenter(qh, vertices )
+    return Voronoi center (Voronoi vertex) for a facet's vertices
+
+  returns:
+    return temporary point equal to the center
+
+  see:
+    qh_voronoi_center()
+*/
+pointT *qh_facetcenter(qhT *qh, setT *vertices) {
+  setT *points= qh_settemp(qh, qh_setsize(qh, vertices));
+  vertexT *vertex, **vertexp;
+  pointT *center;
+
+  FOREACHvertex_(vertices)
+    qh_setappend(qh, &points, vertex->point);
+  center= qh_voronoi_center(qh, qh->hull_dim-1, points);
+  qh_settempfree(qh, &points);
+  return center;
+} /* facetcenter */
+
+/*-<a                             href="qh-geom_r.htm#TOC"
+  >-------------------------------</a><a name="findgooddist">-</a>
+
+  qh_findgooddist(qh, point, facetA, dist, facetlist )
+    find best good facet visible for point from facetA
+    assumes facetA is visible from point
+
+  returns:
+    best facet, i.e., good facet that is furthest from point
+      distance to best facet
+      NULL if none
+
+    moves good, visible facets (and some other visible facets)
+      to end of qh->facet_list
+
+  notes:
+    uses qh->visit_id
+
+  design:
+    initialize bestfacet if facetA is good
+    move facetA to end of facetlist
+    for each facet on facetlist
+      for each unvisited neighbor of facet
+        move visible neighbors to end of facetlist
+        update best good neighbor
+        if no good neighbors, update best facet
+*/
+facetT *qh_findgooddist(qhT *qh, pointT *point, facetT *facetA, realT *distp,
+               facetT **facetlist) {
+  realT bestdist= -REALmax, dist;
+  facetT *neighbor, **neighborp, *bestfacet=NULL, *facet;
+  boolT goodseen= False;
+
+  if (facetA->good) {
+    zzinc_(Zcheckpart);  /* calls from check_bestdist occur after print stats */
+    qh_distplane(qh, point, facetA, &bestdist);
+    bestfacet= facetA;
+    goodseen= True;
+  }
+  qh_removefacet(qh, facetA);
+  qh_appendfacet(qh, facetA);
+  *facetlist= facetA;
+  facetA->visitid= ++qh->visit_id;
+  FORALLfacet_(*facetlist) {
+    FOREACHneighbor_(facet) {
+      if (neighbor->visitid == qh->visit_id)
+        continue;
+      neighbor->visitid= qh->visit_id;
+      if (goodseen && !neighbor->good)
+        continue;
+      zzinc_(Zcheckpart);
+      qh_distplane(qh, point, neighbor, &dist);
+      if (dist > 0) {
+        qh_removefacet(qh, neighbor);
+        qh_appendfacet(qh, neighbor);
+        if (neighbor->good) {
+          goodseen= True;
+          if (dist > bestdist) {
+            bestdist= dist;
+            bestfacet= neighbor;
+          }
+        }
+      }
+    }
+  }
+  if (bestfacet) {
+    *distp= bestdist;
+    trace2((qh, qh->ferr, 2003, "qh_findgooddist: p%d is %2.2g above good facet f%d\n",
+      qh_pointid(qh, point), bestdist, bestfacet->id));
+    return bestfacet;
+  }
+  trace4((qh, qh->ferr, 4011, "qh_findgooddist: no good facet for p%d above f%d\n",
+      qh_pointid(qh, point), facetA->id));
+  return NULL;
+}  /* findgooddist */
+
+/*-<a                             href="qh-geom_r.htm#TOC"
+  >-------------------------------</a><a name="furthestnewvertex">-</a>
+
+  qh_furthestnewvertex(qh, unvisited, facet, &maxdist )
+    return furthest unvisited, new vertex to a facet
+
+  return:
+    NULL if no vertex is above facet
+    maxdist to facet
+    updates v.visitid
+
+  notes:
+    Ignores vertices in facetB
+    Does not change qh.vertex_visit.  Use in conjunction with qh_furthestvertex
+*/
+vertexT *qh_furthestnewvertex(qhT *qh, unsigned int unvisited, facetT *facet, realT *maxdistp /* qh.newvertex_list */) {
+  vertexT *maxvertex= NULL, *vertex;
+  coordT dist, maxdist= 0.0;
+
+  FORALLvertex_(qh->newvertex_list) {
+    if (vertex->newfacet && vertex->visitid <= unvisited) {
+      vertex->visitid= qh->vertex_visit;
+      qh_distplane(qh, vertex->point, facet, &dist);
+      if (dist > maxdist) {
+        maxdist= dist;
+        maxvertex= vertex;
+      }
+    }
+  }
+  trace4((qh, qh->ferr, 4085, "qh_furthestnewvertex: v%d dist %2.2g is furthest new vertex for f%d\n",
+    getid_(maxvertex), maxdist, facet->id));
+  *maxdistp= maxdist;
+  return maxvertex;
+} /* furthestnewvertex */
+
+/*-<a                             href="qh-geom_r.htm#TOC"
+  >-------------------------------</a><a name="furthestvertex">-</a>
+
+  qh_furthestvertex(qh, facetA, facetB, &maxdist, &mindist )
+    return furthest vertex in facetA from facetB, or NULL if none
+
+  return:
+    maxdist and mindist to facetB or 0.0 if none
+    updates qh.vertex_visit
+
+  notes:
+    Ignores vertices in facetB
+*/
+vertexT *qh_furthestvertex(qhT *qh, facetT *facetA, facetT *facetB, realT *maxdistp, realT *mindistp) {
+  vertexT *maxvertex= NULL, *vertex, **vertexp;
+  coordT dist, maxdist= -REALmax, mindist= REALmax;
+
+  qh->vertex_visit++;
+  FOREACHvertex_(facetB->vertices)
+    vertex->visitid= qh->vertex_visit;
+  FOREACHvertex_(facetA->vertices) {
+    if (vertex->visitid != qh->vertex_visit) {
+      vertex->visitid= qh->vertex_visit;
+      zzinc_(Zvertextests);
+      qh_distplane(qh, vertex->point, facetB, &dist);
+      if (!maxvertex) {
+        maxdist= dist;
+        mindist= dist;
+        maxvertex= vertex;
+      }else if (dist > maxdist) {
+        maxdist= dist;
+        maxvertex= vertex;
+      }else if (dist < mindist)
+        mindist= dist;
+    }
+  }
+  if (!maxvertex) {
+    trace3((qh, qh->ferr, 3067, "qh_furthestvertex: all vertices of f%d are in f%d.  Returning 0.0 for max and mindist\n",
+      facetA->id, facetB->id));
+    maxdist= mindist= 0.0;
+  }else {
+    trace4((qh, qh->ferr, 4084, "qh_furthestvertex: v%d dist %2.2g is furthest (mindist %2.2g) of f%d above f%d\n",
+      maxvertex->id, maxdist, mindist, facetA->id, facetB->id));
+  }
+  *maxdistp= maxdist;
+  *mindistp= mindist;
+  return maxvertex;
+} /* furthestvertex */
+
+/*-<a                             href="qh-geom_r.htm#TOC"
+  >-------------------------------</a><a name="getarea">-</a>
+
+  qh_getarea(qh, facetlist )
+    set area of all facets in facetlist
+    collect statistics
+    nop if hasAreaVolume
+
+  returns:
+    sets qh->totarea/totvol to total area and volume of convex hull
+    for Delaunay triangulation, computes projected area of the lower or upper hull
+      ignores upper hull if qh->ATinfinity
+
+  notes:
+    could compute outer volume by expanding facet area by rays from interior
+    the following attempt at perpendicular projection underestimated badly:
+      qh.totoutvol += (-dist + facet->maxoutside + qh->DISTround)
+                            * area/ qh->hull_dim;
+  design:
+    for each facet on facetlist
+      compute facet->area
+      update qh.totarea and qh.totvol
+*/
+void qh_getarea(qhT *qh, facetT *facetlist) {
+  realT area;
+  realT dist;
+  facetT *facet;
+
+  if (qh->hasAreaVolume)
+    return;
+  if (qh->REPORTfreq)
+    qh_fprintf(qh, qh->ferr, 8020, "computing area of each facet and volume of the convex hull\n");
+  else
+    trace1((qh, qh->ferr, 1001, "qh_getarea: computing area for each facet and its volume to qh.interior_point (dist*area/dim)\n"));
+  qh->totarea= qh->totvol= 0.0;
+  FORALLfacet_(facetlist) {
+    if (!facet->normal)
+      continue;
+    if (facet->upperdelaunay && qh->ATinfinity)
+      continue;
+    if (!facet->isarea) {
+      facet->f.area= qh_facetarea(qh, facet);
+      facet->isarea= True;
+    }
+    area= facet->f.area;
+    if (qh->DELAUNAY) {
+      if (facet->upperdelaunay == qh->UPPERdelaunay)
+        qh->totarea += area;
+    }else {
+      qh->totarea += area;
+      qh_distplane(qh, qh->interior_point, facet, &dist);
+      qh->totvol += -dist * area/ qh->hull_dim;
+    }
+    if (qh->PRINTstatistics) {
+      wadd_(Wareatot, area);
+      wmax_(Wareamax, area);
+      wmin_(Wareamin, area);
+    }
+  }
+  qh->hasAreaVolume= True;
+} /* getarea */
+
+/*-<a                             href="qh-geom_r.htm#TOC"
+  >-------------------------------</a><a name="gram_schmidt">-</a>
+
+  qh_gram_schmidt(qh, dim, row )
+    implements Gram-Schmidt orthogonalization by rows
+
+  returns:
+    false if zero norm
+    overwrites rows[dim][dim]
+
+  notes:
+    see Golub & van Loan, 1983, Algorithm 6.2-2, "Modified Gram-Schmidt"
+    overflow due to small divisors not handled
+
+  design:
+    for each row
+      compute norm for row
+      if non-zero, normalize row
+      for each remaining rowA
+        compute inner product of row and rowA
+        reduce rowA by row * inner product
+*/
+boolT qh_gram_schmidt(qhT *qh, int dim, realT **row) {
+  realT *rowi, *rowj, norm;
+  int i, j, k;
+
+  for (i=0; i < dim; i++) {
+    rowi= row[i];
+    for (norm=0.0, k=dim; k--; rowi++)
+      norm += *rowi * *rowi;
+    norm= sqrt(norm);
+    wmin_(Wmindenom, norm);
+    if (norm == 0.0)  /* either 0 or overflow due to sqrt */
+      return False;
+    for (k=dim; k--; )
+      *(--rowi) /= norm;
+    for (j=i+1; j < dim; j++) {
+      rowj= row[j];
+      for (norm=0.0, k=dim; k--; )
+        norm += *rowi++ * *rowj++;
+      for (k=dim; k--; )
+        *(--rowj) -= *(--rowi) * norm;
+    }
+  }
+  return True;
+} /* gram_schmidt */
+
+
+/*-<a                             href="qh-geom_r.htm#TOC"
+  >-------------------------------</a><a name="inthresholds">-</a>
+
+  qh_inthresholds(qh, normal, angle )
+    return True if normal within qh.lower_/upper_threshold
+
+  returns:
+    estimate of angle by summing of threshold diffs
+      angle may be NULL
+      smaller "angle" is better
+
+  notes:
+    invalid if qh.SPLITthresholds
+
+  see:
+    qh.lower_threshold in qh_initbuild()
+    qh_initthresholds()
+
+  design:
+    for each dimension
+      test threshold
+*/
+boolT qh_inthresholds(qhT *qh, coordT *normal, realT *angle) {
+  boolT within= True;
+  int k;
+  realT threshold;
+
+  if (angle)
+    *angle= 0.0;
+  for (k=0; k < qh->hull_dim; k++) {
+    threshold= qh->lower_threshold[k];
+    if (threshold > -REALmax/2) {
+      if (normal[k] < threshold)
+        within= False;
+      if (angle) {
+        threshold -= normal[k];
+        *angle += fabs_(threshold);
+      }
+    }
+    if (qh->upper_threshold[k] < REALmax/2) {
+      threshold= qh->upper_threshold[k];
+      if (normal[k] > threshold)
+        within= False;
+      if (angle) {
+        threshold -= normal[k];
+        *angle += fabs_(threshold);
+      }
+    }
+  }
+  return within;
+} /* inthresholds */
+
+
+/*-<a                             href="qh-geom_r.htm#TOC"
+  >-------------------------------</a><a name="joggleinput">-</a>
+
+  qh_joggleinput(qh)
+    randomly joggle input to Qhull by qh.JOGGLEmax
+    initial input is qh.first_point/qh.num_points of qh.hull_dim
+      repeated calls use qh.input_points/qh.num_points
+
+  returns:
+    joggles points at qh.first_point/qh.num_points
+    copies data to qh.input_points/qh.input_malloc if first time
+    determines qh.JOGGLEmax if it was zero
+    if qh.DELAUNAY
+      computes the Delaunay projection of the joggled points
+
+  notes:
+    if qh.DELAUNAY, unnecessarily joggles the last coordinate
+    the initial 'QJn' may be set larger than qh_JOGGLEmaxincrease
+
+  design:
+    if qh.DELAUNAY
+      set qh.SCALElast for reduced precision errors
+    if first call
+      initialize qh.input_points to the original input points
+      if qh.JOGGLEmax == 0
+        determine default qh.JOGGLEmax
+    else
+      increase qh.JOGGLEmax according to qh.build_cnt
+    joggle the input by adding a random number in [-qh.JOGGLEmax,qh.JOGGLEmax]
+    if qh.DELAUNAY
+      sets the Delaunay projection
+*/
+void qh_joggleinput(qhT *qh) {
+  int i, seed, size;
+  coordT *coordp, *inputp;
+  realT randr, randa, randb;
+
+  if (!qh->input_points) { /* first call */
+    qh->input_points= qh->first_point;
+    qh->input_malloc= qh->POINTSmalloc;
+    size= qh->num_points * qh->hull_dim * (int)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 input error (qh_joggleinput): 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_ERRinput, 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 %4.4g with seed %d\n",
+    qh->JOGGLEmax, seed));
+  inputp= qh->input_points;
+  coordp= qh->first_point;
+  randa= 2.0 * qh->JOGGLEmax/qh_RANDOMmax;
+  randb= -qh->JOGGLEmax;
+  size= qh->num_points * qh->hull_dim;
+  for (i=size; i--; ) {
+    randr= qh_RANDOMint;
+    *(coordp++)= *(inputp++) + (randr * randa + randb);
+  }
+  if (qh->DELAUNAY) {
+    qh->last_low= qh->last_high= qh->last_newhigh= REALmax;
+    qh_setdelaunay(qh, qh->hull_dim, qh->num_points, qh->first_point);
+  }
+} /* joggleinput */
+
+/*-<a                             href="qh-geom_r.htm#TOC"
+  >-------------------------------</a><a name="maxabsval">-</a>
+
+  qh_maxabsval( normal, dim )
+    return pointer to maximum absolute value of a dim vector
+    returns NULL if dim=0
+*/
+realT *qh_maxabsval(realT *normal, int dim) {
+  realT maxval= -REALmax;
+  realT *maxp= NULL, *colp, absval;
+  int k;
+
+  for (k=dim, colp= normal; k--; colp++) {
+    absval= fabs_(*colp);
+    if (absval > maxval) {
+      maxval= absval;
+      maxp= colp;
+    }
+  }
+  return maxp;
+} /* maxabsval */
+
+
+/*-<a                             href="qh-geom_r.htm#TOC"
+  >-------------------------------</a><a name="maxmin">-</a>
+
+  qh_maxmin(qh, points, numpoints, dimension )
+    return max/min points for each dimension
+    determine max and min coordinates
+
+  returns:
+    returns a temporary set of max and min points
+      may include duplicate points. Does not include qh.GOODpoint
+    sets qh.NEARzero, qh.MAXabs_coord, qh.MAXsumcoord, qh.MAXwidth
+         qh.MAXlastcoord, qh.MINlastcoord
+    initializes qh.max_outside, qh.min_vertex, qh.WAScoplanar, qh.ZEROall_ok
+
+  notes:
+    loop duplicated in qh_detjoggle()
+
+  design:
+    initialize global precision variables
+    checks definition of REAL...
+    for each dimension
+      for each point
+        collect maximum and minimum point
+      collect maximum of maximums and minimum of minimums
+      determine qh.NEARzero for Gaussian Elimination
+*/
+setT *qh_maxmin(qhT *qh, pointT *points, int numpoints, int dimension) {
+  int k;
+  realT maxcoord, temp;
+  pointT *minimum, *maximum, *point, *pointtemp;
+  setT *set;
+
+  qh->max_outside= 0.0;
+  qh->MAXabs_coord= 0.0;
+  qh->MAXwidth= -REALmax;
+  qh->MAXsumcoord= 0.0;
+  qh->min_vertex= 0.0;
+  qh->WAScoplanar= False;
+  if (qh->ZEROcentrum)
+    qh->ZEROall_ok= True;
+  if (REALmin < REALepsilon && REALmin < REALmax && REALmin > -REALmax
+  && REALmax > 0.0 && -REALmax < 0.0)
+    ; /* all ok */
+  else {
+    qh_fprintf(qh, qh->ferr, 6011, "qhull error: one or more floating point constants in user_r.h are inconsistent. REALmin %g, -REALmax %g, 0.0, REALepsilon %g, REALmax %g\n",
+          REALmin, -REALmax, REALepsilon, REALmax);
+    qh_errexit(qh, qh_ERRinput, NULL, NULL);
+  }
+  set= qh_settemp(qh, 2*dimension);
+  trace1((qh, qh->ferr, 8082, "qh_maxmin: dim             min             max           width    nearzero  min-point  max-point\n"));
+  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->MAXabs_coord;
+    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, minimum);
+    qh_setappend(qh, &set, maximum);
+    /* 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;
+    trace1((qh, qh->ferr, 8106, "           %3d % 14.8e % 14.8e % 14.8e  %4.4e  p%-9d p%-d\n",
+            k, minimum[k], maximum[k], maximum[k]-minimum[k], qh->NEARzero[k], qh_pointid(qh, minimum), qh_pointid(qh, maximum)));
+    if (qh->SCALElast && k == dimension-1)
+      trace1((qh, qh->ferr, 8107, "           last coordinate scaled to (%4.4g, %4.4g), width %4.4e for option 'Qbb'\n",
+            qh->MAXabs_coord - qh->MAXwidth, qh->MAXabs_coord, qh->MAXwidth));
+  }
+  if (qh->IStracing >= 1)
+    qh_printpoints(qh, qh->ferr, "qh_maxmin: found the max and min points (by dim):", set);
+  return(set);
+} /* maxmin */
+
+/*-<a                             href="qh-geom_r.htm#TOC"
+  >-------------------------------</a><a name="maxouter">-</a>
+
+  qh_maxouter(qh)
+    return maximum distance from facet to outer plane
+    normally this is qh.max_outside+qh.DISTround
+    does not include qh.JOGGLEmax
+
+  see:
+    qh_outerinner()
+
+  notes:
+    need to add another qh.DISTround if testing actual point with computation
+    see qh_detmaxoutside for a qh_RATIO... target
+
+  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 %4.4g, qh.max_outside is %4.4g\n", dist, qh->max_outside));
+  return dist;
+} /* maxouter */
+
+/*-<a                             href="qh-geom_r.htm#TOC"
+  >-------------------------------</a><a name="maxsimplex">-</a>
+
+  qh_maxsimplex(qh, dim, maxpoints, points, numpoints, simplex )
+    determines maximum simplex for a set of points
+    maxpoints is the subset of points with a min or max coordinate
+    may start with points already in simplex
+    skips qh.GOODpointp (assumes that it isn't in maxpoints)
+
+  returns:
+    simplex with dim+1 points
+
+  notes:
+    called by qh_initialvertices, qh_detvnorm, and qh_voronoi_center
+    requires qh.MAXwidth to estimate determinate for each vertex
+    assumes at least needed 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)
+    create a simplex of dim+1 vertices as follows
+      add point from maxpoints that maximizes the determinate of the point and the simplex vertices  
+      if last point and maxdet/prevdet < qh_RATIOmaxsimplex (3.0e-2)
+        flag maybe_falsenarrow
+      if no maxpoint or maxnearzero or maybe_falsenarrow
+        search all points for maximum determinate
+        early exit if maybe_falsenarrow and !maxnearzero and maxdet > prevdet
+*/
+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, maybe_falsenarrow;
+  int i, sizinit;
+  realT maxdet= -1.0, prevdet= -1.0, det, mincoord= REALmax, maxcoord= -REALmax, mindet, ratio, targetdet;
+
+  if (qh->MAXwidth <= 0.0) {
+    qh_fprintf(qh, qh->ferr, 6421, "qhull internal error (qh_maxsimplex): qh.MAXwidth required for qh_maxsimplex.  Used to estimate determinate\n");
+    qh_errexit(qh, qh_ERRqhull, NULL, NULL);
+  }
+  sizinit= qh_setsize(qh, *simplex);
+  if (sizinit >= 2) {
+    maxdet= pow(qh->MAXwidth, sizinit - 1);
+  }else {
+    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;
+        }
+      }
+    }
+    maxdet= maxcoord - mincoord;
+    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_joggle_restart(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): %d points with the same x coordinate %4.4g\n",
+                 qh_setsize(qh, maxpoints)+numpoints, mincoord);
+        qh_errexit(qh, qh_ERRprec, NULL, NULL);
+      }else {
+        qh_fprintf(qh, qh->ferr, 6013, "qhull input error: input is less than %d-dimensional since all points have the same x coordinate %4.4g\n",
+                 qh->hull_dim, mincoord);
+        qh_errexit(qh, qh_ERRinput, NULL, NULL);
+      }
+    }
+  }
+  for (i=sizinit; i < dim+1; i++) {
+    prevdet= maxdet;
+    maxpoint= NULL;
+    maxdet= -1.0;
+    FOREACHpoint_(maxpoints) {
+      if (!qh_setin(*simplex, point) && point != maxpoint) {
+        det= qh_detsimplex(qh, point, *simplex, i, &nearzero); /* retests maxpoints if duplicate or multiple iterations */
+        if ((det= fabs_(det)) > maxdet) {
+          maxdet= det;
+          maxpoint= point;
+          maxnearzero= nearzero;
+        }
+      }
+    }
+    maybe_falsenarrow= False;
+    ratio= 1.0;
+    targetdet= prevdet * qh->MAXwidth;
+    mindet= 10 * qh_RATIOmaxsimplex * targetdet;
+    if (maxdet > 0.0) {
+      ratio= maxdet / targetdet;
+      if (ratio < qh_RATIOmaxsimplex)
+        maybe_falsenarrow= True;
+    }
+    if (!maxpoint || maxnearzero || maybe_falsenarrow) {
+      zinc_(Zsearchpoints);
+      if (!maxpoint) {
+        trace0((qh, qh->ferr, 7, "qh_maxsimplex: searching all points for %d-th initial vertex, better than mindet %4.4g, targetdet %4.4g\n",
+                i+1, mindet, targetdet));
+      }else if (qh->ALLpoints) {
+        trace0((qh, qh->ferr, 30, "qh_maxsimplex: searching all points ('Qs') for %d-th initial vertex, better than p%d det %4.4g, targetdet %4.4g, ratio %4.4g\n",
+                i+1, qh_pointid(qh, maxpoint), maxdet, targetdet, ratio));
+      }else if (maybe_falsenarrow) {
+        trace0((qh, qh->ferr, 17, "qh_maxsimplex: searching all points for %d-th initial vertex, better than p%d det %4.4g and mindet %4.4g, ratio %4.4g\n",
+                i+1, qh_pointid(qh, maxpoint), maxdet, mindet, ratio));
+      }else {
+        trace0((qh, qh->ferr, 8, "qh_maxsimplex: searching all points for %d-th initial vertex, better than p%d det %2.2g and mindet %4.4g, targetdet %4.4g\n",
+                i+1, qh_pointid(qh, maxpoint), maxdet, mindet, targetdet));
+      }
+      FORALLpoint_(qh, points, numpoints) {
+        if (point == qh->GOODpointp)
+          continue;
+        if (!qh_setin(maxpoints, point) && !qh_setin(*simplex, point)) {
+          det= qh_detsimplex(qh, point, *simplex, i, &nearzero);
+          if ((det= fabs_(det)) > maxdet) {
+            maxdet= det;
+            maxpoint= point;
+            maxnearzero= nearzero;
+            if (!maxnearzero && !qh->ALLpoints && maxdet > mindet)
+              break;
+          }
+        }
+      }
+    } /* !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=%4.4g, targetdet=%4.4g, mindet=%4.4g\n",
+            qh_pointid(qh, maxpoint), i+1, maxdet, prevdet * qh->MAXwidth, mindet));
+  } /* i */
+} /* maxsimplex */
+
+/*-<a                             href="qh-geom_r.htm#TOC"
+  >-------------------------------</a><a name="minabsval">-</a>
+
+  qh_minabsval( normal, dim )
+    return minimum absolute value of a dim vector
+*/
+realT qh_minabsval(realT *normal, int dim) {
+  realT minval= 0;
+  realT maxval= 0;
+  realT *colp;
+  int k;
+
+  for (k=dim, colp=normal; k--; colp++) {
+    maximize_(maxval, *colp);
+    minimize_(minval, *colp);
+  }
+  return fmax_(maxval, -minval);
+} /* minabsval */
+
+
+/*-<a                             href="qh-geom_r.htm#TOC"
+  >-------------------------------</a><a name="mindiff">-</a>
+
+  qh_mindif(qh, vecA, vecB, dim )
+    return index of min abs. difference of two vectors
+*/
+int qh_mindiff(realT *vecA, realT *vecB, int dim) {
+  realT mindiff= REALmax, diff;
+  realT *vecAp= vecA, *vecBp= vecB;
+  int k, mink= 0;
+
+  for (k=0; k < dim; k++) {
+    diff= *vecAp++ - *vecBp++;
+    diff= fabs_(diff);
+    if (diff < mindiff) {
+      mindiff= diff;
+      mink= k;
+    }
+  }
+  return mink;
+} /* mindiff */
+
+
+
+/*-<a                             href="qh-geom_r.htm#TOC"
+  >-------------------------------</a><a name="orientoutside">-</a>
+
+  qh_orientoutside(qh, facet  )
+    make facet outside oriented via qh.interior_point
+
+  returns:
+    True if facet reversed orientation.
+*/
+boolT qh_orientoutside(qhT *qh, facetT *facet) {
+  int k;
+  realT dist;
+
+  qh_distplane(qh, qh->interior_point, facet, &dist);
+  if (dist > 0) {
+    for (k=qh->hull_dim; k--; )
+      facet->normal[k]= -facet->normal[k];
+    facet->offset= -facet->offset;
+    return True;
+  }
+  return False;
+} /* orientoutside */
+
+/*-<a                             href="qh-geom_r.htm#TOC"
+  >-------------------------------</a><a name="outerinner">-</a>
+
+  qh_outerinner(qh, facet, outerplane, innerplane  )
+    if facet and qh.maxoutdone (i.e., qh_check_maxout)
+      returns outer and inner plane for facet
+    else
+      returns maximum outer and inner plane
+    accounts for qh.JOGGLEmax
+
+  see:
+    qh_maxouter(qh), qh_check_bestdist(), qh_check_points()
+
+  notes:
+    outerplaner or innerplane may be NULL
+    facet is const
+    Does not error (QhullFacet)
+
+    includes qh.DISTround for actual points
+    adds another qh.DISTround if testing with floating point arithmetic
+*/
+void qh_outerinner(qhT *qh, facetT *facet, realT *outerplane, realT *innerplane) {
+  realT dist, mindist;
+  vertexT *vertex, **vertexp;
+
+  if (outerplane) {
+    if (!qh_MAXoutside || !facet || !qh->maxoutdone) {
+      *outerplane= qh_maxouter(qh);       /* includes qh.DISTround */
+    }else { /* qh_MAXoutside ... */
+#if qh_MAXoutside
+      *outerplane= facet->maxoutside + qh->DISTround;
+#endif
+
+    }
+    if (qh->JOGGLEmax < REALmax/2)
+      *outerplane += qh->JOGGLEmax * sqrt((realT)qh->hull_dim);
+  }
+  if (innerplane) {
+    if (facet) {
+      mindist= REALmax;
+      FOREACHvertex_(facet->vertices) {
+        zinc_(Zdistio);
+        qh_distplane(qh, vertex->point, facet, &dist);
+        minimize_(mindist, dist);
+      }
+      *innerplane= mindist - qh->DISTround;
+    }else
+      *innerplane= qh->min_vertex - qh->DISTround;
+    if (qh->JOGGLEmax < REALmax/2)
+      *innerplane -= qh->JOGGLEmax * sqrt((realT)qh->hull_dim);
+  }
+} /* outerinner */
+
+/*-<a                             href="qh-geom_r.htm#TOC"
+  >-------------------------------</a><a name="pointdist">-</a>
+
+  qh_pointdist( point1, point2, dim )
+    return distance between two points
+
+  notes:
+    returns distance squared if 'dim' is negative
+*/
+coordT qh_pointdist(pointT *point1, pointT *point2, int dim) {
+  coordT dist, diff;
+  int k;
+
+  dist= 0.0;
+  for (k= (dim > 0 ? dim : -dim); k--; ) {
+    diff= *point1++ - *point2++;
+    dist += diff * diff;
+  }
+  if (dim > 0)
+    return(sqrt(dist));
+  return dist;
+} /* pointdist */
+
+
+/*-<a                             href="qh-geom_r.htm#TOC"
+  >-------------------------------</a><a name="printmatrix">-</a>
+
+  qh_printmatrix(qh, fp, string, rows, numrow, numcol )
+    print matrix to fp given by row vectors
+    print string as header
+    qh may be NULL if fp is defined
+
+  notes:
+    print a vector by qh_printmatrix(qh, fp, "", &vect, 1, len)
+*/
+void qh_printmatrix(qhT *qh, FILE *fp, const char *string, realT **rows, int numrow, int numcol) {
+  realT *rowp;
+  realT r; /*bug fix*/
+  int i,k;
+
+  qh_fprintf(qh, fp, 9001, "%s\n", string);
+  for (i=0; i < numrow; i++) {
+    rowp= rows[i];
+    for (k=0; k < numcol; k++) {
+      r= *rowp++;
+      qh_fprintf(qh, fp, 9002, "%6.3g ", r);
+    }
+    qh_fprintf(qh, fp, 9003, "\n");
+  }
+} /* printmatrix */
+
+
+/*-<a                             href="qh-geom_r.htm#TOC"
+  >-------------------------------</a><a name="printpoints">-</a>
+
+  qh_printpoints(qh, fp, string, points )
+    print pointids to fp for a set of points
+    if string, prints string and 'p' point ids
+*/
+void qh_printpoints(qhT *qh, FILE *fp, const char *string, setT *points) {
+  pointT *point, **pointp;
+
+  if (string) {
+    qh_fprintf(qh, fp, 9004, "%s", string);
+    FOREACHpoint_(points)
+      qh_fprintf(qh, fp, 9005, " p%d", qh_pointid(qh, point));
+    qh_fprintf(qh, fp, 9006, "\n");
+  }else {
+    FOREACHpoint_(points)
+      qh_fprintf(qh, fp, 9007, " %d", qh_pointid(qh, point));
+    qh_fprintf(qh, fp, 9008, "\n");
+  }
+} /* printpoints */
+
+
+/*-<a                             href="qh-geom_r.htm#TOC"
+  >-------------------------------</a><a name="projectinput">-</a>
+
+  qh_projectinput(qh)
+    project input points using qh.lower_bound/upper_bound and qh->DELAUNAY
+    if qh.lower_bound[k]=qh.upper_bound[k]= 0,
+      removes dimension k
+    if halfspace intersection
+      removes dimension k from qh.feasible_point
+    input points in qh->first_point, num_points, input_dim
+
+  returns:
+    new point array in qh->first_point of qh->hull_dim coordinates
+    sets qh->POINTSmalloc
+    if qh->DELAUNAY
+      projects points to paraboloid
+      lowbound/highbound is also projected
+    if qh->ATinfinity
+      adds point "at-infinity"
+    if qh->POINTSmalloc
+      frees old point array
+
+  notes:
+    checks that qh.hull_dim agrees with qh.input_dim, PROJECTinput, and DELAUNAY
+
+
+  design:
+    sets project[k] to -1 (delete), 0 (keep), 1 (add for Delaunay)
+    determines newdim and newnum for qh->hull_dim and qh->num_points
+    projects points to newpoints
+    projects qh.lower_bound to itself
+    projects qh.upper_bound to itself
+    if qh->DELAUNAY
+      if qh->ATINFINITY
+        projects points to paraboloid
+        computes "infinity" point as vertex average and 10% above all points
+      else
+        uses qh_setdelaunay to project points to paraboloid
+*/
+void qh_projectinput(qhT *qh) {
+  int k,i;
+  int newdim= qh->input_dim, newnum= qh->num_points;
+  signed char *project;
+  int projectsize= (qh->input_dim + 1) * (int)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.0 && qh->upper_bound[k] == 0.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((size_t)(newnum * newdim) * sizeof(coordT)))) {
+    qh_memfree(qh, project, projectsize);
+    qh_fprintf(qh, qh->ferr, 6016, "qhull error: insufficient memory to project %d points\n",
+           qh->num_points);
+    qh_errexit(qh, qh_ERRmem, NULL, NULL);
+  }
+  /* qh_projectpoints throws error if mismatched dimensions */
+  qh_projectpoints(qh, project, qh->input_dim+1, qh->first_point,
+                    qh->num_points, qh->input_dim, newpoints, newdim);
+  trace1((qh, qh->ferr, 1003, "qh_projectinput: updating lower and upper_bound\n"));
+  qh_projectpoints(qh, project, qh->input_dim+1, qh->lower_bound,
+                    1, qh->input_dim+1, qh->lower_bound, newdim+1);
+  qh_projectpoints(qh, project, qh->input_dim+1, qh->upper_bound,
+                    1, qh->input_dim+1, qh->upper_bound, newdim+1);
+  if (qh->HALFspace) {
+    if (!qh->feasible_point) {
+      qh_memfree(qh, project, projectsize);
+      qh_fprintf(qh, qh->ferr, 6017, "qhull internal error (qh_projectinput): HALFspace defined without qh.feasible_point\n");
+      qh_errexit(qh, qh_ERRqhull, NULL, NULL);
+    }
+    qh_projectpoints(qh, project, qh->input_dim, qh->feasible_point,
+                      1, qh->input_dim, qh->feasible_point, newdim);
+  }
+  qh_memfree(qh, project, projectsize);
+  if (qh->POINTSmalloc)
+    qh_free(qh->first_point);
+  qh->first_point= newpoints;
+  qh->POINTSmalloc= True;
+  qh->temp_malloc= NULL;
+  if (qh->DELAUNAY && qh->ATinfinity) {
+    coord= qh->first_point;
+    infinity= qh->first_point + qh->hull_dim * qh->num_points;
+    for (k=qh->hull_dim-1; k--; )
+      infinity[k]= 0.0;
+    for (i=qh->num_points; i--; ) {
+      paraboloid= 0.0;
+      for (k=0; k < qh->hull_dim-1; k++) {
+        paraboloid += *coord * *coord;
+        infinity[k] += *coord;
+        coord++;
+      }
+      *(coord++)= paraboloid;
+      maximize_(maxboloid, paraboloid);
+    }
+    /* coord == infinity */
+    for (k=qh->hull_dim-1; k--; )
+      *(coord++) /= qh->num_points;
+    *(coord++)= maxboloid * 1.1;
+    qh->num_points++;
+    trace0((qh, qh->ferr, 9, "qh_projectinput: projected points to paraboloid for Delaunay\n"));
+  }else if (qh->DELAUNAY)  /* !qh->ATinfinity */
+    qh_setdelaunay(qh, qh->hull_dim, qh->num_points, qh->first_point);
+} /* projectinput */
+
+
+/*-<a                             href="qh-geom_r.htm#TOC"
+  >-------------------------------</a><a name="projectpoints">-</a>
+
+  qh_projectpoints(qh, project, n, points, numpoints, dim, newpoints, newdim )
+    project points/numpoints/dim to newpoints/newdim
+    if project[k] == -1
+      delete dimension k
+    if project[k] == 1
+      add dimension k by duplicating previous column
+    n is size of project
+
+  notes:
+    newpoints may be points if only adding dimension at end
+
+  design:
+    check that 'project' and 'newdim' agree
+    for each dimension
+      if project == -1
+        skip dimension
+      else
+        determine start of column in newpoints
+        determine start of column in points
+          if project == +1, duplicate previous column
+        copy dimension (column) from points to newpoints
+*/
+void qh_projectpoints(qhT *qh, signed char *project, int n, realT *points,
+        int numpoints, int dim, realT *newpoints, int newdim) {
+  int testdim= dim, oldk=0, newk=0, i,j=0,k;
+  realT *newp, *oldp;
+
+  for (k=0; k < n; k++)
+    testdim += project[k];
+  if (testdim != newdim) {
+    qh_fprintf(qh, qh->ferr, 6018, "qhull internal error (qh_projectpoints): newdim %d should be %d after projection\n",
+      newdim, testdim);
+    qh_errexit(qh, qh_ERRqhull, NULL, NULL);
+  }
+  for (j=0; j<n; j++) {
+    if (project[j] == -1)
+      oldk++;
+    else {
+      newp= newpoints+newk++;
+      if (project[j] == +1) {
+        if (oldk >= dim)
+          continue;
+        oldp= points+oldk;
+      }else
+        oldp= points+oldk++;
+      for (i=numpoints; i--; ) {
+        *newp= *oldp;
+        newp += newdim;
+        oldp += dim;
+      }
+    }
+    if (oldk >= dim)
+      break;
+  }
+  trace1((qh, qh->ferr, 1004, "qh_projectpoints: projected %d points from dim %d to dim %d\n",
+    numpoints, dim, newdim));
+} /* projectpoints */
+
+
+/*-<a                             href="qh-geom_r.htm#TOC"
+  >-------------------------------</a><a name="rotateinput">-</a>
+
+  qh_rotateinput(qh, rows )
+    rotate input using row matrix
+    input points given by qh->first_point, num_points, hull_dim
+    assumes rows[dim] is a scratch buffer
+    if qh->POINTSmalloc, overwrites input points, else mallocs a new array
+
+  returns:
+    rotated input
+    sets qh->POINTSmalloc
+
+  design:
+    see qh_rotatepoints
+*/
+void qh_rotateinput(qhT *qh, realT **rows) {
+
+  if (!qh->POINTSmalloc) {
+    qh->first_point= qh_copypoints(qh, qh->first_point, qh->num_points, qh->hull_dim);
+    qh->POINTSmalloc= True;
+  }
+  qh_rotatepoints(qh, qh->first_point, qh->num_points, qh->hull_dim, rows);
+}  /* rotateinput */
+
+/*-<a                             href="qh-geom_r.htm#TOC"
+  >-------------------------------</a><a name="rotatepoints">-</a>
+
+  qh_rotatepoints(qh, points, numpoints, dim, row )
+    rotate numpoints points by a d-dim row matrix
+    assumes rows[dim] is a scratch buffer
+
+  returns:
+    rotated points in place
+
+  design:
+    for each point
+      for each coordinate
+        use row[dim] to compute partial inner product
+      for each coordinate
+        rotate by partial inner product
+*/
+void qh_rotatepoints(qhT *qh, realT *points, int numpoints, int dim, realT **row) {
+  realT *point, *rowi, *coord= NULL, sum, *newval;
+  int i,j,k;
+
+  if (qh->IStracing >= 1)
+    qh_printmatrix(qh, qh->ferr, "qh_rotatepoints: rotate points by", row, dim, dim);
+  for (point=points, j=numpoints; j--; point += dim) {
+    newval= row[dim];
+    for (i=0; i < dim; i++) {
+      rowi= row[i];
+      coord= point;
+      for (sum=0.0, k=dim; k--; )
+        sum += *rowi++ * *coord++;
+      *(newval++)= sum;
+    }
+    for (k=dim; k--; )
+      *(--coord)= *(--newval);
+  }
+} /* rotatepoints */
+
+
+/*-<a                             href="qh-geom_r.htm#TOC"
+  >-------------------------------</a><a name="scaleinput">-</a>
+
+  qh_scaleinput(qh)
+    scale input points using qh->low_bound/high_bound
+    input points given by qh->first_point, num_points, hull_dim
+    if qh->POINTSmalloc, overwrites input points, else mallocs a new array
+
+  returns:
+    scales coordinates of points to low_bound[k], high_bound[k]
+    sets qh->POINTSmalloc
+
+  design:
+    see qh_scalepoints
+*/
+void qh_scaleinput(qhT *qh) {
+
+  if (!qh->POINTSmalloc) {
+    qh->first_point= qh_copypoints(qh, qh->first_point, qh->num_points, qh->hull_dim);
+    qh->POINTSmalloc= True;
+  }
+  qh_scalepoints(qh, qh->first_point, qh->num_points, qh->hull_dim,
+       qh->lower_bound, qh->upper_bound);
+}  /* scaleinput */
+
+/*-<a                             href="qh-geom_r.htm#TOC"
+  >-------------------------------</a><a name="scalelast">-</a>
+
+  qh_scalelast(qh, points, numpoints, dim, low, high, newhigh )
+    scale last coordinate to [0.0, newhigh], for Delaunay triangulation
+    input points given by points, numpoints, dim
+
+  returns:
+    changes scale of last coordinate from [low, high] to [0.0, newhigh]
+    overwrites last coordinate of each point
+    saves low/high/newhigh in qh.last_low, etc. for qh_setdelaunay()
+
+  notes:
+    to reduce precision issues, qh_scalelast makes the last coordinate similar to other coordinates
+      the last coordinate for Delaunay triangulation is the sum of squares of input coordinates
+      note that the range [0.0, newwidth] is wrong for narrow distributions with large positive coordinates (e.g., [995933.64, 995963.48])
+
+    when called by qh_setdelaunay, low/high may not match the data passed to qh_setdelaunay
+
+  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, newlow;
+  int i;
+  boolT nearzero= False;
+
+  newlow= 0.0;
+  trace4((qh, qh->ferr, 4013, "qh_scalelast: scale last coordinate from [%2.2g, %2.2g] to [%2.2g, %2.2g]\n",
+    low, high, newlow, newhigh));
+  qh->last_low= low;
+  qh->last_high= high;
+  qh->last_newhigh= newhigh;
+  scale= qh_divzero(newhigh - newlow, high - low,
+                  qh->MINdenom_1, &nearzero);
+  if (nearzero) {
+    if (qh->DELAUNAY)
+      qh_fprintf(qh, qh->ferr, 6019, "qhull input error (qh_scalelast): can not scale last coordinate to [%4.4g, %4.4g].  Input is cocircular or cospherical.   Use option 'Qz' to add a point at infinity.\n",
+             newlow, newhigh);
+    else
+      qh_fprintf(qh, qh->ferr, 6020, "qhull input error (qh_scalelast): can not scale last coordinate to [%4.4g, %4.4g].  New bounds are too wide for compared to existing bounds [%4.4g, %4.4g] (width %4.4g)\n",
+             newlow, newhigh, low, high, high-low);
+    qh_errexit(qh, qh_ERRinput, NULL, NULL);
+  }
+  shift= newlow - low * scale;
+  coord= points + dim - 1;
+  for (i=numpoints; i--; coord += dim)
+    *coord= *coord * scale + shift;
+} /* scalelast */
+
+/*-<a                             href="qh-geom_r.htm#TOC"
+  >-------------------------------</a><a name="scalepoints">-</a>
+
+  qh_scalepoints(qh, points, numpoints, dim, newlows, newhighs )
+    scale points to new lowbound and highbound
+    retains old bound when newlow= -REALmax or newhigh= +REALmax
+
+  returns:
+    scaled points
+    overwrites old points
+
+  design:
+    for each coordinate
+      compute current low and high bound
+      compute scale and shift factors
+      scale all points
+      enforce new low and high bound for all points
+*/
+void qh_scalepoints(qhT *qh, pointT *points, int numpoints, int dim,
+        realT *newlows, realT *newhighs) {
+  int i,k;
+  realT shift, scale, *coord, low, high, newlow, newhigh, mincoord, maxcoord;
+  boolT nearzero= False;
+
+  for (k=0; k < dim; k++) {
+    newhigh= newhighs[k];
+    newlow= newlows[k];
+    if (newhigh > REALmax/2 && newlow < -REALmax/2)
+      continue;
+    low= REALmax;
+    high= -REALmax;
+    for (i=numpoints, coord=points+k; i--; coord += dim) {
+      minimize_(low, *coord);
+      maximize_(high, *coord);
+    }
+    if (newhigh > REALmax/2)
+      newhigh= high;
+    if (newlow < -REALmax/2)
+      newlow= low;
+    if (qh->DELAUNAY && k == dim-1 && newhigh < newlow) {
+      qh_fprintf(qh, qh->ferr, 6021, "qhull input error: 'Qb%d' or 'QB%d' inverts paraboloid since high bound %.2g < low bound %.2g\n",
+               k, k, newhigh, newlow);
+      qh_errexit(qh, qh_ERRinput, NULL, NULL);
+    }
+    scale= qh_divzero(newhigh - newlow, high - low,
+                  qh->MINdenom_1, &nearzero);
+    if (nearzero) {
+      qh_fprintf(qh, qh->ferr, 6022, "qhull input error: %d'th dimension's new bounds [%2.2g, %2.2g] too wide for\nexisting bounds [%2.2g, %2.2g]\n",
+              k, newlow, newhigh, low, high);
+      qh_errexit(qh, qh_ERRinput, NULL, NULL);
+    }
+    shift= (newlow * high - low * newhigh)/(high-low);
+    coord= points+k;
+    for (i=numpoints; i--; coord += dim)
+      *coord= *coord * scale + shift;
+    coord= points+k;
+    if (newlow < newhigh) {
+      mincoord= newlow;
+      maxcoord= newhigh;
+    }else {
+      mincoord= newhigh;
+      maxcoord= newlow;
+    }
+    for (i=numpoints; i--; coord += dim) {
+      minimize_(*coord, maxcoord);  /* because of roundoff error */
+      maximize_(*coord, mincoord);
+    }
+    trace0((qh, qh->ferr, 10, "qh_scalepoints: scaled %d'th coordinate [%2.2g, %2.2g] to [%.2g, %.2g] for %d points by %2.2g and shifted %2.2g\n",
+      k, low, high, newlow, newhigh, numpoints, scale, shift));
+  }
+} /* scalepoints */
+
+
+/*-<a                             href="qh-geom_r.htm#TOC"
+  >-------------------------------</a><a name="setdelaunay">-</a>
+
+  qh_setdelaunay(qh, dim, count, points )
+    project count points to dim-d paraboloid for Delaunay triangulation
+
+    dim is one more than the dimension of the input set
+    assumes dim is at least 3 (i.e., at least a 2-d Delaunay triangulation)
+
+    points is a dim*count realT array.  The first dim-1 coordinates
+    are the coordinates of the first input point.  array[dim] is
+    the first coordinate of the second input point.  array[2*dim] is
+    the first coordinate of the third input point.
+
+    if qh.last_low defined (i.e., 'Qbb' called qh_scalelast)
+      calls qh_scalelast to scale the last coordinate the same as the other points
+
+  returns:
+    for each point
+      sets point[dim-1] to sum of squares of coordinates
+    scale points to 'Qbb' if needed
+
+  notes:
+    to project one point, use
+      qh_setdelaunay(qh, qh->hull_dim, 1, point)
+
+    Do not use options 'Qbk', 'QBk', or 'QbB' since they scale
+    the coordinates after the original projection.
+
+*/
+void qh_setdelaunay(qhT *qh, int dim, int count, pointT *points) {
+  int i, k;
+  coordT *coordp, coord;
+  realT paraboloid;
+
+  trace0((qh, qh->ferr, 11, "qh_setdelaunay: project %d points to paraboloid for Delaunay triangulation\n", count));
+  coordp= points;
+  for (i=0; i < count; i++) {
+    coord= *coordp++;
+    paraboloid= coord*coord;
+    for (k=dim-2; k--; ) {
+      coord= *coordp++;
+      paraboloid += coord*coord;
+    }
+    *coordp++= paraboloid;
+  }
+  if (qh->last_low < REALmax/2)
+    qh_scalelast(qh, points, count, dim, qh->last_low, qh->last_high, qh->last_newhigh);
+} /* setdelaunay */
+
+
+/*-<a                             href="qh-geom_r.htm#TOC"
+  >-------------------------------</a><a name="sethalfspace">-</a>
+
+  qh_sethalfspace(qh, dim, coords, nextp, normal, offset, feasible )
+    set point to dual of halfspace relative to feasible point
+    halfspace is normal coefficients and offset.
+
+  returns:
+    false and prints error if feasible point is outside of hull
+    overwrites coordinates for point at dim coords
+    nextp= next point (coords)
+    does not call qh_errexit
+
+  design:
+    compute distance from feasible point to halfspace
+    divide each normal coefficient by -dist
+*/
+boolT qh_sethalfspace(qhT *qh, int dim, coordT *coords, coordT **nextp,
+         coordT *normal, coordT *offset, coordT *feasible) {
+  coordT *normp= normal, *feasiblep= feasible, *coordp= coords;
+  realT dist;
+  realT r; /*bug fix*/
+  int k;
+  boolT zerodiv;
+
+  dist= *offset;
+  for (k=dim; k--; )
+    dist += *(normp++) * *(feasiblep++);
+  if (dist > 0)
+    goto LABELerroroutside;
+  normp= normal;
+  if (dist < -qh->MINdenom) {
+    for (k=dim; k--; )
+      *(coordp++)= *(normp++) / -dist;
+  }else {
+    for (k=dim; k--; ) {
+      *(coordp++)= qh_divzero(*(normp++), -dist, qh->MINdenom_1, &zerodiv);
+      if (zerodiv)
+        goto LABELerroroutside;
+    }
+  }
+  *nextp= coordp;
+#ifndef qh_NOtrace
+  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");
+  }
+#endif
+  return True;
+LABELerroroutside:
+  feasiblep= feasible;
+  normp= normal;
+  qh_fprintf(qh, qh->ferr, 6023, "qhull input error: feasible point is not clearly inside halfspace\nfeasible point: ");
+  for (k=dim; k--; )
+    qh_fprintf(qh, qh->ferr, 8024, qh_REAL_1, r=*(feasiblep++));
+  qh_fprintf(qh, qh->ferr, 8025, "\n     halfspace: ");
+  for (k=dim; k--; )
+    qh_fprintf(qh, qh->ferr, 8026, qh_REAL_1, r=*(normp++));
+  qh_fprintf(qh, qh->ferr, 8027, "\n     at offset: ");
+  qh_fprintf(qh, qh->ferr, 8028, qh_REAL_1, *offset);
+  qh_fprintf(qh, qh->ferr, 8029, " and distance: ");
+  qh_fprintf(qh, qh->ferr, 8030, qh_REAL_1, dist);
+  qh_fprintf(qh, qh->ferr, 8031, "\n");
+  return False;
+} /* sethalfspace */
+
+/*-<a                             href="qh-geom_r.htm#TOC"
+  >-------------------------------</a><a name="sethalfspace_all">-</a>
+
+  qh_sethalfspace_all(qh, dim, count, halfspaces, feasible )
+    generate dual for halfspace intersection with feasible point
+    array of count halfspaces
+      each halfspace is normal coefficients followed by offset
+      the origin is inside the halfspace if the offset is negative
+    feasible is a point inside all halfspaces (http://www.qhull.org/html/qhalf.htm#notes)
+
+  returns:
+    malloc'd array of count X dim-1 points
+
+  notes:
+    call before qh_init_B or qh_initqhull_globals
+    free memory when done
+    unused/untested code: please email bradb@shore.net if this works ok for you
+    if using option 'Fp', qh.feasible_point must be set (e.g., to 'feasible')
+    qh->feasible_point is a malloc'd array that is freed by qh_freebuffers.
+
+  design:
+    see qh_sethalfspace
+*/
+coordT *qh_sethalfspace_all(qhT *qh, int dim, int count, coordT *halfspaces, pointT *feasible) {
+  int i, newdim;
+  pointT *newpoints;
+  coordT *coordp, *normalp, *offsetp;
+
+  trace0((qh, qh->ferr, 12, "qh_sethalfspace_all: compute dual for halfspace intersection\n"));
+  newdim= dim - 1;
+  if (!(newpoints= (coordT *)qh_malloc((size_t)(count * newdim) * sizeof(coordT)))){
+    qh_fprintf(qh, qh->ferr, 6024, "qhull error: insufficient memory to compute dual of %d halfspaces\n",
+          count);
+    qh_errexit(qh, qh_ERRmem, NULL, NULL);
+  }
+  coordp= newpoints;
+  normalp= halfspaces;
+  for (i=0; i < count; i++) {
+    offsetp= normalp + newdim;
+    if (!qh_sethalfspace(qh, newdim, coordp, &coordp, normalp, offsetp, feasible)) {
+      qh_free(newpoints);  /* feasible is not inside halfspace as reported by qh_sethalfspace */
+      qh_fprintf(qh, qh->ferr, 8032, "The halfspace was at index %d\n", i);
+      qh_errexit(qh, qh_ERRinput, NULL, NULL);
+    }
+    normalp= offsetp + 1;
+  }
+  return newpoints;
+} /* sethalfspace_all */
+
+
+/*-<a                             href="qh-geom_r.htm#TOC"
+  >-------------------------------</a><a name="sharpnewfacets">-</a>
+
+  qh_sharpnewfacets(qh)
+
+  returns:
+    true if could be an acute angle (facets in different quadrants)
+
+  notes:
+    for qh_findbest
+
+  design:
+    for all facets on qh.newfacet_list
+      if two facets are in different quadrants
+        set issharp
+*/
+boolT qh_sharpnewfacets(qhT *qh) {
+  facetT *facet;
+  boolT issharp= False;
+  int *quadrant, k;
+
+  quadrant= (int *)qh_memalloc(qh, qh->hull_dim * (int)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 * (int)sizeof(int));
+  trace3((qh, qh->ferr, 3001, "qh_sharpnewfacets: %d\n", issharp));
+  return issharp;
+} /* sharpnewfacets */
+
+/*-<a                             href="qh-geom_r.htm#TOC"
+  >-------------------------------</a><a name="vertex_bestdist">-</a>
+
+  qh_vertex_bestdist(qh, vertices )
+  qh_vertex_bestdist2(qh, vertices, vertexp, vertexp2 )
+    return nearest distance between vertices
+    optionally returns vertex and vertex2
+
+  notes:
+    called by qh_partitioncoplanar, qh_mergefacet, qh_check_maxout, qh_checkpoint
+*/
+coordT qh_vertex_bestdist(qhT *qh, setT *vertices) {
+  vertexT *vertex, *vertex2;
+
+  return qh_vertex_bestdist2(qh, vertices, &vertex, &vertex2);
+} /* vertex_bestdist */
+
+coordT qh_vertex_bestdist2(qhT *qh, setT *vertices, vertexT **vertexp/*= NULL*/, vertexT **vertexp2/*= NULL*/) {
+  vertexT *vertex, *vertexA, *bestvertex= NULL, *bestvertex2= NULL;
+  coordT dist, bestdist= REALmax;
+  int k, vertex_i, vertex_n;
+
+  FOREACHvertex_i_(qh, vertices) {
+    for (k= vertex_i+1; k < vertex_n; k++) {
+      vertexA= SETelemt_(vertices, k, vertexT);
+      dist= qh_pointdist(vertex->point, vertexA->point, -qh->hull_dim);
+      if (dist < bestdist) {
+        bestdist= dist;
+        bestvertex= vertex;
+        bestvertex2= vertexA;
+      }
+    }
+  }
+  *vertexp= bestvertex;
+  *vertexp2= bestvertex2;
+  return sqrt(bestdist);
+} /* vertex_bestdist */
+
+/*-<a                             href="qh-geom_r.htm#TOC"
+  >-------------------------------</a><a name="voronoi_center">-</a>
+
+  qh_voronoi_center(qh, dim, points )
+    return Voronoi center for a set of points
+    dim is the orginal dimension of the points
+    gh.gm_matrix/qh.gm_row are scratch buffers
+
+  returns:
+    center as a temporary point (qh_memalloc)
+    if non-simplicial,
+      returns center for max simplex of points
+
+  notes:
+    only called by qh_facetcenter
+    from Bowyer & Woodwark, A Programmer's Geometry, 1983, p. 65
+
+  design:
+    if non-simplicial
+      determine max simplex for points
+    translate point0 of simplex to origin
+    compute sum of squares of diagonal
+    compute determinate
+    compute Voronoi center (see Bowyer & Woodwark)
+*/
+pointT *qh_voronoi_center(qhT *qh, int dim, setT *points) {
+  pointT *point, **pointp, *point0;
+  pointT *center= (pointT *)qh_memalloc(qh, qh->center_size);
+  setT *simplex;
+  int i, j, k, size= qh_setsize(qh, points);
+  coordT *gmcoord;
+  realT *diffp, sum2, *sum2row, *sum2p, det, factor;
+  boolT nearzero, infinite;
+
+  if (size == dim+1)
+    simplex= points;
+  else if (size < dim+1) {
+    qh_memfree(qh, center, qh->center_size);
+    qh_fprintf(qh, qh->ferr, 6025, "qhull internal error (qh_voronoi_center):  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, 3061, "qh_voronoi_center: det %2.2g factor %2.2g ", det, factor);
+      qh_printmatrix(qh, qh->ferr, "center:", &center, 1, dim);
+      if (qh->IStracing >= 5) {
+        qh_printpoints(qh, qh->ferr, "points", simplex);
+        FOREACHpoint_(simplex)
+          qh_fprintf(qh, qh->ferr, 8034, "p%d dist %.2g, ", qh_pointid(qh, point),
+                   qh_pointdist(point, center, dim));
+        qh_fprintf(qh, qh->ferr, 8035, "\n");
+      }
+    }
+#endif
+  }
+  if (simplex != points)
+    qh_settempfree(qh, &simplex);
+  return center;
+} /* voronoi_center */
+

+ 1284 - 0
contrib/libs/qhull/libqhull_r/geom_r.c

@@ -0,0 +1,1284 @@
+/*<html><pre>  -<a                             href="qh-geom_r.htm"
+  >-------------------------------</a><a name="TOP">-</a>
+
+   geom_r.c
+   geometric routines of qhull
+
+   see qh-geom_r.htm and geom_r.h
+
+   Copyright (c) 1993-2020 The Geometry Center.
+   $Id: //main/2019/qhull/src/libqhull_r/geom_r.c#5 $$Change: 2953 $
+   $DateTime: 2020/05/21 22:05:32 $$Author: bbarber $
+
+   infrequent code goes into geom2_r.c
+*/
+
+#include "qhull_ra.h"
+
+/*-<a                             href="qh-geom_r.htm#TOC"
+  >-------------------------------</a><a name="distplane">-</a>
+
+  qh_distplane(qh, point, facet, dist )
+    return distance from point to facet
+
+  returns:
+    dist
+    if qh.RANDOMdist, joggles result
+
+  notes:
+    dist > 0 if point is above facet (i.e., outside)
+    does not error (for qh_sortfacets, qh_outerinner)
+    for nearly coplanar points, the returned values may be duplicates
+      for example pairs of nearly incident points, rbox 175 C1,2e-13 t1538759579 | qhull d T4
+      622 qh_distplane: e-014  # count of two or more duplicate values for unique calls
+      258 qh_distplane: e-015
+      38 qh_distplane: e-016
+      40 qh_distplane: e-017
+      6 qh_distplane: e-018
+      5 qh_distplane: -e-018
+      33 qh_distplane: -e-017
+         3153 qh_distplane: -2.775557561562891e-017  # duplicated value for 3153 unique calls
+      42 qh_distplane: -e-016
+      307 qh_distplane: -e-015
+      1271 qh_distplane: -e-014
+      13 qh_distplane: -e-013
+
+  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;
+  }
+  zzinc_(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;
+  }
+#ifndef qh_NOtrace
+  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);
+  }
+#endif
+  return;
+} /* distplane */
+
+
+/*-<a                             href="qh-geom_r.htm#TOC"
+  >-------------------------------</a><a name="findbest">-</a>
+
+  qh_findbest(qh, point, startfacet, bestoutside, qh_ISnewfacets, qh_NOupper, dist, isoutside, numpart )
+    find facet that is furthest below a point
+    for upperDelaunay facets
+      returns facet only if !qh_NOupper and clearly above
+
+  input:
+    starts search at 'startfacet' (can not be flipped)
+    if !bestoutside(qh_ALL), stops at qh.MINoutside
+
+  returns:
+    best facet (reports error if NULL)
+    early out if isoutside defined and bestdist > qh.MINoutside
+    dist is distance to facet
+    isoutside is true if point is outside of facet
+    numpart counts the number of distance tests
+
+  see also:
+    qh_findbestnew()
+
+  notes:
+    If merging (testhorizon), searches horizon facets of coplanar best facets because
+    after qh_distplane, this and qh_partitionpoint are the most expensive in 3-d
+      avoid calls to distplane, function calls, and real number operations.
+    caller traces result
+    Optimized for outside points.   Tried recording a search set for qh_findhorizon.
+    Made code more complicated.
+
+  when called by qh_partitionvisible():
+    indicated by qh_ISnewfacets
+    qh.newfacet_list is list of simplicial, new facets
+    qh_findbestnew set if qh_sharpnewfacets returns True (to use qh_findbestnew)
+    qh.bestfacet_notsharp set if qh_sharpnewfacets returns False
+
+  when called by qh_findfacet(), qh_partitionpoint(), qh_partitioncoplanar(),
+                 qh_check_bestdist(), qh_addpoint()
+    indicated by !qh_ISnewfacets
+    returns best facet in neighborhood of given facet
+      this is best facet overall if dist >= -qh.MAXcoplanar
+        or hull has at least a "spherical" curvature
+
+  design:
+    initialize and test for early exit
+    repeat while there are better facets
+      for each neighbor of facet
+        exit if outside facet found
+        test for better facet
+    if point is inside and partitioning
+      test for new facets with a "sharp" intersection
+      if so, future calls go to qh_findbestnew()
+    test horizon facets
+*/
+facetT *qh_findbest(qhT *qh, pointT *point, facetT *startfacet,
+                     boolT bestoutside, boolT isnewfacets, boolT noupper,
+                     realT *dist, boolT *isoutside, int *numpart) {
+  realT bestdist= -REALmax/2 /* avoid underflow */;
+  facetT *facet, *neighbor, **neighborp;
+  facetT *bestfacet= NULL, *lastfacet= NULL;
+  int oldtrace= qh->IStracing;
+  unsigned int visitid= ++qh->visit_id;
+  int numpartnew=0;
+  boolT testhorizon= True; /* needed if precise, e.g., rbox c D6 | qhull Q0 Tv */
+
+  zinc_(Zfindbest);
+#ifndef qh_NOtrace
+  if (qh->IStracing >= 4 || (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,",
+             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 qh_addpoint p%d,", qh->furthest_id);
+    qh_fprintf(qh, qh->ferr, 8007, " Last merge #%d, max_outside %2.2g\n", zzval_(Ztotmerge), qh->max_outside);
+  }
+#endif
+  if (isoutside)
+    *isoutside= True;
+  if (!startfacet->flipped) {  /* test startfacet before testing its neighbors */
+    *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) { /* startfacet is upperdelaunay (or flipped) w/o !flipped newfacet neighbors */
+      bestdist= -REALmax/2;
+      bestfacet= qh_findbestnew(qh, point, qh->newfacet_list, &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); /* lastfacet is non-NULL because startfacet is non-NULL */
+  if (testhorizon) /* qh_findbestnew not called */
+    bestfacet= qh_findbesthorizon(qh, !qh_IScheckmax, point, bestfacet, noupper, &bestdist, &numpartnew);
+  *dist= bestdist;
+  if (isoutside && bestdist < qh->MINoutside)
+    *isoutside= False;
+LABELreturn_best:
+  zadd_(Zfindbesttot, *numpart);
+  zmax_(Zfindbestmax, *numpart);
+  (*numpart) += numpartnew;
+  qh->IStracing= oldtrace;
+  return bestfacet;
+}  /* findbest */
+
+
+/*-<a                             href="qh-geom_r.htm#TOC"
+  >-------------------------------</a><a name="findbesthorizon">-</a>
+
+  qh_findbesthorizon(qh, qh_IScheckmax, point, startfacet, qh_NOupper, &bestdist, &numpart )
+    search coplanar and better horizon facets from startfacet/bestdist
+    ischeckmax turns off statistics and minsearch update
+    all arguments must be initialized, including *bestdist and *numpart
+    qh.coplanarfacetset used to maintain current search set, reset whenever best facet is substantially better
+  returns(ischeckmax):
+    best facet
+    updates f.maxoutside for neighbors of searched facets (if qh_MAXoutside)
+  returns(!ischeckmax):
+    best facet that is not upperdelaunay or newfacet (qh.first_newfacet)
+    allows upperdelaunay that is clearly outside
+  returns:
+    bestdist is distance to bestfacet
+    numpart -- updates number of distance tests
+
+  notes:
+    called by qh_findbest if point is not outside a facet (directed search)
+    called by qh_findbestnew if point is not outside a new facet
+    called by qh_check_maxout for each point in hull
+    called by qh_check_bestdist for each point in hull (rarely used)
+
+    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, numcoplanar= 0, numfacet= 0;
+  unsigned int visitid= ++qh->visit_id;
+  boolT newbest= False; /* for tracing */
+  realT minsearch, searchdist;  /* skip facets that are too far from point */
+  boolT is_5x_minsearch;
+
+  if (!ischeckmax) {
+    zinc_(Zfindhorizon);
+  }else {
+#if qh_MAXoutside
+    if ((!qh->ONLYgood || startfacet->good) && *bestdist > startfacet->maxoutside)
+      startfacet->maxoutside= *bestdist;
+#endif
+  }
+  searchdist= qh_SEARCHdist; /* an expression, a 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;
+  startfacet->visitid= visitid;
+  facet= startfacet;
+  while (True) {
+    numfacet++;
+    is_5x_minsearch= (ischeckmax && facet->nummerge > 10 && qh_setsize(qh, facet->neighbors) > 100);  /* QH11033 FIX: qh_findbesthorizon: many tests for facets with many merges and neighbors.  Can hide coplanar facets, e.g., 'rbox 1000 s Z1 G1e-13' with 4400+ neighbors */
+    trace4((qh, qh->ferr, 4002, "qh_findbesthorizon: test neighbors of f%d bestdist %2.2g f%d ischeckmax? %d noupper? %d minsearch %2.2g is_5x? %d searchdist %2.2g\n",
+                facet->id, *bestdist, getid_(bestfacet), ischeckmax, noupper,
+                minsearch, is_5x_minsearch, searchdist));
+    FOREACHneighbor_(facet) {
+      if (neighbor->visitid == visitid)
+        continue;
+      neighbor->visitid= visitid;
+      if (!neighbor->flipped) {  /* neighbors of flipped facets always searched via nextfacet */
+        qh_distplane(qh, point, neighbor, &dist); /* duplicate qh_distpane for new facets, they may be coplanar */
+        (*numpart)++;
+        if (dist > *bestdist) {
+          if (!neighbor->upperdelaunay || ischeckmax || (!noupper && dist >= qh->MINoutside)) {
+            if (!ischeckmax) {
+              minsearch= dist - searchdist;
+              if (dist > *bestdist + searchdist) {
+                zinc_(Zfindjump);  /* everything in qh.coplanarfacetset at least searchdist below */
+                coplanarfacetset_size= 0;
+              }
+            }
+            bestfacet= neighbor;
+            *bestdist= dist;
+            newbest= True;
+          }
+        }else if (is_5x_minsearch) {
+          if (dist < 5 * minsearch)
+            continue; /* skip this neighbor, do not set nextfacet.  dist is negative */
+        }else if (dist < minsearch)
+          continue;  /* skip this neighbor, do not set nextfacet.  If ischeckmax, dist can't be positive */
+#if qh_MAXoutside
+        if (ischeckmax && dist > neighbor->maxoutside)
+          neighbor->maxoutside= dist;
+#endif
+      } /* end of !flipped, need to search neighbor */
+      if (nextfacet) {
+        numcoplanar++;
+        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, i.e., "for each facet in qh.coplanarfacetset" */
+  if (!ischeckmax) {
+    zadd_(Zfindhorizontot, *numpart - numpartinit);
+    zmax_(Zfindhorizonmax, *numpart - numpartinit);
+    if (newbest)
+      zinc_(Znewbesthorizon);
+  }
+  trace4((qh, qh->ferr, 4003, "qh_findbesthorizon: p%d, newbest? %d, bestfacet f%d, bestdist %2.2g, numfacet %d, coplanarfacets %d, numdist %d\n",
+    qh_pointid(qh, point), newbest, getid_(bestfacet), *bestdist, numfacet, numcoplanar, *numpart - numpartinit));
+  return bestfacet;
+}  /* findbesthorizon */
+
+/*-<a                             href="qh-geom_r.htm#TOC"
+  >-------------------------------</a><a name="findbestnew">-</a>
+
+  qh_findbestnew(qh, point, startfacet, dist, isoutside, numpart )
+    find best newfacet for point
+    searches all of qh.newfacet_list starting at startfacet
+    searches horizon facets of coplanar best newfacets
+    searches all facets if startfacet == qh.facet_list
+  returns:
+    best new or horizon facet that is not upperdelaunay
+    early out if isoutside and not 'Qf'
+    dist is distance to facet
+    isoutside is true if point is outside of facet
+    numpart is number of distance tests
+
+  notes:
+    Always used for merged new facets (see qh_USEfindbestnew)
+    Avoids upperdelaunay facet unless (isoutside and outside)
+
+    Uses qh.visit_id, qh.coplanarfacetset.
+    If share visit_id with qh_findbest, coplanarfacetset is incorrect.
+
+    If merging (testhorizon), searches horizon facets of coplanar best facets because
+    a point maybe coplanar to the bestfacet, below its horizon facet,
+    and above a horizon facet of a coplanar newfacet.  For example,
+      rbox 1000 s Z1 G1e-13 | qhull
+      rbox 1000 s W1e-13 P0 t992110337 | QHULL d Qbb Qc
+
+    qh_findbestnew() used if
+       qh_sharpnewfacets -- newfacets contains a sharp angle
+       if many merges, qh_premerge found a merge, or 'Qf' (qh.findbestnew)
+
+  see also:
+    qh_partitionall() and qh_findbest()
+
+  design:
+    for each new facet starting from startfacet
+      test distance from point to facet
+      return facet if clearly outside
+      unless upperdelaunay and a lowerdelaunay exists
+         update best facet
+    test horizon facets
+*/
+facetT *qh_findbestnew(qhT *qh, pointT *point, facetT *startfacet,
+           realT *dist, boolT bestoutside, boolT *isoutside, int *numpart) {
+  realT bestdist= -REALmax/2;
+  facetT *bestfacet= NULL, *facet;
+  int oldtrace= qh->IStracing, i;
+  unsigned int visitid= ++qh->visit_id;
+  realT distoutside= 0.0;
+  boolT isdistoutside; /* True if distoutside is defined */
+  boolT testhorizon= True; /* needed if precise, e.g., rbox c D6 | qhull Q0 Tv */
+
+  if (!startfacet || !startfacet->next) {
+    if (qh->MERGING) {
+      qh_fprintf(qh, qh->ferr, 6001, "qhull topology error (qh_findbestnew): merging has formed and deleted a cone of new facets.  Can not continue.\n");
+      qh_errexit(qh, qh_ERRtopology, NULL, NULL);
+    }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_r.h */
+  }
+  if (isoutside)
+    *isoutside= True;
+  *numpart= 0;
+#ifndef qh_NOtrace
+  if (qh->IStracing >= 4 || (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,",
+             qh_pointid(qh, point), startfacet->id, isdistoutside, distoutside);
+    qh_fprintf(qh, qh->ferr, 8009, " Last qh_addpoint p%d, qh.visit_id %d, vertex_visit %d,",  qh->furthest_id, visitid, qh->vertex_visit);
+    qh_fprintf(qh, qh->ferr, 8010, " Last merge #%d\n", zzval_(Ztotmerge));
+  }
+#endif
+  /* 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 for p%d f%d bestoutside? %d \n",
+    getid_(bestfacet), *dist, qh_pointid(qh, point), startfacet->id, bestoutside));
+  qh->IStracing= oldtrace;
+  return bestfacet;
+}  /* findbestnew */
+
+/* ============ hyperplane functions -- keep code together [?] ============ */
+
+/*-<a                             href="qh-geom_r.htm#TOC"
+  >-------------------------------</a><a name="backnormal">-</a>
+
+  qh_backnormal(qh, rows, numrow, numcol, sign, normal, nearzero )
+    given an upper-triangular rows array and a sign,
+    solve for normal equation x using back substitution over rows U
+
+  returns:
+     normal= x
+
+     if will not be able to divzero() when normalized(qh.MINdenom_2 and qh.MINdenom_1_2),
+       if fails on last row
+         this means that the hyperplane intersects [0,..,1]
+         sets last coordinate of normal to sign
+       otherwise
+         sets tail of normal to [...,sign,0,...], i.e., solves for b= [0...0]
+         sets nearzero
+
+  notes:
+     assumes numrow == numcol-1
+
+     see Golub & van Loan, 1983, Eq. 4.4-9 for "Gaussian elimination with complete pivoting"
+
+     solves Ux=b where Ax=b and PA=LU
+     b= [0,...,0,sign or 0]  (sign is either -1 or +1)
+     last row of A= [0,...,0,1]
+
+     1) Ly=Pb == y=b since P only permutes the 0's of   b
+
+  design:
+    for each row from end
+      perform back substitution
+      if near zero
+        use qh_divzero for division
+        if zero divide and not last row
+          set tail of normal to 0
+*/
+void qh_backnormal(qhT *qh, realT **rows, int numrow, int numcol, boolT sign,
+        coordT *normal, boolT *nearzero) {
+  int i, j;
+  coordT *normalp, *normal_tail, *ai, *ak;
+  realT diagonal;
+  boolT waszero;
+  int zerocol= -1;
+
+  normalp= normal + numcol - 1;
+  *normalp--= (sign ? -1.0 : 1.0);
+  for (i=numrow; i--; ) {
+    *normalp= 0.0;
+    ai= rows[i] + i + 1;
+    ak= normalp+1;
+    for (j=i+1; j < numcol; j++)
+      *normalp -= *ai++ * *ak++;
+    diagonal= (rows[i])[i];
+    if (fabs_(diagonal) > qh->MINdenom_2)
+      *(normalp--) /= diagonal;
+    else {
+      waszero= False;
+      *normalp= qh_divzero(*normalp, diagonal, qh->MINdenom_1_2, &waszero);
+      if (waszero) {
+        zerocol= i;
+        *(normalp--)= (sign ? -1.0 : 1.0);
+        for (normal_tail= normalp+2; normal_tail < normal + numcol; normal_tail++)
+          *normal_tail= 0.0;
+      }else
+        normalp--;
+    }
+  }
+  if (zerocol != -1) {
+    *nearzero= True;
+    trace4((qh, qh->ferr, 4005, "qh_backnormal: zero diagonal at column %d.\n", i));
+    zzinc_(Zback0);
+    qh_joggle_restart(qh, "zero diagonal on back substitution");
+  }
+} /* backnormal */
+
+/*-<a                             href="qh-geom_r.htm#TOC"
+  >-------------------------------</a><a name="gausselim">-</a>
+
+  qh_gausselim(qh, rows, numrow, numcol, sign )
+    Gaussian elimination with partial pivoting
+
+  returns:
+    rows is upper triangular (includes row exchanges)
+    flips sign for each row exchange
+    sets nearzero if pivot[k] < qh.NEARzero[k], else clears it
+
+  notes:
+    if nearzero, the determinant's sign may be incorrect.
+    assumes numrow <= numcol
+
+  design:
+    for each row
+      determine pivot and exchange rows if necessary
+      test for near zero
+      perform gaussian elimination step
+*/
+void qh_gausselim(qhT *qh, realT **rows, int numrow, int numcol, boolT *sign, boolT *nearzero) {
+  realT *ai, *ak, *rowp, *pivotrow;
+  realT n, pivot, pivot_abs= 0.0, temp;
+  int i, j, k, pivoti, flip=0;
+
+  *nearzero= False;
+  for (k=0; k < numrow; k++) {
+    pivot_abs= fabs_((rows[k])[k]);
+    pivoti= k;
+    for (i=k+1; i < numrow; i++) {
+      if ((temp= fabs_((rows[i])[k])) > pivot_abs) {
+        pivot_abs= temp;
+        pivoti= i;
+      }
+    }
+    if (pivoti != k) {
+      rowp= rows[pivoti];
+      rows[pivoti]= rows[k];
+      rows[k]= rowp;
+      *sign ^= 1;
+      flip ^= 1;
+    }
+    if (pivot_abs <= qh->NEARzero[k]) {
+      *nearzero= True;
+      if (pivot_abs == 0.0) {   /* remainder of column == 0 */
+#ifndef qh_NOtrace
+        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);
+        }
+#endif
+        zzinc_(Zgauss0);
+        qh_joggle_restart(qh, "zero pivot for Gaussian elimination");
+        goto LABELnextcol;
+      }
+    }
+    pivotrow= rows[k] + k;
+    pivot= *pivotrow++;  /* signed value of pivot, and remainder of row */
+    for (i=k+1; i < numrow; i++) {
+      ai= rows[i] + k;
+      ak= pivotrow;
+      n= (*ai++)/pivot;   /* divzero() not needed since |pivot| >= |*ai| */
+      for (j= numcol - (k+1); j--; )
+        *ai++ -= n * *ak++;
+    }
+  LABELnextcol:
+    ;
+  }
+  wmin_(Wmindenom, pivot_abs);  /* last pivot element */
+  if (qh->IStracing >= 5)
+    qh_printmatrix(qh, qh->ferr, "qh_gausselem: result", rows, numrow, numcol);
+} /* gausselim */
+
+
+/*-<a                             href="qh-geom_r.htm#TOC"
+  >-------------------------------</a><a name="getangle">-</a>
+
+  qh_getangle(qh, vect1, vect2 )
+    returns the dot product of two vectors
+    if qh.RANDOMdist, joggles result
+
+  notes:
+    the angle may be > 1.0 or < -1.0 because of roundoff errors
+
+*/
+realT qh_getangle(qhT *qh, pointT *vect1, pointT *vect2) {
+  realT angle= 0, randr;
+  int k;
+
+  for (k=qh->hull_dim; k--; )
+    angle += *vect1++ * *vect2++;
+  if (qh->RANDOMdist) {
+    randr= qh_RANDOMint;
+    angle += (2.0 * randr / qh_RANDOMmax - 1.0) *
+      qh->RANDOMfactor;
+  }
+  trace4((qh, qh->ferr, 4006, "qh_getangle: %4.4g\n", angle));
+  return(angle);
+} /* getangle */
+
+
+/*-<a                             href="qh-geom_r.htm#TOC"
+  >-------------------------------</a><a name="getcenter">-</a>
+
+  qh_getcenter(qh, vertices )
+    returns arithmetic center of a set of vertices as a new point
+
+  notes:
+    allocates point array for center
+*/
+pointT *qh_getcenter(qhT *qh, setT *vertices) {
+  int k;
+  pointT *center, *coord;
+  vertexT *vertex, **vertexp;
+  int count= qh_setsize(qh, vertices);
+
+  if (count < 2) {
+    qh_fprintf(qh, qh->ferr, 6003, "qhull internal error (qh_getcenter): not defined for %d points\n", count);
+    qh_errexit(qh, qh_ERRqhull, NULL, NULL);
+  }
+  center= (pointT *)qh_memalloc(qh, qh->normal_size);
+  for (k=0; k < qh->hull_dim; k++) {
+    coord= center+k;
+    *coord= 0.0;
+    FOREACHvertex_(vertices)
+      *coord += vertex->point[k];
+    *coord /= count;  /* count>=2 by QH6003 */
+  }
+  return(center);
+} /* getcenter */
+
+
+/*-<a                             href="qh-geom_r.htm#TOC"
+  >-------------------------------</a><a name="getcentrum">-</a>
+
+  qh_getcentrum(qh, facet )
+    returns the centrum for a facet as a new point
+
+  notes:
+    allocates the centrum
+*/
+pointT *qh_getcentrum(qhT *qh, facetT *facet) {
+  realT dist;
+  pointT *centrum, *point;
+
+  point= qh_getcenter(qh, facet->vertices);
+  zzinc_(Zcentrumtests);
+  qh_distplane(qh, point, facet, &dist);
+  centrum= qh_projectpoint(qh, point, facet, dist);
+  qh_memfree(qh, point, qh->normal_size);
+  trace4((qh, qh->ferr, 4007, "qh_getcentrum: for f%d, %d vertices dist= %2.2g\n",
+          facet->id, qh_setsize(qh, facet->vertices), dist));
+  return centrum;
+} /* getcentrum */
+
+
+/*-<a                             href="qh-geom_r.htm#TOC"
+  >-------------------------------</a><a name="getdistance">-</a>
+
+  qh_getdistance(qh, facet, neighbor, mindist, maxdist )
+    returns the min and max distance to neighbor of non-neighbor vertices in facet
+
+  returns:
+    the max absolute value
+
+  design:
+    for each vertex of facet that is not in neighbor
+      test the distance from vertex to neighbor
+*/
+coordT qh_getdistance(qhT *qh, facetT *facet, facetT *neighbor, coordT *mindist, coordT *maxdist) {
+  vertexT *vertex, **vertexp;
+  coordT dist, maxd, mind;
+
+  FOREACHvertex_(facet->vertices)
+    vertex->seen= False;
+  FOREACHvertex_(neighbor->vertices)
+    vertex->seen= True;
+  mind= 0.0;
+  maxd= 0.0;
+  FOREACHvertex_(facet->vertices) {
+    if (!vertex->seen) {
+      zzinc_(Zbestdist);
+      qh_distplane(qh, vertex->point, neighbor, &dist);
+      if (dist < mind)
+        mind= dist;
+      else if (dist > maxd)
+        maxd= dist;
+    }
+  }
+  *mindist= mind;
+  *maxdist= maxd;
+  mind= -mind;
+  if (maxd > mind)
+    return maxd;
+  else
+    return mind;
+} /* getdistance */
+
+
+/*-<a                             href="qh-geom_r.htm#TOC"
+  >-------------------------------</a><a name="normalize">-</a>
+
+  qh_normalize(qh, normal, dim, toporient )
+    normalize a vector and report if too small
+    does not use min norm
+
+  see:
+    qh_normalize2
+*/
+void qh_normalize(qhT *qh, coordT *normal, int dim, boolT toporient) {
+  qh_normalize2(qh, normal, dim, toporient, NULL, NULL);
+} /* normalize */
+
+/*-<a                             href="qh-geom_r.htm#TOC"
+  >-------------------------------</a><a name="normalize2">-</a>
+
+  qh_normalize2(qh, normal, dim, toporient, minnorm, ismin )
+    normalize a vector and report if too small
+    qh.MINdenom/MINdenom1 are the upper limits for divide overflow
+
+  returns:
+    normalized vector
+    flips sign if !toporient
+    if minnorm non-NULL,
+      sets ismin if normal < minnorm
+
+  notes:
+    if zero norm
+       sets all elements to sqrt(1.0/dim)
+    if divide by zero (divzero())
+       sets largest element to   +/-1
+       bumps Znearlysingular
+
+  design:
+    computes norm
+    test for minnorm
+    if not near zero
+      normalizes normal
+    else if zero norm
+      sets normal to standard value
+    else
+      uses qh_divzero to normalize
+      if nearzero
+        sets norm to direction of maximum value
+*/
+void qh_normalize2(qhT *qh, coordT *normal, int dim, boolT toporient,
+            realT *minnorm, boolT *ismin) {
+  int k;
+  realT *colp, *maxp, norm= 0, temp, *norm1, *norm2, *norm3;
+  boolT zerodiv;
+
+  norm1= normal+1;
+  norm2= normal+2;
+  norm3= normal+3;
+  if (dim == 2)
+    norm= sqrt((*normal)*(*normal) + (*norm1)*(*norm1));
+  else if (dim == 3)
+    norm= sqrt((*normal)*(*normal) + (*norm1)*(*norm1) + (*norm2)*(*norm2));
+  else if (dim == 4) {
+    norm= sqrt((*normal)*(*normal) + (*norm1)*(*norm1) + (*norm2)*(*norm2)
+               + (*norm3)*(*norm3));
+  }else if (dim > 4) {
+    norm= (*normal)*(*normal) + (*norm1)*(*norm1) + (*norm2)*(*norm2)
+               + (*norm3)*(*norm3);
+    for (k=dim-4, colp=normal+4; k--; colp++)
+      norm += (*colp) * (*colp);
+    norm= sqrt(norm);
+  }
+  if (minnorm) {
+    if (norm < *minnorm)
+      *ismin= True;
+    else
+      *ismin= False;
+  }
+  wmin_(Wmindenom, norm);
+  if (norm > qh->MINdenom) {
+    if (!toporient)
+      norm= -norm;
+    *normal /= norm;
+    *norm1 /= norm;
+    if (dim == 2)
+      ; /* all done */
+    else if (dim == 3)
+      *norm2 /= norm;
+    else if (dim == 4) {
+      *norm2 /= norm;
+      *norm3 /= norm;
+    }else if (dim >4) {
+      *norm2 /= norm;
+      *norm3 /= norm;
+      for (k=dim-4, colp=normal+4; k--; )
+        *colp++ /= norm;
+    }
+  }else if (norm == 0.0) {
+    temp= sqrt(1.0/dim);
+    for (k=dim, colp=normal; k--; )
+      *colp++= temp;
+  }else {
+    if (!toporient)
+      norm= -norm;
+    for (k=dim, colp=normal; k--; colp++) { /* k used below */
+      temp= qh_divzero(*colp, norm, qh->MINdenom_1, &zerodiv);
+      if (!zerodiv)
+        *colp= temp;
+      else {
+        maxp= qh_maxabsval(normal, dim);
+        temp= ((*maxp * norm >= 0.0) ? 1.0 : -1.0);
+        for (k=dim, colp=normal; k--; colp++)
+          *colp= 0.0;
+        *maxp= temp;
+        zzinc_(Znearlysingular);
+        /* qh_joggle_restart ignored for Znearlysingular, normal part of qh_sethyperplane_gauss */
+        trace0((qh, qh->ferr, 1, "qh_normalize: norm=%2.2g too small during p%d\n",
+               norm, qh->furthest_id));
+        return;
+      }
+    }
+  }
+} /* normalize */
+
+
+/*-<a                             href="qh-geom_r.htm#TOC"
+  >-------------------------------</a><a name="projectpoint">-</a>
+
+  qh_projectpoint(qh, point, facet, dist )
+    project point onto a facet by dist
+
+  returns:
+    returns a new point
+
+  notes:
+    if dist= distplane(point,facet)
+      this projects point to hyperplane
+    assumes qh_memfree_() is valid for normal_size
+*/
+pointT *qh_projectpoint(qhT *qh, pointT *point, facetT *facet, realT dist) {
+  pointT *newpoint, *np, *normal;
+  int normsize= qh->normal_size;
+  int k;
+  void **freelistp; /* used if !qh_NOmem by qh_memalloc_() */
+
+  qh_memalloc_(qh, normsize, freelistp, newpoint, pointT);
+  np= newpoint;
+  normal= facet->normal;
+  for (k=qh->hull_dim; k--; )
+    *(np++)= *point++ - dist * *normal++;
+  return(newpoint);
+} /* projectpoint */
+
+
+/*-<a                             href="qh-geom_r.htm#TOC"
+  >-------------------------------</a><a name="setfacetplane">-</a>
+
+  qh_setfacetplane(qh, facet )
+    sets the hyperplane for a facet
+    if qh.RANDOMdist, joggles hyperplane
+
+  notes:
+    uses global buffers qh.gm_matrix and qh.gm_row
+    overwrites facet->normal if already defined
+    updates Wnewvertex if PRINTstatistics
+    sets facet->upperdelaunay if upper envelope of Delaunay triangulation
+
+  design:
+    copy vertex coordinates to qh.gm_matrix/gm_row
+    compute determinate
+    if nearzero
+      recompute determinate with gaussian elimination
+      if nearzero
+        force outside orientation by testing interior point
+*/
+void qh_setfacetplane(qhT *qh, facetT *facet) {
+  pointT *point;
+  vertexT *vertex, **vertexp;
+  int normsize= qh->normal_size;
+  int k,i, oldtrace= 0;
+  realT dist;
+  void **freelistp; /* used if !qh_NOmem by qh_memalloc_() */
+  coordT *coord, *gmcoord;
+  pointT *point0= SETfirstt_(facet->vertices, vertexT)->point;
+  boolT nearzero= False;
+
+  zzinc_(Zsetplane);
+  if (!facet->normal)
+    qh_memalloc_(qh, normsize, freelistp, facet->normal, coordT);
+#ifndef qh_NOtrace
+  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);
+  }
+#endif
+  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 due to nearzero gauss and interior_point test.  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, 3060, "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;
+  }
+#ifndef qh_NOtrace
+  if (qh->IStracing >= 4) {
+    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");
+  }
+#endif
+  qh_checkflipped(qh, facet, NULL, qh_ALL);
+  if (facet == qh->tracefacet) {
+    qh->IStracing= oldtrace;
+    qh_printfacet(qh, qh->ferr, facet);
+  }
+} /* setfacetplane */
+
+
+/*-<a                             href="qh-geom_r.htm#TOC"
+  >-------------------------------</a><a name="sethyperplane_det">-</a>
+
+  qh_sethyperplane_det(qh, dim, rows, point0, toporient, normal, offset, nearzero )
+    given dim X dim array indexed by rows[], one row per point,
+        toporient(flips all signs),
+        and point0 (any row)
+    set normalized hyperplane equation from oriented simplex
+
+  returns:
+    normal (normalized)
+    offset (places point0 on the hyperplane)
+    sets nearzero if hyperplane not through points
+
+  notes:
+    only defined for dim == 2..4
+    rows[] is not modified
+    solves det(P-V_0, V_n-V_0, ..., V_1-V_0)=0, i.e. every point is on hyperplane
+    see Bower & Woodworth, A programmer's geometry, Butterworths 1983.
+
+  derivation of 3-d minnorm
+    Goal: all vertices V_i within qh.one_merge of hyperplane
+    Plan: exactly translate the facet so that V_0 is the origin
+          exactly rotate the facet so that V_1 is on the x-axis and y_2=0.
+          exactly rotate the effective perturbation to only effect n_0
+             this introduces a factor of sqrt(3)
+    n_0 = ((y_2-y_0)*(z_1-z_0) - (z_2-z_0)*(y_1-y_0)) / norm
+    Let M_d be the max coordinate difference
+    Let M_a be the greater of M_d and the max abs. coordinate
+    Let u be machine roundoff and distround be max error for distance computation
+    The max error for n_0 is sqrt(3) u M_a M_d / norm.  n_1 is approx. 1 and n_2 is approx. 0
+    The max error for distance of V_1 is sqrt(3) u M_a M_d M_d / norm.  Offset=0 at origin
+    Then minnorm = 1.8 u M_a M_d M_d / qh.ONEmerge
+    Note that qh.one_merge is approx. 45.5 u M_a and norm is usually about M_d M_d
+
+  derivation of 4-d minnorm
+    same as above except rotate the facet so that V_1 on x-axis and w_2, y_3, w_3=0
+     [if two vertices fixed on x-axis, can rotate the other two in yzw.]
+    n_0 = det3_(...) = y_2 det2_(z_1, w_1, z_3, w_3) = - y_2 w_1 z_3
+     [all other terms contain at least two factors nearly zero.]
+    The max error for n_0 is sqrt(4) u M_a M_d M_d / norm
+    Then minnorm = 2 u M_a M_d M_d M_d / qh.ONEmerge
+    Note that qh.one_merge is approx. 82 u M_a and norm is usually about M_d M_d M_d
+*/
+void qh_sethyperplane_det(qhT *qh, int dim, coordT **rows, coordT *point0,
+          boolT toporient, coordT *normal, realT *offset, boolT *nearzero) {
+  realT maxround, dist;
+  int i;
+  pointT *point;
+
+
+  if (dim == 2) {
+    normal[0]= dY(1,0);
+    normal[1]= dX(0,1);
+    qh_normalize2(qh, normal, dim, toporient, NULL, NULL);
+    *offset= -(point0[0]*normal[0]+point0[1]*normal[1]);
+    *nearzero= False;  /* since nearzero norm => incident points */
+  }else if (dim == 3) {
+    normal[0]= det2_(dY(2,0), dZ(2,0),
+                     dY(1,0), dZ(1,0));
+    normal[1]= det2_(dX(1,0), dZ(1,0),
+                     dX(2,0), dZ(2,0));
+    normal[2]= det2_(dX(2,0), dY(2,0),
+                     dX(1,0), dY(1,0));
+    qh_normalize2(qh, normal, dim, toporient, NULL, NULL);
+    *offset= -(point0[0]*normal[0] + point0[1]*normal[1]
+               + point0[2]*normal[2]);
+    maxround= qh->DISTround;
+    for (i=dim; i--; ) {
+      point= rows[i];
+      if (point != point0) {
+        dist= *offset + (point[0]*normal[0] + point[1]*normal[1]
+               + point[2]*normal[2]);
+        if (dist > maxround || dist < -maxround) {
+          *nearzero= True;
+          break;
+        }
+      }
+    }
+  }else if (dim == 4) {
+    normal[0]= - det3_(dY(2,0), dZ(2,0), dW(2,0),
+                        dY(1,0), dZ(1,0), dW(1,0),
+                        dY(3,0), dZ(3,0), dW(3,0));
+    normal[1]=   det3_(dX(2,0), dZ(2,0), dW(2,0),
+                        dX(1,0), dZ(1,0), dW(1,0),
+                        dX(3,0), dZ(3,0), dW(3,0));
+    normal[2]= - det3_(dX(2,0), dY(2,0), dW(2,0),
+                        dX(1,0), dY(1,0), dW(1,0),
+                        dX(3,0), dY(3,0), dW(3,0));
+    normal[3]=   det3_(dX(2,0), dY(2,0), dZ(2,0),
+                        dX(1,0), dY(1,0), dZ(1,0),
+                        dX(3,0), dY(3,0), dZ(3,0));
+    qh_normalize2(qh, normal, dim, toporient, NULL, NULL);
+    *offset= -(point0[0]*normal[0] + point0[1]*normal[1]
+               + point0[2]*normal[2] + point0[3]*normal[3]);
+    maxround= qh->DISTround;
+    for (i=dim; i--; ) {
+      point= rows[i];
+      if (point != point0) {
+        dist= *offset + (point[0]*normal[0] + point[1]*normal[1]
+               + point[2]*normal[2] + point[3]*normal[3]);
+        if (dist > maxround || dist < -maxround) {
+          *nearzero= True;
+          break;
+        }
+      }
+    }
+  }
+  if (*nearzero) {
+    zzinc_(Zminnorm);
+    /* qh_joggle_restart not needed, will call qh_sethyperplane_gauss instead */
+    trace0((qh, qh->ferr, 3, "qh_sethyperplane_det: degenerate norm during p%d, use qh_sethyperplane_gauss instead.\n", qh->furthest_id));
+  }
+} /* sethyperplane_det */
+
+
+/*-<a                             href="qh-geom_r.htm#TOC"
+  >-------------------------------</a><a name="sethyperplane_gauss">-</a>
+
+  qh_sethyperplane_gauss(qh, dim, rows, point0, toporient, normal, offset, nearzero )
+    given(dim-1) X dim array of rows[i]= V_{i+1} - V_0 (point0)
+    set normalized hyperplane equation from oriented simplex
+
+  returns:
+    normal (normalized)
+    offset (places point0 on the hyperplane)
+
+  notes:
+    if nearzero
+      orientation may be incorrect because of incorrect sign flips in gausselim
+    solves [V_n-V_0,...,V_1-V_0, 0 .. 0 1] * N == [0 .. 0 1]
+        or [V_n-V_0,...,V_1-V_0, 0 .. 0 1] * N == [0]
+    i.e., N is normal to the hyperplane, and the unnormalized
+        distance to [0 .. 1] is either 1 or   0
+
+  design:
+    perform gaussian elimination
+    flip sign for negative values
+    perform back substitution
+    normalize result
+    compute offset
+*/
+void qh_sethyperplane_gauss(qhT *qh, int dim, coordT **rows, pointT *point0,
+                boolT toporient, coordT *normal, coordT *offset, boolT *nearzero) {
+  coordT *pointcoord, *normalcoef;
+  int k;
+  boolT sign= toporient, nearzero2= False;
+
+  qh_gausselim(qh, rows, dim-1, dim, &sign, nearzero);
+  for (k=dim-1; k--; ) {
+    if ((rows[k])[k] < 0)
+      sign ^= 1;
+  }
+  if (*nearzero) {
+    zzinc_(Znearlysingular);
+    /* qh_joggle_restart ignored for Znearlysingular, normal part of qh_sethyperplane_gauss */
+    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 */
+
+
+

+ 189 - 0
contrib/libs/qhull/libqhull_r/geom_r.h

@@ -0,0 +1,189 @@
+/*<html><pre>  -<a                             href="qh-geom_r.htm"
+  >-------------------------------</a><a name="TOP">-</a>
+
+  geom_r.h
+    header file for geometric routines
+
+   see qh-geom_r.htm and geom_r.c
+
+   Copyright (c) 1993-2020 The Geometry Center.
+   $Id: //main/2019/qhull/src/libqhull_r/geom_r.h#2 $$Change: 2953 $
+   $DateTime: 2020/05/21 22:05:32 $$Author: bbarber $
+*/
+
+#ifndef qhDEFgeom
+#define qhDEFgeom 1
+
+#include "libqhull_r.h"
+
+/* ============ -macros- ======================== */
+
+/*-<a                             href="qh-geom_r.htm#TOC"
+  >--------------------------------</a><a name="fabs_">-</a>
+
+  fabs_(a)
+    returns the absolute value of a
+*/
+#define fabs_( a ) ((( a ) < 0 ) ? -( a ):( a ))
+
+/*-<a                             href="qh-geom_r.htm#TOC"
+  >--------------------------------</a><a name="fmax_">-</a>
+
+  fmax_(a,b)
+    returns the maximum value of a and b
+*/
+#define fmax_( a,b )  ( ( a ) < ( b ) ? ( b ) : ( a ) )
+
+/*-<a                             href="qh-geom_r.htm#TOC"
+  >--------------------------------</a><a name="fmin_">-</a>
+
+  fmin_(a,b)
+    returns the minimum value of a and b
+*/
+#define fmin_( a,b )  ( ( a ) > ( b ) ? ( b ) : ( a ) )
+
+/*-<a                             href="qh-geom_r.htm#TOC"
+  >--------------------------------</a><a name="maximize_">-</a>
+
+  maximize_(maxval, val)
+    set maxval to val if val is greater than maxval
+*/
+#define maximize_( maxval, val ) { if (( maxval ) < ( val )) ( maxval )= ( val ); }
+
+/*-<a                             href="qh-geom_r.htm#TOC"
+  >--------------------------------</a><a name="minimize_">-</a>
+
+  minimize_(minval, val)
+    set minval to val if val is less than minval
+*/
+#define minimize_( minval, val ) { if (( minval ) > ( val )) ( minval )= ( val ); }
+
+/*-<a                             href="qh-geom_r.htm#TOC"
+  >--------------------------------</a><a name="det2_">-</a>
+
+  det2_(a1, a2,
+        b1, b2)
+
+    compute a 2-d determinate
+*/
+#define det2_( a1,a2,b1,b2 ) (( a1 )*( b2 ) - ( a2 )*( b1 ))
+
+/*-<a                             href="qh-geom_r.htm#TOC"
+  >--------------------------------</a><a name="det3_">-</a>
+
+  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 ) )
+
+/*-<a                             href="qh-geom_r.htm#TOC"
+  >--------------------------------</a><a name="dX">-</a>
+
+  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);
+coordT  qh_getdistance(qhT *qh, facetT *facet, facetT *neighbor, coordT *mindist, coordT *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_detmaxoutside(qhT *qh);
+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);
+vertexT *qh_furthestnewvertex(qhT *qh, unsigned int unvisited, facetT *facet, realT *maxdistp /* qh.newvertex_list */);
+vertexT *qh_furthestvertex(qhT *qh, facetT *facetA, facetT *facetB, realT *maxdistp, realT *mindistp);
+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);
+coordT  qh_vertex_bestdist(qhT *qh, setT *vertices);
+coordT  qh_vertex_bestdist2(qhT *qh, setT *vertices, vertexT **vertexp, vertexT **vertexp2);
+pointT *qh_voronoi_center(qhT *qh, int dim, setT *points);
+
+#ifdef __cplusplus
+} /* extern "C"*/
+#endif
+
+#endif /* qhDEFgeom */
+
+
+

+ 2268 - 0
contrib/libs/qhull/libqhull_r/global_r.c

@@ -0,0 +1,2268 @@
+
+/*<html><pre>  -<a                             href="qh-globa_r.htm"
+  >-------------------------------</a><a name="TOP">-</a>
+
+   global_r.c
+   initializes all the globals of the qhull application
+
+   see README
+
+   see libqhull_r.h for qh.globals and function prototypes
+
+   see qhull_ra.h for internal functions
+
+   Copyright (c) 1993-2020 The Geometry Center.
+   $Id: //main/2019/qhull/src/libqhull_r/global_r.c#19 $$Change: 3037 $
+   $DateTime: 2020/09/03 17:28:32 $$Author: bbarber $
+ */
+
+#include "qhull_ra.h"
+
+/*========= qh->definition -- globals defined in libqhull_r.h =======================*/
+
+/*-<a                             href  ="qh-globa_r.htm#TOC"
+  >--------------------------------</a><a name="version">-</a>
+
+  qh_version
+    version string by year and date
+    qh_version2 for Unix users and -V
+
+    the revision increases on code changes only
+
+  notes:
+    change date:    Changes.txt, Announce.txt, index.htm, README.txt,
+                    qhull-news.html, Eudora signatures, CMakeLists.txt
+    change version: README.txt, qh-get.htm, File_id.diz, Makefile.txt, CMakeLists.txt
+    check that CMakeLists.txt @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[]= "2020.2.r 2020/08/31";
+const char qh_version2[]= "qhull_r 8.0.2 (2020.2.r 2020/08/31)";
+
+/*-<a                             href="qh-globa_r.htm#TOC"
+  >-------------------------------</a><a name="appendprint">-</a>
+
+  qh_appendprint(qh, printFormat )
+    append printFormat to qh.PRINTout unless already defined
+*/
+void qh_appendprint(qhT *qh, qh_PRINT format) {
+  int i;
+
+  for (i=0; i < qh_PRINTEND; i++) {
+    if (qh->PRINTout[i] == format && format != qh_PRINTqhull)
+      break;
+    if (!qh->PRINTout[i]) {
+      qh->PRINTout[i]= format;
+      break;
+    }
+  }
+} /* appendprint */
+
+/*-<a                             href="qh-globa_r.htm#TOC"
+  >-------------------------------</a><a name="checkflags">-</a>
+
+  qh_checkflags(qh, commandStr, hiddenFlags )
+    errors if commandStr contains hiddenFlags
+    hiddenFlags starts and ends with a space and is space delimited (checked)
+
+  notes:
+    ignores first word (e.g., "qconvex i")
+    use qh_strtol/strtod since strtol/strtod may or may not skip trailing spaces
+
+  see:
+    qh_initflags() initializes Qhull according to commandStr
+*/
+void qh_checkflags(qhT *qh, char *command, char *hiddenflags) {
+  char *s= command, *t, *chkerr; /* qh_skipfilename is non-const */
+  char key, opt, prevopt;
+  char chkkey[]=  "   ";    /* check one character options ('s') */
+  char chkopt[]=  "    ";   /* check two character options ('Ta') */
+  char chkopt2[]= "     ";  /* check three character options ('Q12') */
+  boolT waserr= False;
+
+  if (*hiddenflags != ' ' || hiddenflags[strlen(hiddenflags)-1] != ' ') {
+    qh_fprintf(qh, qh->ferr, 6026, "qhull internal error (qh_checkflags): hiddenflags must start and end with a space: \"%s\"\n", hiddenflags);
+    qh_errexit(qh, qh_ERRqhull, NULL, NULL);
+  }
+  if (strpbrk(hiddenflags, ",\n\r\t")) {
+    qh_fprintf(qh, qh->ferr, 6027, "qhull internal error (qh_checkflags): hiddenflags contains commas, newlines, or tabs: \"%s\"\n", hiddenflags);
+    qh_errexit(qh, qh_ERRqhull, 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))) {
+            if (isdigit(*s)) {  /* Q12 */
+              chkopt2[2]= opt;
+              chkopt2[3]= *s++;
+              if (strstr(hiddenflags, chkopt2))
+                chkerr= chkopt2;
+            }else {
+              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 option error: option %s is not used with this program.\n             It may be used with qhull.\n", chkerr);
+      waserr= True;
+    }
+  }
+  if (waserr)
+    qh_errexit(qh, qh_ERRinput, NULL, NULL);
+} /* checkflags */
+
+/*-<a                             href="qh-globa_r.htm#TOC"
+  >-------------------------------</a><a name="clear_outputflags">-</a>
+
+  qh_clear_outputflags(qh)
+    Clear output flags for QhullPoints
+*/
+void qh_clear_outputflags(qhT *qh) {
+  int i,k;
+
+  qh->ANNOTATEoutput= False;
+  qh->DOintersections= False;
+  qh->DROPdim= -1;
+  qh->FORCEoutput= False;
+  qh->GETarea= False;
+  qh->GOODpoint= 0;
+  qh->GOODpointp= NULL;
+  qh->GOODthreshold= False;
+  qh->GOODvertex= 0;
+  qh->GOODvertexp= NULL;
+  qh->IStracing= 0;
+  qh->KEEParea= False;
+  qh->KEEPmerge= False;
+  qh->KEEPminArea= REALmax;
+  qh->PRINTcentrums= False;
+  qh->PRINTcoplanar= False;
+  qh->PRINTdots= False;
+  qh->PRINTgood= False;
+  qh->PRINTinner= False;
+  qh->PRINTneighbors= False;
+  qh->PRINTnoplanes= False;
+  qh->PRINToptions1st= False;
+  qh->PRINTouter= False;
+  qh->PRINTprecision= True;
+  qh->PRINTridges= False;
+  qh->PRINTspheres= False;
+  qh->PRINTstatistics= False;
+  qh->PRINTsummary= False;
+  qh->PRINTtransparent= False;
+  qh->SPLITthresholds= False;
+  qh->TRACElevel= 0;
+  qh->TRInormals= False;
+  qh->USEstdout= False;
+  qh->VERIFYoutput= False;
+  for (k=qh->input_dim+1; k--; ) {  /* duplicated in qh_initqhull_buffers and qh_clear_outputflags */
+    qh->lower_threshold[k]= -REALmax;
+    qh->upper_threshold[k]= REALmax;
+    qh->lower_bound[k]= -REALmax;
+    qh->upper_bound[k]= REALmax;
+  }
+
+  for (i=0; i < qh_PRINTEND; i++) {
+    qh->PRINTout[i]= qh_PRINTnone;
+  }
+
+  if (!qh->qhull_commandsiz2)
+      qh->qhull_commandsiz2= (int)strlen(qh->qhull_command); /* WARN64 */
+  else {
+      qh->qhull_command[qh->qhull_commandsiz2]= '\0';
+  }
+  if (!qh->qhull_optionsiz2)
+    qh->qhull_optionsiz2= (int)strlen(qh->qhull_options);  /* WARN64 */
+  else {
+    qh->qhull_options[qh->qhull_optionsiz2]= '\0';
+    qh->qhull_optionlen= qh_OPTIONline;  /* start a new line */
+  }
+} /* clear_outputflags */
+
+/*-<a                             href="qh-globa_r.htm#TOC"
+  >-------------------------------</a><a name="clock">-</a>
+
+  qh_clock()
+    return user CPU time in 100ths (qh_SECtick)
+    only defined for qh_CLOCKtype == 2
+
+  notes:
+    use first value to determine time 0
+    from Stevens '92 8.15
+*/
+unsigned long qh_clock(qhT *qh) {
+
+#if (qh_CLOCKtype == 2)
+  struct tms time;
+  static long clktck;  /* initialized first call and never updated */
+  double ratio, cpu;
+  unsigned long ticks;
+
+  if (!clktck) {
+    if ((clktck= sysconf(_SC_CLK_TCK)) < 0) {
+      qh_fprintf(qh, qh->ferr, 6030, "qhull internal error (qh_clock): sysconf() failed.  Use qh_CLOCKtype 1 in user_r.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_r.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_r.h\n");
+  qh_errexit(qh, qh_ERRqhull, NULL, NULL); /* never returns */
+  return 0;
+#endif
+} /* clock */
+
+/*-<a                             href="qh-globa_r.htm#TOC"
+  >-------------------------------</a><a name="freebuffers">-</a>
+
+  qh_freebuffers()
+    free up global memory buffers
+
+  notes:
+    must match qh_initbuffers()
+*/
+void qh_freebuffers(qhT *qh) {
+
+  trace5((qh, qh->ferr, 5001, "qh_freebuffers: freeing up global memory buffers\n"));
+  /* allocated by qh_initqhull_buffers */
+  qh_setfree(qh, &qh->other_points);
+  qh_setfree(qh, &qh->del_vertices);
+  qh_setfree(qh, &qh->coplanarfacetset);
+  qh_memfree(qh, qh->NEARzero, qh->hull_dim * (int)sizeof(realT));
+  qh_memfree(qh, qh->lower_threshold, (qh->input_dim+1) * (int)sizeof(realT));
+  qh_memfree(qh, qh->upper_threshold, (qh->input_dim+1) * (int)sizeof(realT));
+  qh_memfree(qh, qh->lower_bound, (qh->input_dim+1) * (int)sizeof(realT));
+  qh_memfree(qh, qh->upper_bound, (qh->input_dim+1) * (int)sizeof(realT));
+  qh_memfree(qh, qh->gm_matrix, (qh->hull_dim+1) * qh->hull_dim * (int)sizeof(coordT));
+  qh_memfree(qh, qh->gm_row, (qh->hull_dim+1) * (int)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;
+
+  if (qh->line)                /* allocated by qh_readinput, freed if no error */
+    qh_free(qh->line);
+  if (qh->half_space)
+    qh_free(qh->half_space);
+  if (qh->temp_malloc)
+    qh_free(qh->temp_malloc);
+  if (qh->feasible_point)      /* allocated by qh_readfeasible */
+    qh_free(qh->feasible_point);
+  if (qh->feasible_string)     /* allocated by qh_initflags */
+    qh_free(qh->feasible_string);
+  qh->line= qh->feasible_string= NULL;
+  qh->half_space= qh->feasible_point= qh->temp_malloc= NULL;
+  /* usually allocated by qh_readinput */
+  if (qh->first_point && qh->POINTSmalloc) {
+    qh_free(qh->first_point);
+    qh->first_point= NULL;
+  }
+  if (qh->input_points && qh->input_malloc) { /* set by qh_joggleinput */
+    qh_free(qh->input_points);
+    qh->input_points= NULL;
+  }
+  trace5((qh, qh->ferr, 5002, "qh_freebuffers: finished\n"));
+} /* freebuffers */
+
+
+/*-<a                             href="qh-globa_r.htm#TOC"
+  >-------------------------------</a><a name="freebuild">-</a>
+
+  qh_freebuild(qh, allmem )
+    free global memory used by qh_initbuild and qh_buildhull
+    if !allmem,
+      does not free short memory (e.g., facetT, freed by qh_memfreeshort)
+
+  design:
+    free centrums
+    free each vertex
+    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 sets
+    free temporary sets
+*/
+void qh_freebuild(qhT *qh, boolT allmem) {
+  facetT *facet, *previousfacet= NULL;
+  vertexT *vertex, *previousvertex= NULL;
+  ridgeT *ridge, **ridgep, *previousridge= NULL;
+  mergeT *merge, **mergep;
+  int newsize;
+  boolT freeall;
+
+  /* free qhT global sets first, includes references from qh_buildhull */
+  trace5((qh, qh->ferr, 5004, "qh_freebuild: free global sets\n"));
+  FOREACHmerge_(qh->facet_mergeset)  /* usually empty */
+    qh_memfree(qh, merge, (int)sizeof(mergeT));
+  FOREACHmerge_(qh->degen_mergeset)  /* usually empty */
+    qh_memfree(qh, merge, (int)sizeof(mergeT));
+  FOREACHmerge_(qh->vertex_mergeset)  /* usually empty */
+    qh_memfree(qh, merge, (int)sizeof(mergeT));
+  qh->facet_mergeset= NULL;  /* temp set freed by qh_settempfree_all */
+  qh->degen_mergeset= NULL;  /* temp set freed by qh_settempfree_all */
+  qh->vertex_mergeset= NULL;  /* temp set freed by qh_settempfree_all */
+  qh_setfree(qh, &(qh->hash_table));
+  trace5((qh, qh->ferr, 5003, "qh_freebuild: free temporary sets (qh_settempfree_all)\n"));
+  qh_settempfree_all(qh);
+  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)); /* sentinel */
+        qh->newvertex_list= qh->vertex_list= NULL;
+        break;
+      }
+      previousvertex= vertex; /* in case of memory fault */
+      QHULL_UNUSED(previousvertex)
+    }
+  }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;
+    }
+    while ((facet= qh->facet_list)) {
+      if (!facet->newfacet || !qh->NEWtentative || qh_setsize(qh, facet->ridges) > 1) { /* skip tentative horizon ridges */
+        trace4((qh, qh->ferr, 4095, "qh_freebuild: delete the previously-seen ridges of f%d\n", facet->id));
+        FOREACHridge_(facet->ridges) {
+          if (ridge->seen)
+            qh_delridge(qh, ridge);
+          else
+            ridge->seen= True;
+          previousridge= ridge; /* in case of memory fault */
+          QHULL_UNUSED(previousridge)
+        }
+      }
+      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;
+      }
+      previousfacet= facet; /* in case of memory fault */
+      QHULL_UNUSED(previousfacet)
+    }
+  }else {
+    freeall= True;
+    if (qh_setlarger_quick(qh, qh->hull_dim + 1, &newsize))
+      freeall= False;
+    FORALLfacets {
+      qh_setfreelong(qh, &(facet->outsideset));
+      qh_setfreelong(qh, &(facet->coplanarset));
+      if (!facet->simplicial || freeall) {
+        qh_setfreelong(qh, &(facet->neighbors));
+        qh_setfreelong(qh, &(facet->ridges));
+        qh_setfreelong(qh, &(facet->vertices));
+      }
+    }
+  }
+  /* qh internal constants */
+  qh_memfree(qh, qh->interior_point, qh->normal_size);
+  qh->interior_point= NULL;
+} /* freebuild */
+
+/*-<a                             href="qh-globa_r.htm#TOC"
+  >-------------------------------</a><a name="freeqhull">-</a>
+
+  qh_freeqhull(qh, allmem )
+
+  free global memory and set qhT to 0
+  if !allmem,
+    does not free short memory (freed by qh_memfreeshort unless qh_NOmem)
+
+notes:
+  sets qh.NOerrexit in case caller forgets to
+  Does not throw errors
+
+see:
+  see qh_initqhull_start2()
+  For libqhull_r, qhstatT is part of qhT
+
+design:
+  free global and temporary memory from qh_initbuild and qh_buildhull
+  free buffers
+*/
+void qh_freeqhull(qhT *qh, boolT allmem) {
+
+  qh->NOerrexit= True;  /* no more setjmp since called at exit and ~QhullQh */
+  trace1((qh, qh->ferr, 1006, "qh_freeqhull: free global memory\n"));
+  qh_freebuild(qh, allmem);
+  qh_freebuffers(qh);
+  trace1((qh, qh->ferr, 1061, "qh_freeqhull: clear qhT except for qh.qhmem and qh.qhstat\n"));
+  /* memset is the same in qh_freeqhull() and qh_initqhull_start2() */
+  memset((char *)qh, 0, sizeof(qhT)-sizeof(qhmemT)-sizeof(qhstatT));
+  qh->NOerrexit= True;
+} /* freeqhull */
+
+/*-<a                             href="qh-globa_r.htm#TOC"
+  >-------------------------------</a><a name="init_A">-</a>
+
+  qh_init_A(qh, infile, outfile, errfile, argc, argv )
+    initialize memory and stdio files
+    convert input options to option string (qh.qhull_command)
+
+  notes:
+    infile may be NULL if qh_readpoints() is not called
+
+    errfile should always be defined.  It is used for reporting
+    errors.  outfile is used for output and format options.
+
+    argc/argv may be 0/NULL
+
+    called before error handling initialized
+    qh_errexit() may not be used
+*/
+void qh_init_A(qhT *qh, FILE *infile, FILE *outfile, FILE *errfile, int argc, char *argv[]) {
+  qh_meminit(qh, errfile);
+  qh_initqhull_start(qh, infile, outfile, errfile);
+  qh_init_qhull_command(qh, argc, argv);
+} /* init_A */
+
+/*-<a                             href="qh-globa_r.htm#TOC"
+  >-------------------------------</a><a name="init_B">-</a>
+
+  qh_init_B(qh, points, numpoints, dim, ismalloc )
+    initialize globals for points array
+
+    points has numpoints dim-dimensional points
+      points[0] is the first coordinate of the first point
+      points[1] is the second coordinate of the first point
+      points[dim] is the first coordinate of the second point
+
+    ismalloc=True
+      Qhull will call qh_free(points) on exit or input transformation
+    ismalloc=False
+      Qhull will allocate a new point array if needed for input transformation
+
+    qh.qhull_command
+      is the option string.
+      It is defined by qh_init_B(), qh_qhull_command(), or qh_initflags
+
+  returns:
+    if qh.PROJECTinput or (qh.DELAUNAY and qh.PROJECTdelaunay)
+      projects the input to a new point array
+
+        if qh.DELAUNAY,
+          qh.hull_dim is increased by one
+        if qh.ATinfinity,
+          qh_projectinput adds point-at-infinity for Delaunay tri.
+
+    if qh.SCALEinput
+      changes the upper and lower bounds of the input, see qh_scaleinput
+
+    if qh.ROTATEinput
+      rotates the input by a random rotation, see qh_rotateinput
+      if qh.DELAUNAY
+        rotates about the last coordinate
+
+  notes:
+    called after points are defined
+    qh_errexit() may be used
+*/
+void qh_init_B(qhT *qh, coordT *points, int numpoints, int dim, boolT ismalloc) {
+  qh_initqhull_globals(qh, points, numpoints, dim, ismalloc);
+  if (qh->qhmem.LASTsize == 0)
+    qh_initqhull_mem(qh);
+  /* mem_r.c and qset_r.c are initialized */
+  qh_initqhull_buffers(qh);
+  qh_initthresholds(qh, qh->qhull_command);
+  if (qh->PROJECTinput || (qh->DELAUNAY && qh->PROJECTdelaunay))
+    qh_projectinput(qh);
+  if (qh->SCALEinput)
+    qh_scaleinput(qh);
+  if (qh->ROTATErandom >= 0) {
+    qh_randommatrix(qh, qh->gm_matrix, qh->hull_dim, qh->gm_row);
+    if (qh->DELAUNAY) {
+      int k, lastk= qh->hull_dim-1;
+      for (k=0; k < lastk; k++) {
+        qh->gm_row[k][lastk]= 0.0;
+        qh->gm_row[lastk][k]= 0.0;
+      }
+      qh->gm_row[lastk][lastk]= 1.0;
+    }
+    qh_gram_schmidt(qh, qh->hull_dim, qh->gm_row);
+    qh_rotateinput(qh, qh->gm_row);
+  }
+} /* init_B */
+
+/*-<a                             href="qh-globa_r.htm#TOC"
+  >-------------------------------</a><a name="init_qhull_command">-</a>
+
+  qh_init_qhull_command(qh, argc, argv )
+    build qh.qhull_command from argc/argv
+    Calls qh_exit if qhull_command is too short
+
+  returns:
+    a space-delimited string of options (just as typed)
+
+  notes:
+    makes option string easy to input and output
+
+    argc/argv may be 0/NULL
+*/
+void qh_init_qhull_command(qhT *qh, int argc, char *argv[]) {
+
+  if (!qh_argv_to_command(argc, argv, qh->qhull_command, (int)sizeof(qh->qhull_command))){
+    /* Assumes qh.ferr is defined. */
+    qh_fprintf(qh, qh->ferr, 6033, "qhull input error: more than %d characters in command line.\n",
+          (int)sizeof(qh->qhull_command));
+    qh_exit(qh_ERRinput);  /* error reported, can not use qh_errexit */
+  }
+} /* init_qhull_command */
+
+/*-<a                             href="qh-globa_r.htm#TOC"
+  >-------------------------------</a><a name="initflags">-</a>
+
+  qh_initflags(qh, commandStr )
+    set flags and initialized constants from commandStr
+    calls qh_exit() if qh.NOerrexit
+
+  returns:
+    sets qh.qhull_command to command if needed
+
+  notes:
+    ignores first word (e.g., 'qhull' in "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, *lastwarning= NULL;
+  boolT isgeom= False, wasproject;
+  realT r;
+
+  if(qh->NOerrexit){
+    qh_fprintf(qh, qh->ferr, 6245, "qhull internal error (qh_initflags): qh.NOerrexit was not cleared before calling qh_initflags().  It should be cleared after setjmp().  Exit qhull.\n");
+    qh_exit(qh_ERRqhull);
+  }
+#ifdef qh_RANDOMdist
+  qh->RANDOMfactor= qh_RANDOMdist;
+  qh_option(qh, "Random-qh_RANDOMdist", NULL, &qh->RANDOMfactor);
+  qh->RANDOMdist= True;
+#endif
+  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 input warning: no maximum cosine angle given for option 'An'.  A1.0 is coplanar\n");
+        lastwarning= s-1;
+      }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 input warning: no centrum radius given for option 'Cn'\n");
+        lastwarning= s-1;
+      }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, 6363, "qhull option error: expecting a positive number for maximum roundoff 'En'.  Got '%s'\n", s-1);
+        qh_errexit(qh, qh_ERRinput, NULL, NULL);
+      }else if (!isdigit(*s)) {
+        qh_fprintf(qh, qh->ferr, 7005, "qhull option warning: no maximum roundoff given for option 'En'\n");
+        lastwarning= s-1;
+      }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, 6364, "qhull option error: expecting 'Hn,n,n,...' for feasible point of halfspace intersection. Got '%s'\n", start-1);
+            qh_errexit(qh, qh_ERRinput, NULL, NULL);
+          }
+        }
+        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 option warning: missing random perturbation for option 'Rn'\n");
+        lastwarning= s-1;
+      }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 option warning: missing visible distance for option 'Vn'\n");
+        lastwarning= s-1;
+      }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 option warning: missing coplanar distance for option 'Un'\n");
+        lastwarning= s-1;
+      }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, 6365, "qhull option error: expecting a positive number for outside width 'Wn'.  Got '%s'\n", s-1);
+        qh_errexit(qh, qh_ERRinput, NULL, NULL);
+      }else if (!isdigit(*s)) {
+        qh_fprintf(qh, qh->ferr, 7011, "qhull option warning: missing outside width for option 'Wn'\n");
+        lastwarning= s-1;
+      }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 option warning: unknown 'F' output option 'F%c', skip to next space\n", (int)s[0]);
+          lastwarning= s-1;
+          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, 7004, "qhull option warning: missing dimension for option 'GDn'\n");
+            lastwarning= s-2;
+          }else {
+            if (qh->DROPdim >= 0) {
+              qh_fprintf(qh, qh->ferr, 7013, "qhull option warning: can only drop one dimension.  Previous 'GD%d' ignored\n",
+                   qh->DROPdim);
+              lastwarning= s-2;
+            }
+            qh->DROPdim= qh_strtol(s, &s);
+            qh_option(qh, "GDrop-dim", &qh->DROPdim, NULL);
+          }
+          break;
+        default:
+          s--;
+          qh_fprintf(qh, qh->ferr, 7014, "qhull option warning: unknown 'G' geomview option 'G%c', skip to next space\n", (int)s[0]);
+          lastwarning= s-1;
+          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, 7006, "qhull option warning: missing facet count for keep area option 'PAn'\n");
+            lastwarning= s-2;
+          }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, 7010, "qhull option warning: missing facet area for option 'PFn'\n");
+            lastwarning= s-2;
+          }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, 7090, "qhull option warning: missing merge count for option 'PMn'\n");
+            lastwarning= s-2;
+          }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 option warning: unknown 'P' print option 'P%c', skip to next space\n", (int)s[0]);
+          lastwarning= s-1;
+          while (*++s && !isspace(*s));
+          break;
+        }
+      }
+      break;
+    case 'Q':
+      lastproject= -1;
+      while (*s && !isspace(*s)) {
+        switch (*s++) {
+        case 'a':
+          qh_option(qh, "Qallow-short", NULL, NULL);
+          qh->ALLOWshort= True;
+          break;
+        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, 7091, "qhull option warning: missing number of test points for option 'QTn'\n");
+            lastwarning= s-2;
+          }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-angle-merge", NULL, NULL);
+            qh->ANGLEmerge= True;
+            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-allow-wide", NULL, NULL);
+            qh->ALLOWwide= True;
+            break;
+          case '4':
+#ifndef qh_NOmerge
+            qh_option(qh, "Q14-merge-pinched-vertices", NULL, NULL);
+            qh->MERGEpinched= True;
+#else
+            /* ignore 'Q14' for q_benchmark testing of difficult cases for Qhull */
+            qh_fprintf(qh, qh->ferr, 7099, "qhull option warning: option 'Q14-merge-pinched' disabled due to qh_NOmerge\n");
+#endif
+            break;
+          case '7':
+            qh_option(qh, "Q15-check-duplicates", NULL, NULL);
+            qh->CHECKduplicates= True;
+            break;
+          default:
+            s--;
+            qh_fprintf(qh, qh->ferr, 7016, "qhull option warning: unknown 'Q' qhull option 'Q1%c', skip to next space\n", (int)s[0]);
+            lastwarning= s-1;
+            while (*++s && !isspace(*s));
+            break;
+          }
+          break;
+        case '2':
+          qh_option(qh, "Q2-no-merge-independent", NULL, NULL);
+          qh->MERGEindependent= False;
+          goto LABELcheckdigit;
+          break; /* no gcc 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 option warning: can not follow '1', '2', or '3' with a digit.  'Q%c%c' skipped\n", *(s-1), *s);
+            lastwarning= s-2;
+            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 option warning: good point already defined for option 'QGn'.  Ignored\n");
+            lastwarning= s-2;
+          }else if (s == t) {
+            qh_fprintf(qh, qh->ferr, 7019, "qhull option warning: missing good point id for option 'QGn'.  Ignored\n");
+            lastwarning= s-2;
+          }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 option warning: missing random seed for option 'QRn'\n");
+            lastwarning= s-2;
+          }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 option warning: good vertex already defined for option 'QVn'.  Ignored\n");
+            lastwarning= s-2;
+          }else if (s == t) {
+            qh_fprintf(qh, qh->ferr, 7022, "qhull option warning: no good point id given for option 'QVn'.  Ignored\n");
+            lastwarning= s-2;
+          }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;
+        case 'w':
+          qh_option(qh, "Qwarn-allow", NULL, NULL);
+          qh->ALLOWwarning= True;
+          break;
+        default:
+          s--;
+          qh_fprintf(qh, qh->ferr, 7023, "qhull option warning: unknown 'Q' qhull option 'Q%c', skip to next space\n", (int)s[0]);
+          lastwarning= s-1;
+          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 'f':
+          qh_option(qh, "Tflush", NULL, NULL);
+          qh->FLUSHprint= 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 option warning: output file undefined(stdout).  Option 'Tz' ignored.\n");
+            lastwarning= s-2;
+          }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 option warning: missing point id for cone for trace option 'TCn'\n");
+            lastwarning= s-2;
+          }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 option warning: missing frequency count for trace option 'TFn'\n");
+            lastwarning= s-2;
+          }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':
+          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 option error: cannot open 'TI' file \"%s\"\n", 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':
+          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 */
+            if (!qh->fout) {
+              qh_fprintf(qh, qh->ferr, 7092, "qhull option warning: qh.fout was not set by caller of qh_initflags.  Cannot use option 'TO' to redirect output.  Ignoring option 'TO'\n");
+              lastwarning= s-2;
+            }else if (!freopen(filename, "w", qh->fout)) {
+              qh_fprintf(qh, qh->ferr, 6044, "qhull option error: cannot open file \"%s\" for writing as option 'TO'.  It is already in use or read-only\n", filename);
+              qh_errexit(qh, qh_ERRinput, NULL, NULL);
+            }else {
+              qh_option(qh, "TOutput-file", NULL, NULL);
+              qh_option(qh, filename, NULL, NULL);
+            }
+            s= t;
+          }
+          break;
+        case 'A':
+          if (!isdigit(*s)) {
+            qh_fprintf(qh, qh->ferr, 7093, "qhull option warning: missing count of added points for trace option 'TAn'\n");
+            lastwarning= s-2;
+          }else {
+            i= qh_strtol(s, &t);
+            qh->STOPadd= i + 1;
+            qh_option(qh, "TA-stop-add", &i, NULL);
+          }
+          s= t;
+          break;
+        case 'P':
+          if (*s == '-') {
+            if (s[1] == '1' && !isdigit(s[2])) {
+              s += 2;
+              qh->TRACEpoint= qh_IDunknown; /* qh_buildhull done */
+              qh_option(qh, "Trace-point", &qh->TRACEpoint, NULL);
+            }else {
+              qh_fprintf(qh, qh->ferr, 7100, "qhull option warning: negative point id for trace option 'TPn'.  Expecting 'TP-1' for tracing after qh_buildhull and qh_postmerge\n");
+              lastwarning= s-2;
+              while (isdigit(*(++s)))
+                ; /* skip digits */
+            }
+          }else if (!isdigit(*s)) {
+            qh_fprintf(qh, qh->ferr, 7029, "qhull option warning: missing point id or -1 for trace option 'TPn'\n");
+            lastwarning= s-2;
+          }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 option warning: missing merge id for trace option 'TMn'\n");
+            lastwarning= s-2;
+          }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 option warning: missing rerun count for trace option 'TRn'\n");
+            lastwarning= s-2;
+          }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 option warning: missing furthest point id for trace option 'TVn'\n");
+            lastwarning= s-2;
+          }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 option warning: missing max width for trace option 'TWn'\n");
+            lastwarning= s-2;
+          }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 option warning: unknown 'T' trace option 'T%c', skip to next space\n", (int)s[0]);
+          lastwarning= s-2;
+          while (*++s && !isspace(*s));
+          break;
+        }
+      }
+      break;
+    default:
+      qh_fprintf(qh, qh->ferr, 7094, "qhull option warning: unknown option '%c'(%x)\n",
+        (int)s[-1], (int)s[-1]);
+      lastwarning= s-2;
+      break;
+    }
+    if (s-1 == prev_s && *s && !isspace(*s)) {
+      qh_fprintf(qh, qh->ferr, 7036, "qhull option warning: missing space after option '%c'(%x), reserved for sub-options, ignoring '%c' options to next space\n",
+               (int)*prev_s, (int)*prev_s, (int)*prev_s);
+      lastwarning= s-1;
+      while (*s && !isspace(*s))
+        s++;
+    }
+  }
+  if (qh->STOPcone && qh->JOGGLEmax < REALmax/2) {
+    qh_fprintf(qh, qh->ferr, 7078, "qhull option warning: 'TCn' (stopCone) ignored when used with 'QJn' (joggle)\n");
+    lastwarning= command;
+  }
+  if (isgeom && !qh->FORCEoutput && qh->PRINTout[1]) {
+    qh_fprintf(qh, qh->ferr, 7037, "qhull option warning: additional output formats ('Fc',etc.) are not compatible with Geomview ('G').  Use option 'Po' to override\n");
+    lastwarning= command;
+  }
+  if (lastwarning && !qh->ALLOWwarning) {
+    qh_fprintf(qh, qh->ferr, 6035, "qhull option error: see previous warnings, use 'Qw' to override: '%s' (last offset %d)\n",
+          command, (int)(lastwarning-command));
+    qh_errexit(qh, qh_ERRinput, NULL, NULL);
+  }
+  trace4((qh, qh->ferr, 4093, "qh_initflags: option flags initialized\n"));
+  /* set derived values in qh_initqhull_globals */
+} /* initflags */
+
+
+/*-<a                             href="qh-globa_r.htm#TOC"
+  >-------------------------------</a><a name="initqhull_buffers">-</a>
+
+  qh_initqhull_buffers(qh)
+    initialize global memory buffers
+
+  notes:
+    must match qh_freebuffers()
+*/
+void qh_initqhull_buffers(qhT *qh) {
+  int k;
+
+  qh->TEMPsize= (qh->qhmem.LASTsize - (int)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 * (int)sizeof(realT));
+  qh->lower_threshold= (realT *)qh_memalloc(qh, (qh->input_dim+1) * (int)sizeof(realT));
+  qh->upper_threshold= (realT *)qh_memalloc(qh, (qh->input_dim+1) * (int)sizeof(realT));
+  qh->lower_bound= (realT *)qh_memalloc(qh, (qh->input_dim+1) * (int)sizeof(realT));
+  qh->upper_bound= (realT *)qh_memalloc(qh, (qh->input_dim+1) * (int)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 * (int)sizeof(coordT));
+  qh->gm_row= (coordT **)qh_memalloc(qh, (qh->hull_dim+1) * (int)sizeof(coordT *));
+} /* initqhull_buffers */
+
+/*-<a                             href="qh-globa_r.htm#TOC"
+  >-------------------------------</a><a name="initqhull_globals">-</a>
+
+  qh_initqhull_globals(qh, points, numpoints, dim, ismalloc )
+    initialize globals
+    if ismalloc
+      points were malloc'd and qhull should free at end
+
+  returns:
+    sets qh.first_point, num_points, input_dim, hull_dim and others
+    seeds random number generator (seed=1 if tracing)
+    modifies qh.hull_dim if ((qh.DELAUNAY and qh.PROJECTdelaunay) or qh.PROJECTinput)
+    adjust user flags as needed
+    also checks DIM3 dependencies and constants
+
+  notes:
+    do not use qh_point() since an input transformation may move them elsewhere
+    qh_initqhull_start() sets default values for non-zero globals
+    consider duplicate error checks in qh_readpoints.  It is called before qh_initqhull_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));
+  if (numpoints < 1 || numpoints > qh_POINTSmax) {
+    qh_fprintf(qh, qh->ferr, 6412, "qhull input error (qh_initqhull_globals): expecting between 1 and %d points.  Got %d %d-d points\n",
+      qh_POINTSmax, numpoints, dim);
+    qh_errexit(qh, qh_ERRinput, NULL, NULL);
+    /* same error message in qh_readpoints */
+  }
+  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->MERGEexact || qh->PREmerge))
+    qh_fprintf(qh, qh->ferr, 7095, "qhull option warning: 'Q0-no-premerge' ignored due to exact merge ('Qx') or pre-merge ('C-n' or 'A-n')\n");
+  if (!qh->NOpremerge && qh->JOGGLEmax > REALmax/2) {
+#ifdef qh_NOmerge
+    qh->JOGGLEmax= 0.0;
+#endif
+  }
+  if (qh->TRIangulate && qh->JOGGLEmax < REALmax/2 && !qh->PREmerge && !qh->POSTmerge && qh->PRINTprecision)
+    qh_fprintf(qh, qh->ferr, 7038, "qhull option warning: joggle ('QJ') produces simplicial output (i.e., triangles in 2-D).  Unless merging is requested, option 'Qt' has no effect\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.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_r.h).\n",
+          REALepsilon);
+#ifdef qh_NOmerge
+  if (qh->MERGING) {
+    qh_fprintf(qh, qh->ferr, 6045, "qhull option error: merging not installed (qh_NOmerge) for '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->VORONOI && !qh->DELAUNAY) {
+    qh_fprintf(qh, qh->ferr, 6038, "qhull internal error (qh_initqhull_globals): if qh.VORONOI is set, qh.DELAUNAY must be set.  Qhull constructs the Delaunay triangulation in order to compute the Voronoi diagram\n");
+    qh_errexit(qh, qh_ERRqhull, NULL, NULL);
+  }
+  if (qh->DELAUNAY && qh->HALFspace) {
+    qh_fprintf(qh, qh->ferr, 6046, "qhull option error: can not use Delaunay('d') or Voronoi('v') with halfspace intersection('H')\n");
+    qh_errexit(qh, qh_ERRinput, NULL, NULL);
+    /* same error message in qh_readpoints */
+  }
+  if (!qh->DELAUNAY && (qh->UPPERdelaunay || qh->ATinfinity)) {
+    qh_fprintf(qh, qh->ferr, 6047, "qhull option 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 option error: can not use infinity-point('Qz') with upper-Delaunay('Qu')\n");
+    qh_errexit(qh, qh_ERRinput, NULL, NULL);
+  }
+  if (qh->MERGEpinched && qh->ONLYgood) {
+    qh_fprintf(qh, qh->ferr, 6362, "qhull option error: can not use merge-pinched-vertices ('Q14') with good-facets-only ('Qg')\n");
+    qh_errexit(qh, qh_ERRinput, NULL, NULL);
+  }
+  if (qh->MERGEpinched && qh->hull_dim == 2) {
+    trace2((qh, qh->ferr, 2108, "qh_initqhull_globals: disable qh.MERGEpinched for 2-d.  It has no effect"))
+    qh->MERGEpinched= False;
+  }
+  if (qh->SCALElast && !qh->DELAUNAY && qh->PRINTprecision)
+    qh_fprintf(qh, qh->ferr, 7040, "qhull option warning: option 'Qbb' (scale-last-coordinate) is normally used with 'd' or 'v'\n");
+  qh->DOcheckmax= (!qh->SKIPcheckmax && (qh->MERGING || qh->APPROXhull));
+  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 option 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.  input_dim %d, numpoints %d, malloc? %d, projected %d to hull_dim %d\n",
+        qh->input_dim, numpoints, ismalloc, qh->PROJECTinput, qh->hull_dim));
+  qh->normal_size= qh->hull_dim * (int)sizeof(coordT);
+  qh->center_size= qh->normal_size - (int)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->TRACEmerge || qh->TRACEpoint != qh_IDnone || qh->TRACEdist < REALmax/2) {
+      qh_fprintf(qh, qh->ferr, 6051, "qhull option error: tracing is not installed (qh_NOtrace in user_r.h).  Trace options 'Tn', 'TMn', 'TPn' and 'TWn' mostly removed.  Continue with 'Qw' (allow warning)\n");
+      if (!qh->ALLOWwarning)
+        qh_errexit(qh, qh_ERRinput, NULL, NULL);
+  }
+#endif
+  if (qh->RERUN > 1) {
+    qh->TRACElastrun= qh->IStracing; /* qh_build_withrestart duplicates next conditional */
+    if (qh->IStracing && qh->IStracing != -1) {
+      qh_fprintf(qh, qh->ferr, 8162, "qh_initqhull_globals: trace last of TR%d runs at level %d\n", qh->RERUN, qh->IStracing);
+      qh->IStracing= 0;
+    }
+  }else if (qh->TRACEpoint != qh_IDnone || 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_r.h): random integer %d > qh_RANDOMmax (%.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_r.h): average of 1000 random integers (%.2g) is much different than expected (%.2g).  Is qh_RANDOMmax (%.2g) wrong?\n",
+             randr, qh_RANDOMmax * 0.5, qh_RANDOMmax);
+  qh->RANDOMa= 2.0 * qh->RANDOMfactor/qh_RANDOMmax;
+  qh->RANDOMb= 1.0 - qh->RANDOMfactor;
+  if (qh_HASHfactor < 1.1) {
+    qh_fprintf(qh, qh->ferr, 6052, "qhull internal error (qh_initqhull_globals): qh_HASHfactor %d must be at least 1.1.  Qhull uses linear hash probing\n",
+      qh_HASHfactor);
+    qh_errexit(qh, qh_ERRqhull, NULL, NULL);
+  }
+  if (numpoints+extra < pointsneeded) {
+    qh_fprintf(qh, qh->ferr, 6214, "qhull input error: not enough points(%d) to construct initial simplex (need %d)\n",
+            numpoints, pointsneeded);
+    qh_errexit(qh, qh_ERRinput, NULL, NULL);
+  }
+  qh_initqhull_outputflags(qh);
+} /* initqhull_globals */
+
+/*-<a                             href="qh-globa_r.htm#TOC"
+  >-------------------------------</a><a name="initqhull_mem">-</a>
+
+  qh_initqhull_mem(qh )
+    initialize mem_r.c for qhull
+    qh.hull_dim and qh.normal_size determine some of the allocation sizes
+    if qh.MERGING,
+      includes ridgeT
+    calls qh_user_memsizes (user_r.c) 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= (int)sizeof(setT) + (qh->hull_dim - 1) * SETelemsize;  /* ridge.vertices */
+  qh_memsize(qh, i);
+  qh_memsize(qh, qh->normal_size);        /* normal */
+  i += SETelemsize;                 /* facet.vertices, .ridges, .neighbors */
+  qh_memsize(qh, i);
+  qh_user_memsizes(qh);
+  qh_memsetup(qh);
+} /* initqhull_mem */
+
+/*-<a                             href="qh-globa_r.htm#TOC"
+  >-------------------------------</a><a name="initqhull_outputflags">-</a>
+
+  qh_initqhull_outputflags
+    initialize flags concerned with output
+
+  returns:
+    adjust user flags as needed
+
+  see:
+    qh_clear_outputflags() resets the flags
+
+  design:
+    test for qh.PRINTgood (i.e., only print 'good' facets)
+    check for conflicting print output options
+*/
+void qh_initqhull_outputflags(qhT *qh) {
+  boolT printgeom= False, printmath= False, printcoplanar= False;
+  int i;
+
+  trace3((qh, qh->ferr, 3024, "qh_initqhull_outputflags: %s\n", qh->qhull_command));
+  if (!(qh->PRINTgood || qh->PRINTneighbors)) {
+    if (qh->DELAUNAY || qh->KEEParea || qh->KEEPminArea < REALmax/2 || qh->KEEPmerge
+        || (!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 option 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 option 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 option error: option 'Ft' is not available for Voronoi vertices ('v') or halfspace intersection ('H')\n");
+      qh_errexit(qh, qh_ERRinput, NULL, NULL);
+    }else if (qh->PRINTout[i] == qh_PRINTcentrums && qh->VORONOI) {
+      qh_fprintf(qh, qh->ferr, 6055, "qhull option 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 option 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 option 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 option 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 option 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 option 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 option warning: coplanars, vertices, and centrums output not 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 option warning: drop dimension 'GD%d' is only available for 3-d/4-d Geomview\n", qh->DROPdim);
+    }else
+      qh->DROPdim= -1;
+  }else if (qh->VORONOI) {
+    qh->DROPdim= qh->hull_dim-1;
+    qh->PRINTdim= qh->hull_dim-1;
+  }
+} /* qh_initqhull_outputflags */
+
+/*-<a                             href="qh-globa_r.htm#TOC"
+  >-------------------------------</a><a name="initqhull_start">-</a>
+
+  qh_initqhull_start(qh, infile, outfile, errfile )
+    allocate memory if needed and call qh_initqhull_start2()
+*/
+void qh_initqhull_start(qhT *qh, FILE *infile, FILE *outfile, FILE *errfile) {
+
+  qh_initstatistics(qh);
+  qh_initqhull_start2(qh, infile, outfile, errfile);
+} /* initqhull_start */
+
+/*-<a                             href="qh-globa_r.htm#TOC"
+  >-------------------------------</a><a name="initqhull_start2">-</a>
+
+  qh_initqhull_start2(qh, infile, outfile, errfile )
+    start initialization of qhull
+    initialize statistics, stdio, default values for global variables
+    assumes qh is allocated
+  notes:
+    report errors elsewhere, error handling and g_qhull_output [Qhull.cpp, QhullQh()] not in initialized
+  see:
+    qh_maxmin() determines the precision constants
+    qh_freeqhull()
+*/
+void qh_initqhull_start2(qhT *qh, FILE *infile, FILE *outfile, FILE *errfile) {
+  time_t timedata;
+  int seed;
+
+  qh_CPUclock; /* start the clock(for qh_clock).  One-shot. */
+  /* memset is the same in qh_freeqhull() and qh_initqhull_start2() */
+  memset((char *)qh, 0, sizeof(qhT)-sizeof(qhmemT)-sizeof(qhstatT));   /* every field is 0, FALSE, NULL */
+  qh->NOerrexit= True;
+  qh->DROPdim= -1;
+  qh->ferr= errfile;
+  qh->fin= infile;
+  qh->fout= outfile;
+  qh->furthest_id= qh_IDunknown;
+#ifndef qh_NOmerge
+  qh->JOGGLEmax= REALmax;
+#else
+  qh->JOGGLEmax= 0.0;  /* Joggle ('QJ') if qh_NOmerge */
+#endif
+  qh->KEEPminArea= REALmax;
+  qh->last_low= REALmax;
+  qh->last_high= REALmax;
+  qh->last_newhigh= REALmax;
+  qh->last_random= 1; /* reentrant only */
+  qh->lastcpu= 0.0;
+  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_IDnone;    /* recompile to trace a point, or use 'TPn' */
+  qh->tracefacet_id= UINT_MAX;  /* recompile to trace a facet, set to UINT_MAX when done, see userprintf_r.c/qh_fprintf */
+  qh->traceridge_id= UINT_MAX;  /* recompile to trace a ridge, set to UINT_MAX when done, see userprintf_r.c/qh_fprintf */
+  qh->tracevertex_id= UINT_MAX; /* recompile to trace a vertex, set to UINT_MAX when done, see userprintf_r.c/qh_fprintf */
+  seed= (int)time(&timedata);
+  qh_RANDOMseed_(qh, seed);
+  qh->run_id= qh_RANDOMint;
+  if(!qh->run_id)
+      qh->run_id++;  /* guarantee non-zero */
+  qh_option(qh, "run-id", &qh->run_id, NULL);
+  strcat(qh->qhull, "qhull");
+} /* initqhull_start2 */
+
+/*-<a                             href="qh-globa_r.htm#TOC"
+  >-------------------------------</a><a name="initthresholds">-</a>
+
+  qh_initthresholds(qh, commandString )
+    set thresholds for printing and scaling from commandString
+
+  returns:
+    sets qh.GOODthreshold or qh.SPLITthreshold if 'Pd0D1' used
+
+  see:
+    qh_initflags(), 'Qbk' 'QBk' 'Pdk' and 'PDk'
+    qh_inthresholds()
+
+  design:
+    for each 'Pdn' or 'PDn' option
+      check syntax
+      set qh.lower_threshold or qh.upper_threshold
+    set qh.GOODthreshold if an unbounded threshold is used
+    set qh.SPLITthreshold if a bounded threshold is used
+*/
+void qh_initthresholds(qhT *qh, char *command) {
+  realT value;
+  int idx, maxdim, k;
+  char *s= command; /* non-const due to strtol */
+  char *lastoption, *lastwarning= NULL;
+  char key;
+
+  maxdim= qh->input_dim;
+  if (qh->DELAUNAY && (qh->PROJECTdelaunay || qh->PROJECTinput))
+    maxdim++;
+  while (*s) {
+    if (*s == '-')
+      s++;
+    if (*s == 'P') {
+      lastoption= s++;
+      while (*s && !isspace(key= *s++)) {
+        if (key == 'd' || key == 'D') {
+          if (!isdigit(*s)) {
+            qh_fprintf(qh, qh->ferr, 7044, "qhull option warning: no dimension given for Print option 'P%c' at: %s.  Ignored\n",
+                    key, s-1);
+            lastwarning= lastoption;
+            continue;
+          }
+          idx= qh_strtol(s, &s);
+          if (idx >= qh->hull_dim) {
+            qh_fprintf(qh, qh->ferr, 7045, "qhull option warning: dimension %d for Print option 'P%c' is >= %d.  Ignored\n",
+                idx, key, qh->hull_dim);
+            lastwarning= lastoption;
+            continue;
+          }
+          if (*s == ':') {
+            s++;
+            value= qh_strtod(s, &s);
+            if (fabs((double)value) > 1.0) {
+              qh_fprintf(qh, qh->ferr, 7046, "qhull option warning: value %2.4g for Print option 'P%c' is > +1 or < -1.  Ignored\n",
+                      value, key);
+              lastwarning= lastoption;
+              continue;
+            }
+          }else
+            value= 0.0;
+          if (key == 'd')
+            qh->lower_threshold[idx]= value;
+          else
+            qh->upper_threshold[idx]= value;
+        }
+      }
+    }else if (*s == 'Q') {
+      lastoption= 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 option warning: no dimension given for Qhull option 'Q%c'\n",
+                    key);
+            lastwarning= lastoption;
+            continue;
+          }
+          idx= qh_strtol(s, &s);
+          if (idx >= maxdim) {
+            qh_fprintf(qh, qh->ferr, 7048, "qhull option warning: dimension %d for Qhull option 'Q%c' is >= %d.  Ignored\n",
+                idx, key, maxdim);
+            lastwarning= lastoption;
+            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;
+  }
+  if (lastwarning && !qh->ALLOWwarning) {
+    qh_fprintf(qh, qh->ferr, 6036, "qhull option error: see previous warnings, use 'Qw' to override: '%s' (last offset %d)\n",
+      command, (int)(lastwarning-command));
+    qh_errexit(qh, qh_ERRinput, NULL, NULL);
+  }
+} /* initthresholds */
+
+/*-<a                             href="qh-globa_r.htm#TOC"
+  >-------------------------------</a><a name="lib_check">-</a>
+
+  qh_lib_check( qhullLibraryType, qhTsize, vertexTsize, ridgeTsize, facetTsize, setTsize, qhmemTsize )
+    Report error if library does not agree with caller
+
+  notes:
+    NOerrors -- qh_lib_check can not call qh_errexit()
+*/
+void qh_lib_check(int qhullLibraryType, int qhTsize, int vertexTsize, int ridgeTsize, int facetTsize, int setTsize, int qhmemTsize) {
+    int last_errcode= qh_ERRnone;
+
+#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.  Qhull library is reentrant.\n");
+      last_errcode= 6257;
+    }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.  Qhull library is reentrant.\n");
+      last_errcode= 6258;
+    }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);
+      last_errcode= 6262;
+    }
+    if (qhTsize != (int)sizeof(qhT)) {
+      qh_fprintf_stderr(6249, "qh_lib_check: Incorrect qhull library called.  Size of qhT for caller is %d, but for qhull library is %d.\n", qhTsize, (int)sizeof(qhT));
+      last_errcode= 6249;
+    }
+    if (vertexTsize != (int)sizeof(vertexT)) {
+      qh_fprintf_stderr(6250, "qh_lib_check: Incorrect qhull library called.  Size of vertexT for caller is %d, but for qhull library is %d.\n", vertexTsize, (int)sizeof(vertexT));
+      last_errcode= 6250;
+    }
+    if (ridgeTsize != (int)sizeof(ridgeT)) {
+      qh_fprintf_stderr(6251, "qh_lib_check: Incorrect qhull library called.  Size of ridgeT for caller is %d, but for qhull library is %d.\n", ridgeTsize, (int)sizeof(ridgeT));
+      last_errcode= 6251;
+    }
+    if (facetTsize != (int)sizeof(facetT)) {
+      qh_fprintf_stderr(6252, "qh_lib_check: Incorrect qhull library called.  Size of facetT for caller is %d, but for qhull library is %d.\n", facetTsize, (int)sizeof(facetT));
+      last_errcode= 6252;
+    }
+    if (setTsize && setTsize != (int)sizeof(setT)) {
+      qh_fprintf_stderr(6253, "qh_lib_check: Incorrect qhull library called.  Size of setT for caller is %d, but for qhull library is %d.\n", setTsize, (int)sizeof(setT));
+      last_errcode= 6253;
+    }
+    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 qhull library is %d.\n", qhmemTsize, sizeof(qhmemT));
+      last_errcode= 6254;
+    }
+    if (last_errcode) {
+      qh_fprintf_stderr(6259, "qhull internal error (qh_lib_check): Cannot continue due to QH%d.  '%s' is not reentrant (e.g., qhull.so) or out-of-date.  Exit with %d\n",
+            last_errcode, qh_version2, last_errcode - 6200);
+      qh_exit(last_errcode - 6200);  /* can not use qh_errexit(), must be less than 255 */
+    }
+} /* lib_check */
+
+/*-<a                             href="qh-globa_r.htm#TOC"
+  >-------------------------------</a><a name="option">-</a>
+
+  qh_option(qh, option, intVal, realVal )
+    add an option description to qh.qhull_options
+
+  notes:
+    NOerrors -- qh_option can not call qh_errexit() [qh_initqhull_start2]
+    will be printed with statistics ('Ts') and errors
+    strlen(option) < 40
+*/
+void qh_option(qhT *qh, const char *option, int *i, realT *r) {
+  char buf[200];
+  int buflen, remainder;
+
+  if (strlen(option) > sizeof(buf)-30-30) {
+    qh_fprintf(qh, qh->ferr, 6408, "qhull internal error (qh_option): option (%d chars) has more than %d chars.  May overflow temporary buffer.  Option '%s'\n",
+        (int)strlen(option), (int)sizeof(buf)-30-30, option);
+    qh_errexit(qh, qh_ERRqhull, NULL, NULL);
+  }
+  sprintf(buf, "  %s", option);
+  if (i)
+    sprintf(buf+strlen(buf), " %d", *i);
+  if (r)
+    sprintf(buf+strlen(buf), " %2.2g", *r);
+  buflen= (int)strlen(buf);   /* WARN64 */
+  qh->qhull_optionlen += buflen;
+  remainder= (int)(sizeof(qh->qhull_options) - strlen(qh->qhull_options)) - 1;    /* WARN64 */
+  maximize_(remainder, 0);
+  if (qh->qhull_optionlen >= qh_OPTIONline && remainder > 0) {
+    strncat(qh->qhull_options, "\n", (unsigned int)remainder);
+    --remainder;
+    qh->qhull_optionlen= buflen;
+  }
+  if (buflen > remainder) {
+    trace1((qh, qh->ferr, 1058, "qh_option: option would overflow qh.qhull_options. Truncated '%s'\n", buf));
+  }
+  strncat(qh->qhull_options, buf, (unsigned int)remainder);
+} /* option */
+
+/*-<a                             href="qh-globa_r.htm#TOC"
+  >-------------------------------</a><a name="zero">-</a>
+
+  qh_zero( qh, errfile )
+    Initialize and zero Qhull's memory for qh_new_qhull()
+
+  notes:
+    Not needed in global_r.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 */
+

+ 4128 - 0
contrib/libs/qhull/libqhull_r/io_r.c

@@ -0,0 +1,4128 @@
+/*<html><pre>  -<a                             href="qh-io_r.htm"
+  >-------------------------------</a><a name="TOP">-</a>
+
+   io_r.c
+   Input/Output routines of qhull application
+
+   see qh-io_r.htm and io_r.h
+
+   see user_r.c for qh_errprint and qh_printfacetlist
+
+   unix_r.c calls qh_readpoints and qh_produce_output
+
+   unix_r.c and user_r.c are the only callers of io_r.c functions
+   This allows the user to avoid loading io_r.o from qhull.a
+
+   Copyright (c) 1993-2020 The Geometry Center.
+   $Id: //main/2019/qhull/src/libqhull_r/io_r.c#12 $$Change: 2965 $
+   $DateTime: 2020/06/04 15:37:41 $$Author: bbarber $
+*/
+
+#include "qhull_ra.h"
+
+/*========= -functions in alphabetical order after qh_produce_output(qh)  =====*/
+
+/*-<a                             href="qh-io_r.htm#TOC"
+  >-------------------------------</a><a name="produce_output">-</a>
+
+  qh_produce_output(qh )
+  qh_produce_output2(qh )
+    prints out the result of qhull in desired format
+    qh_produce_output2 does not call qh_prepare_output
+      qh_checkpolygon is valid for 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(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;
+
+  fflush(NULL);
+  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);
+  fflush(NULL);
+
+  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= (int)sizeof(setT) + (qh->hull_dim - 1) * SETelemsize;
+    qh_fprintf(qh, qh->ferr, 8040, "\
+    size in bytes: merge %d ridge %d vertex %d facet %d\n\
+         normal %d ridge vertices %d facet vertices or neighbors %d\n",
+            (int)sizeof(mergeT), (int)sizeof(ridgeT),
+            (int)sizeof(vertexT), (int)sizeof(facetT),
+            qh->normal_size, d_1, d_1 + SETelemsize);
+  }
+  if (qh_setsize(qh, qh->qhmem.tempstack) != tempsize) {
+    qh_fprintf(qh, qh->ferr, 6065, "qhull internal error (qh_produce_output2): temporary sets not empty(%d)\n",
+             qh_setsize(qh, qh->qhmem.tempstack));
+    qh_errexit(qh, qh_ERRqhull, NULL, NULL);
+  }
+} /* produce_output2 */
+
+/*-<a                             href="qh-io_r.htm#TOC"
+  >-------------------------------</a><a name="dfacet">-</a>
+
+  qh_dfacet(qh, id )
+    print facet by id, for debugging
+
+*/
+void qh_dfacet(qhT *qh, unsigned int id) {
+  facetT *facet;
+
+  FORALLfacets {
+    if (facet->id == id) {
+      qh_printfacet(qh, qh->fout, facet);
+      break;
+    }
+  }
+} /* dfacet */
+
+
+/*-<a                             href="qh-io_r.htm#TOC"
+  >-------------------------------</a><a name="dvertex">-</a>
+
+  qh_dvertex(qh, id )
+    print vertex by id, for debugging
+*/
+void qh_dvertex(qhT *qh, unsigned int id) {
+  vertexT *vertex;
+
+  FORALLvertices {
+    if (vertex->id == id) {
+      qh_printvertex(qh, qh->fout, vertex);
+      break;
+    }
+  }
+} /* dvertex */
+
+
+/*-<a                             href="qh-io_r.htm#TOC"
+  >-------------------------------</a><a name="compare_facetarea">-</a>
+
+  qh_compare_facetarea( p1, p2 )
+    used by qsort() to order facets by area
+*/
+int qh_compare_facetarea(const void *p1, const void *p2) {
+  const facetT *a= *((facetT *const*)p1), *b= *((facetT *const*)p2);
+
+  if (!a->isarea)
+    return -1;
+  if (!b->isarea)
+    return 1;
+  if (a->f.area > b->f.area)
+    return 1;
+  else if (a->f.area == b->f.area)
+    return 0;
+  return -1;
+} /* compare_facetarea */
+
+/*-<a                             href="qh-io_r.htm#TOC"
+  >-------------------------------</a><a name="compare_facetvisit">-</a>
+
+  qh_compare_facetvisit( p1, p2 )
+    used by qsort() to order facets by visit id or id
+*/
+int qh_compare_facetvisit(const void *p1, const void *p2) {
+  const facetT *a= *((facetT *const*)p1), *b= *((facetT *const*)p2);
+  int i,j;
+
+  if (!(i= (int)a->visitid))
+    i= (int)(0 - a->id); /* sign distinguishes id from visitid */
+  if (!(j= (int)b->visitid))
+    j= (int)(0 - b->id);
+  return(i - j);
+} /* compare_facetvisit */
+
+/*-<a                             href="qh-io_r.htm#TOC"
+  >-------------------------------</a><a name="compare_nummerge">-</a>
+
+  qh_compare_nummerge( p1, p2 )
+    used by qsort() to order facets by number of merges
+
+notes:
+    called by qh_markkeep ('PMerge-keep')
+*/
+int qh_compare_nummerge(const void *p1, const void *p2) {
+  const facetT *a= *((facetT *const*)p1), *b= *((facetT *const*)p2);
+
+  return(a->nummerge - b->nummerge);
+} /* compare_nummerge */
+
+/*-<a                             href="qh-io_r.htm#TOC"
+  >-------------------------------</a><a name="copyfilename">-</a>
+
+  qh_copyfilename(qh, dest, size, source, length )
+    copy filename identified by qh_skipfilename()
+
+  notes:
+    see qh_skipfilename() for syntax
+*/
+void qh_copyfilename(qhT *qh, char *filename, int size, const char* source, int length) {
+  char c= *source;
+
+  if (length > size + 1) {
+      qh_fprintf(qh, qh->ferr, 6040, "qhull error: filename is more than %d characters, %s\n",  size-1, source);
+      qh_errexit(qh, qh_ERRinput, NULL, NULL);
+  }
+  strncpy(filename, source, (size_t)length);
+  filename[length]= '\0';
+  if (c == '\'' || c == '"') {
+    char *s= filename + 1;
+    char *t= filename;
+    while (*s) {
+      if (*s == c) {
+          if (s[-1] == '\\')
+              t[-1]= c;
+      }else
+          *t++= *s;
+      s++;
+    }
+    *t= '\0';
+  }
+} /* copyfilename */
+
+/*-<a                             href="qh-io_r.htm#TOC"
+  >-------------------------------</a><a name="countfacets">-</a>
+
+  qh_countfacets(qh, facetlist, facets, printall,
+          numfacets, numsimplicial, totneighbors, numridges, numcoplanar, numtricoplanars  )
+    count good facets for printing and set visitid
+    if allfacets, ignores qh_skipfacet()
+
+  notes:
+    qh_printsummary and qh_countfacets must match counts
+
+  returns:
+    numfacets, numsimplicial, total neighbors, numridges, coplanars
+    each facet with ->visitid indicating 1-relative position
+      ->visitid==0 indicates not good
+
+  notes
+    numfacets >= numsimplicial
+    if qh.NEWfacets,
+      does not count visible facets (matches qh_printafacet)
+
+  design:
+    for all facets on facetlist and in facets set
+      unless facet is skipped or visible (i.e., will be deleted)
+        mark facet->visitid
+        update counts
+*/
+void qh_countfacets(qhT *qh, facetT *facetlist, setT *facets, boolT printall,
+    int *numfacetsp, int *numsimplicialp, int *totneighborsp, int *numridgesp, int *numcoplanarsp, int *numtricoplanarsp) {
+  facetT *facet, **facetp;
+  int numfacets= 0, numsimplicial= 0, numridges= 0, totneighbors= 0, numcoplanars= 0, numtricoplanars= 0;
+
+  FORALLfacet_(facetlist) {
+    if ((facet->visible && qh->NEWfacets)
+    || (!printall && qh_skipfacet(qh, facet)))
+      facet->visitid= 0;
+    else {
+      facet->visitid= (unsigned int)(++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= (unsigned int)(++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 += (unsigned int)numfacets + 1;
+  *numfacetsp= numfacets;
+  *numsimplicialp= numsimplicial;
+  *totneighborsp= totneighbors;
+  *numridgesp= numridges;
+  *numcoplanarsp= numcoplanars;
+  *numtricoplanarsp= numtricoplanars;
+} /* countfacets */
+
+/*-<a                             href="qh-io_r.htm#TOC"
+  >-------------------------------</a><a name="detvnorm">-</a>
+
+  qh_detvnorm(qh, vertex, vertexA, centers, offset )
+    compute separating plane of the Voronoi diagram for a pair of input sites
+    centers= set of facets (i.e., Voronoi vertices)
+      facet->visitid= 0 iff vertex-at-infinity (i.e., unbounded)
+
+  assumes:
+    qh_ASvoronoi and qh_vertexneighbors() already set
+
+  returns:
+    norm
+      a pointer into qh.gm_matrix to qh.hull_dim-1 reals
+      copy the data before reusing qh.gm_matrix
+    offset
+      if 'QVn'
+        sign adjusted so that qh.GOODvertexp is inside
+      else
+        sign adjusted so that vertex is inside
+
+    qh.gm_matrix= simplex of points from centers relative to first center
+
+  notes:
+    in io_r.c so that code for 'v Tv' can be removed by removing io_r.c
+    returns pointer into qh.gm_matrix to avoid tracking of temporary memory
+
+  design:
+    determine midpoint of input sites
+    build points as the set of Voronoi vertices
+    select a simplex from points (if necessary)
+      include midpoint if the Voronoi region is unbounded
+    relocate the first vertex of the simplex to the origin
+    compute the normalized hyperplane through the simplex
+    orient the hyperplane toward 'QVn' or 'vertex'
+    if 'Tv' or 'Ts'
+      if bounded
+        test that hyperplane is the perpendicular bisector of the input sites
+      test that Voronoi vertices not in the simplex are still on the hyperplane
+    free up temporary memory
+*/
+pointT *qh_detvnorm(qhT *qh, vertexT *vertex, vertexT *vertexA, setT *centers, realT *offsetp) {
+  facetT *facet, **facetp;
+  int  i, k, pointid, pointidA, point_i, point_n;
+  setT *simplex= NULL;
+  pointT *point, **pointp, *point0, *midpoint, *normal, *inpoint;
+  coordT *coord, *gmcoord, *normalp;
+  setT *points= qh_settemp(qh, qh->TEMPsize);
+  boolT nearzero= False;
+  boolT unbounded= False;
+  int numcenters= 0;
+  int dim= qh->hull_dim - 1;
+  realT dist, offset, angle, zero= 0.0;
+
+  midpoint= qh->gm_matrix + qh->hull_dim * qh->hull_dim;  /* last row */
+  for (k=0; k < dim; k++)
+    midpoint[k]= (vertex->point[k] + vertexA->point[k])/2;
+  FOREACHfacet_(centers) {
+    numcenters++;
+    if (!facet->visitid)
+      unbounded= True;
+    else {
+      if (!facet->center)
+        facet->center= qh_facetcenter(qh, facet->vertices);
+      qh_setappend(qh, &points, facet->center);
+    }
+  }
+  if (numcenters > dim) {
+    simplex= qh_settemp(qh, qh->TEMPsize);
+    qh_setappend(qh, &simplex, vertex->point);
+    if (unbounded)
+      qh_setappend(qh, &simplex, midpoint);
+    qh_maxsimplex(qh, dim, points, NULL, 0, &simplex);
+    qh_setdelnth(qh, simplex, 0);
+  }else if (numcenters == dim) {
+    if (unbounded)
+      qh_setappend(qh, &points, midpoint);
+    simplex= points;
+  }else {
+    qh_fprintf(qh, qh->ferr, 6216, "qhull internal error (qh_detvnorm): too few points(%d) to compute separating plane\n", numcenters);
+    qh_errexit(qh, qh_ERRqhull, NULL, NULL);
+  }
+  i= 0;
+  gmcoord= qh->gm_matrix;
+  point0= SETfirstt_(simplex, pointT);
+  FOREACHpoint_(simplex) {
+    if (qh->IStracing >= 4)
+      qh_printmatrix(qh, qh->ferr, "qh_detvnorm: Voronoi vertex or midpoint",
+                              &point, 1, dim);
+    if (point != point0) {
+      qh->gm_row[i++]= gmcoord;
+      coord= point0;
+      for (k=dim; k--; )
+        *(gmcoord++)= *point++ - *coord++;
+    }
+  }
+  qh->gm_row[i]= gmcoord;  /* does not overlap midpoint, may be used later for qh_areasimplex */
+  normal= gmcoord;
+  qh_sethyperplane_gauss(qh, dim, qh->gm_row, point0, True,
+                normal, &offset, &nearzero);
+  /* nearzero is true for axis-parallel hyperplanes (e.g., a bounding box).  Should detect degenerate hyperplanes.  See 'Tv' check following */
+  if (qh->GOODvertexp == vertexA->point)
+    inpoint= vertexA->point;
+  else
+    inpoint= vertex->point;
+  zinc_(Zdistio);
+  dist= qh_distnorm(dim, inpoint, normal, &offset);
+  if (dist > 0) {
+    offset= -offset;
+    normalp= normal;
+    for (k=dim; k--; ) {
+      *normalp= -(*normalp);
+      normalp++;
+    }
+  }
+  if (qh->VERIFYoutput || qh->PRINTstatistics) {
+    pointid= qh_pointid(qh, vertex->point);
+    pointidA= qh_pointid(qh, vertexA->point);
+    if (!unbounded) {
+      zinc_(Zdiststat);
+      dist= qh_distnorm(dim, midpoint, normal, &offset);
+      if (dist < 0)
+        dist= -dist;
+      zzinc_(Zridgemid);
+      wwmax_(Wridgemidmax, dist);
+      wwadd_(Wridgemid, dist);
+      trace4((qh, qh->ferr, 4014, "qh_detvnorm: points %d %d midpoint dist %2.2g\n",
+                 pointid, pointidA, dist));
+      for (k=0; k < dim; k++)
+        midpoint[k]= vertexA->point[k] - vertex->point[k];  /* overwrites midpoint! */
+      qh_normalize(qh, midpoint, dim, False);
+      angle= qh_distnorm(dim, midpoint, normal, &zero); /* qh_detangle uses dim+1 */
+      if (angle < 0.0)
+        angle= angle + 1.0;
+      else
+        angle= angle - 1.0;
+      if (angle < 0.0)
+        angle= -angle;
+      trace4((qh, qh->ferr, 4015, "qh_detvnorm: points %d %d angle %2.2g nearzero %d\n",
+                 pointid, pointidA, angle, nearzero));
+      if (nearzero) {
+        zzinc_(Zridge0);
+        wwmax_(Wridge0max, angle);
+        wwadd_(Wridge0, angle);
+      }else {
+        zzinc_(Zridgeok)
+        wwmax_(Wridgeokmax, angle);
+        wwadd_(Wridgeok, angle);
+      }
+    }
+    if (simplex != points) {
+      FOREACHpoint_i_(qh, points) {
+        if (!qh_setin(simplex, point)) {
+          facet= SETelemt_(centers, point_i, facetT);
+          zinc_(Zdiststat);
+          dist= qh_distnorm(dim, point, normal, &offset);
+          if (dist < 0)
+            dist= -dist;
+          zzinc_(Zridge);
+          wwmax_(Wridgemax, dist);
+          wwadd_(Wridge, dist);
+          trace4((qh, qh->ferr, 4016, "qh_detvnorm: points %d %d Voronoi vertex %d dist %2.2g\n",
+                             pointid, pointidA, facet->visitid, dist));
+        }
+      }
+    }
+  }
+  *offsetp= offset;
+  if (simplex != points)
+    qh_settempfree(qh, &simplex);
+  qh_settempfree(qh, &points);
+  return normal;
+} /* detvnorm */
+
+/*-<a                             href="qh-io_r.htm#TOC"
+  >-------------------------------</a><a name="detvridge">-</a>
+
+  qh_detvridge(qh, vertexA )
+    determine Voronoi ridge from 'seen' neighbors of vertexA
+    include one vertex-at-infinite if an !neighbor->visitid
+
+  returns:
+    temporary set of centers (facets, i.e., Voronoi vertices)
+    sorted by center id
+*/
+setT *qh_detvridge(qhT *qh, vertexT *vertex) {
+  setT *centers= qh_settemp(qh, qh->TEMPsize);
+  setT *tricenters= qh_settemp(qh, qh->TEMPsize);
+  facetT *neighbor, **neighborp;
+  boolT firstinf= True;
+
+  FOREACHneighbor_(vertex) {
+    if (neighbor->seen) {
+      if (neighbor->visitid) {
+        if (!neighbor->tricoplanar || qh_setunique(qh, &tricenters, neighbor->center))
+          qh_setappend(qh, &centers, neighbor);
+      }else if (firstinf) {
+        firstinf= False;
+        qh_setappend(qh, &centers, neighbor);
+      }
+    }
+  }
+  qsort(SETaddr_(centers, facetT), (size_t)qh_setsize(qh, centers),
+             sizeof(facetT *), qh_compare_facetvisit);
+  qh_settempfree(qh, &tricenters);
+  return centers;
+} /* detvridge */
+
+/*-<a                             href="qh-io_r.htm#TOC"
+  >-------------------------------</a><a name="detvridge3">-</a>
+
+  qh_detvridge3(qh, atvertex, vertex )
+    determine 3-d Voronoi ridge from 'seen' neighbors of atvertex and vertex
+    include one vertex-at-infinite for !neighbor->visitid
+    assumes all facet->seen2= True
+
+  returns:
+    temporary set of centers (facets, i.e., Voronoi vertices)
+    listed in adjacency order (!oriented)
+    all facet->seen2= True
+
+  design:
+    mark all neighbors of atvertex
+    for each adjacent neighbor of both atvertex and vertex
+      if neighbor selected
+        add neighbor to set of Voronoi vertices
+*/
+setT *qh_detvridge3(qhT *qh, vertexT *atvertex, vertexT *vertex) {
+  setT *centers= qh_settemp(qh, qh->TEMPsize);
+  setT *tricenters= qh_settemp(qh, qh->TEMPsize);
+  facetT *neighbor, **neighborp, *facet= NULL;
+  boolT firstinf= True;
+
+  FOREACHneighbor_(atvertex)
+    neighbor->seen2= False;
+  FOREACHneighbor_(vertex) {
+    if (!neighbor->seen2) {
+      facet= neighbor;
+      break;
+    }
+  }
+  while (facet) {
+    facet->seen2= True;
+    if (neighbor->seen) {
+      if (facet->visitid) {
+        if (!facet->tricoplanar || qh_setunique(qh, &tricenters, facet->center))
+          qh_setappend(qh, &centers, facet);
+      }else if (firstinf) {
+        firstinf= False;
+        qh_setappend(qh, &centers, facet);
+      }
+    }
+    FOREACHneighbor_(facet) {
+      if (!neighbor->seen2) {
+        if (qh_setin(vertex->neighbors, neighbor))
+          break;
+        else
+          neighbor->seen2= True;
+      }
+    }
+    facet= neighbor;
+  }
+  if (qh->CHECKfrequently) {
+    FOREACHneighbor_(vertex) {
+      if (!neighbor->seen2) {
+          qh_fprintf(qh, qh->ferr, 6217, "qhull internal error (qh_detvridge3): neighbors of vertex p%d are not connected at facet %d\n",
+                 qh_pointid(qh, vertex->point), neighbor->id);
+        qh_errexit(qh, qh_ERRqhull, neighbor, NULL);
+      }
+    }
+  }
+  FOREACHneighbor_(atvertex)
+    neighbor->seen2= True;
+  qh_settempfree(qh, &tricenters);
+  return centers;
+} /* detvridge3 */
+
+/*-<a                             href="qh-io_r.htm#TOC"
+  >-------------------------------</a><a name="eachvoronoi">-</a>
+
+  qh_eachvoronoi(qh, fp, printvridge, vertex, visitall, innerouter, inorder )
+    if visitall,
+      visit all Voronoi ridges for vertex (i.e., an input site)
+    else
+      visit all unvisited Voronoi ridges for vertex
+      all vertex->seen= False if unvisited
+    assumes
+      all facet->seen= False
+      all facet->seen2= True (for qh_detvridge3)
+      all facet->visitid == 0 if vertex_at_infinity
+                         == index of Voronoi vertex
+                         >= qh.num_facets if ignored
+    innerouter:
+      qh_RIDGEall--  both inner (bounded) and outer(unbounded) ridges
+      qh_RIDGEinner- only inner
+      qh_RIDGEouter- only outer
+
+    if inorder
+      orders vertices for 3-d Voronoi diagrams
+
+  returns:
+    number of visited ridges (does not include previously visited ridges)
+
+    if printvridge,
+      calls printvridge( fp, vertex, vertexA, centers)
+        fp== any pointer (assumes FILE*)
+             fp may be NULL for QhullQh::qh_fprintf which calls appendQhullMessage
+        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) {
+              if (inorder && qh->hull_dim == 3+1) /* 3-d Voronoi diagram */
+                centers= qh_detvridge3(qh, atvertex, vertex);
+              else
+                centers= qh_detvridge(qh, vertex);
+              (*printvridge)(qh, fp, atvertex, vertex, centers, unbounded);
+              qh_settempfree(qh, &centers);
+            }
+          }
+        }
+      }
+    }
+  }
+  FOREACHneighbor_(atvertex)
+    neighbor->seen= False;
+  qh_settempfree(qh, &tricenters);
+  return totridges;
+} /* eachvoronoi */
+
+
+/*-<a                             href="qh-poly_r.htm#TOC"
+  >-------------------------------</a><a name="eachvoronoi_all">-</a>
+
+  qh_eachvoronoi_all(qh, fp, printvridge, isUpper, innerouter, inorder )
+    visit all Voronoi ridges
+
+    innerouter:
+      see qh_eachvoronoi()
+
+    if inorder
+      orders vertices for 3-d Voronoi diagrams
+
+  returns
+    total number of ridges
+
+    if isUpper == facet->upperdelaunay  (i.e., a Vornoi vertex)
+      facet->visitid= Voronoi vertex index(same as 'o' format)
+    else
+      facet->visitid= 0
+
+    if printvridge,
+      calls printvridge( fp, vertex, vertexA, centers)
+      [see qh_eachvoronoi]
+
+  notes:
+    Not used for qhull.exe
+    same effect as qh_printvdiagram but ridges not sorted by point id
+*/
+int qh_eachvoronoi_all(qhT *qh, FILE *fp, printvridgeT printvridge, boolT isUpper, qh_RIDGE innerouter, boolT inorder) {
+  facetT *facet;
+  vertexT *vertex;
+  int numcenters= 1;  /* vertex 0 is vertex-at-infinity */
+  int totridges= 0;
+
+  qh_clearcenters(qh, qh_ASvoronoi);
+  qh_vertexneighbors(qh);
+  maximize_(qh->visit_id, (unsigned int)qh->num_facets);
+  FORALLfacets {
+    facet->visitid= 0;
+    facet->seen= False;
+    facet->seen2= True;
+  }
+  FORALLfacets {
+    if (facet->upperdelaunay == isUpper)
+      facet->visitid= (unsigned int)(numcenters++);
+  }
+  FORALLvertices
+    vertex->seen= False;
+  FORALLvertices {
+    if (qh->GOODvertex > 0 && qh_pointid(qh, vertex->point)+1 != qh->GOODvertex)
+      continue;
+    totridges += qh_eachvoronoi(qh, fp, printvridge, vertex,
+                   !qh_ALL, innerouter, inorder);
+  }
+  return totridges;
+} /* eachvoronoi_all */
+
+/*-<a                             href="qh-io_r.htm#TOC"
+  >-------------------------------</a><a name="facet2point">-</a>
+
+  qh_facet2point(qh, facet, point0, point1, mindist )
+    return two projected temporary vertices for a 2-d facet
+    may be non-simplicial
+
+  returns:
+    point0 and point1 oriented and projected to the facet
+    returns mindist (maximum distance below plane)
+*/
+void qh_facet2point(qhT *qh, facetT *facet, pointT **point0, pointT **point1, realT *mindist) {
+  vertexT *vertex0, *vertex1;
+  realT dist;
+
+  if (facet->toporient ^ qh_ORIENTclock) {
+    vertex0= SETfirstt_(facet->vertices, vertexT);
+    vertex1= SETsecondt_(facet->vertices, vertexT);
+  }else {
+    vertex1= SETfirstt_(facet->vertices, vertexT);
+    vertex0= SETsecondt_(facet->vertices, vertexT);
+  }
+  zadd_(Zdistio, 2);
+  qh_distplane(qh, vertex0->point, facet, &dist);
+  *mindist= dist;
+  *point0= qh_projectpoint(qh, vertex0->point, facet, dist);
+  qh_distplane(qh, vertex1->point, facet, &dist);
+  minimize_(*mindist, dist);
+  *point1= qh_projectpoint(qh, vertex1->point, facet, dist);
+} /* facet2point */
+
+
+/*-<a                             href="qh-io_r.htm#TOC"
+  >-------------------------------</a><a name="facetvertices">-</a>
+
+  qh_facetvertices(qh, facetlist, facets, allfacets )
+    returns temporary set of vertices in a set and/or list of facets
+    if allfacets, ignores qh_skipfacet()
+
+  returns:
+    vertices with qh.vertex_visit
+
+  notes:
+    optimized for allfacets of facet_list
+
+  design:
+    if allfacets of facet_list
+      create vertex set from vertex_list
+    else
+      for each selected facet in facets or facetlist
+        append unvisited vertices to vertex set
+*/
+setT *qh_facetvertices(qhT *qh, facetT *facetlist, setT *facets, boolT allfacets) {
+  setT *vertices;
+  facetT *facet, **facetp;
+  vertexT *vertex, **vertexp;
+
+  qh->vertex_visit++;
+  if (facetlist == qh->facet_list && allfacets && !facets) {
+    vertices= qh_settemp(qh, qh->num_vertices);
+    FORALLvertices {
+      vertex->visitid= qh->vertex_visit;
+      qh_setappend(qh, &vertices, vertex);
+    }
+  }else {
+    vertices= qh_settemp(qh, qh->TEMPsize);
+    FORALLfacet_(facetlist) {
+      if (!allfacets && qh_skipfacet(qh, facet))
+        continue;
+      FOREACHvertex_(facet->vertices) {
+        if (vertex->visitid != qh->vertex_visit) {
+          vertex->visitid= qh->vertex_visit;
+          qh_setappend(qh, &vertices, vertex);
+        }
+      }
+    }
+  }
+  FOREACHfacet_(facets) {
+    if (!allfacets && qh_skipfacet(qh, facet))
+      continue;
+    FOREACHvertex_(facet->vertices) {
+      if (vertex->visitid != qh->vertex_visit) {
+        vertex->visitid= qh->vertex_visit;
+        qh_setappend(qh, &vertices, vertex);
+      }
+    }
+  }
+  return vertices;
+} /* facetvertices */
+
+/*-<a                             href="qh-geom_r.htm#TOC"
+  >-------------------------------</a><a name="geomplanes">-</a>
+
+  qh_geomplanes(qh, facet, outerplane, innerplane )
+    return outer and inner planes for Geomview
+    qh.PRINTradius is size of vertices and points (includes qh.JOGGLEmax)
+
+  notes:
+    assume precise calculations in io_r.c with roundoff covered by qh_GEOMepsilon
+*/
+void qh_geomplanes(qhT *qh, facetT *facet, realT *outerplane, realT *innerplane) {
+  realT radius;
+
+  if (qh->MERGING || qh->JOGGLEmax < REALmax/2) {
+    qh_outerinner(qh, facet, outerplane, innerplane);
+    radius= qh->PRINTradius;
+    if (qh->JOGGLEmax < REALmax/2)
+      radius -= qh->JOGGLEmax * sqrt((realT)qh->hull_dim);  /* already accounted for in qh_outerinner() */
+    *outerplane += radius;
+    *innerplane -= radius;
+    if (qh->PRINTcoplanar || qh->PRINTspheres) {
+      *outerplane += qh->MAXabs_coord * qh_GEOMepsilon;
+      *innerplane -= qh->MAXabs_coord * qh_GEOMepsilon;
+    }
+  }else
+    *innerplane= *outerplane= 0;
+} /* geomplanes */
+
+
+/*-<a                             href="qh-io_r.htm#TOC"
+  >-------------------------------</a><a name="markkeep">-</a>
+
+  qh_markkeep(qh, facetlist )
+    restrict good facets for qh.KEEParea, qh.KEEPmerge, and qh.KEEPminArea
+    ignores visible facets (!part of convex hull)
+
+  returns:
+    may clear facet->good
+    recomputes qh.num_good
+
+  notes:
+    only called by qh_prepare_output after qh_findgood_all
+    does not throw errors except memory/corruption of qset_r.c
+
+  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_nummerge);
+    if ((count= size - qh->KEEPmerge) > 0) {
+      FOREACHfacet_(facets) {
+        facet->good= False;
+        if (--count == 0)
+          break;
+      }
+    }
+  }
+  if (qh->KEEPminArea < REALmax/2) {
+    FOREACHfacet_(facets) {
+      if (!facet->isarea || facet->f.area < qh->KEEPminArea)
+        facet->good= False;
+    }
+  }
+  qh_settempfree(qh, &facets);
+  count= 0;
+  FORALLfacet_(facetlist) {
+    if (facet->good)
+      count++;
+  }
+  qh->num_good= count;
+} /* markkeep */
+
+
+/*-<a                             href="qh-io_r.htm#TOC"
+  >-------------------------------</a><a name="markvoronoi">-</a>
+
+  qh_markvoronoi(qh, facetlist, facets, printall, isLower, numcenters )
+    mark voronoi vertices for printing by site pairs
+
+  returns:
+    temporary set of vertices indexed by pointid
+    isLower set if printing lower hull (i.e., at least one facet is lower hull)
+    numcenters= total number of Voronoi vertices
+    bumps qh.printoutnum for vertex-at-infinity
+    clears all facet->seen and sets facet->seen2
+
+    if selected
+      facet->visitid= Voronoi vertex id
+    else if upper hull (or 'Qu' and lower hull)
+      facet->visitid= 0
+    else
+      facet->visitid >= qh->num_facets
+
+  notes:
+    ignores qh.ATinfinity, if defined
+*/
+setT *qh_markvoronoi(qhT *qh, facetT *facetlist, setT *facets, boolT printall, boolT *isLowerp, int *numcentersp) {
+  int numcenters=0;
+  facetT *facet, **facetp;
+  setT *vertices;
+  boolT isLower= False;
+
+  qh->printoutnum++;
+  qh_clearcenters(qh, qh_ASvoronoi);  /* in case, qh_printvdiagram2 called by user */
+  qh_vertexneighbors(qh);
+  vertices= qh_pointvertex(qh);
+  if (qh->ATinfinity)
+    SETelem_(vertices, qh->num_points-1)= NULL;
+  qh->visit_id++;
+  maximize_(qh->visit_id, (unsigned int)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= (unsigned int)(numcenters++);
+  }
+  FOREACHfacet_(facets) {
+    if (printall || !qh_skipfacet(qh, facet))
+      facet->visitid= (unsigned int)(numcenters++);
+  }
+  *isLowerp= isLower;
+  *numcentersp= numcenters;
+  trace2((qh, qh->ferr, 2007, "qh_markvoronoi: isLower %d numcenters %d\n", isLower, numcenters));
+  return vertices;
+} /* markvoronoi */
+
+/*-<a                             href="qh-io_r.htm#TOC"
+  >-------------------------------</a><a name="order_vertexneighbors">-</a>
+
+  qh_order_vertexneighbors(qh, vertex )
+    order facet neighbors of vertex by 2-d (orientation), 3-d (adjacency), or n-d (f.visitid,id)
+
+  notes:
+    error if qh_vertexneighbors not called beforehand
+    only 2-d orients the neighbors
+    for 4-d and higher
+      set or clear f.visitid for qh_compare_facetvisit
+      for example, use qh_markvoronoi (e.g., qh_printvornoi) or qh_countfacets (e.g., qh_printvneighbors)
+
+  design (2-d):
+    see qh_printextremes_2d
+  design (3-d):
+    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
+  design (n-d):
+    qsort by f.visitid, or f.facetid (qh_compare_facetvisit)
+    facet_id is negated (sorted before visit_id facets)
+*/
+void qh_order_vertexneighbors(qhT *qh, vertexT *vertex) {
+  setT *newset;
+  facetT *facet, *facetA, *facetB, *neighbor, **neighborp;
+  vertexT *vertexA;
+  int numneighbors;
+
+  trace4((qh, qh->ferr, 4018, "qh_order_vertexneighbors: order facet neighbors of v%d by 2-d (orientation), 3-d (adjacency), or n-d (f.visitid,id)\n", vertex->id));
+  if (!qh->VERTEXneighbors) {
+    qh_fprintf(qh, qh->ferr, 6428, "qhull internal error (qh_order_vertexneighbors): call qh_vertexneighbors before calling qh_order_vertexneighbors\n");
+    qh_errexit(qh, qh_ERRqhull, NULL, NULL);
+  }
+  if (qh->hull_dim == 2) {
+    facetA= SETfirstt_(vertex->neighbors, facetT);
+    if (facetA->toporient ^ qh_ORIENTclock)
+      vertexA= SETfirstt_(facetA->vertices, vertexT);
+    else
+      vertexA= SETsecondt_(facetA->vertices, vertexT);
+    if (vertexA!=vertex) {
+      facetB= SETsecondt_(vertex->neighbors, facetT);
+      SETfirst_(vertex->neighbors)= facetB;
+      SETsecond_(vertex->neighbors)= facetA;
+    }
+  }else if (qh->hull_dim == 3) {
+    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;
+  }else { /* qh.hull_dim >= 4 */
+    numneighbors= qh_setsize(qh, vertex->neighbors);
+    qsort(SETaddr_(vertex->neighbors, facetT), (size_t)numneighbors,
+        sizeof(facetT *), qh_compare_facetvisit);
+  }
+} /* order_vertexneighbors */
+
+/*-<a                             href="qh-io_r.htm#TOC"
+  >-------------------------------</a><a name="prepare_output">-</a>
+
+  qh_prepare_output(qh )
+    prepare for qh_produce_output2(qh) according to
+      qh.KEEPminArea, KEEParea, KEEPmerge, GOODvertex, GOODthreshold, GOODpoint, ONLYgood, SPLITthresholds
+    does not reset facet->good
+
+  notes
+    called by qh_produce_output, qh_new_qhull, Qhull.outputQhull
+    except for PRINTstatistics, no-op if previously called with same options
+*/
+void qh_prepare_output(qhT *qh) {
+  if (qh->VORONOI) {
+    qh_clearcenters(qh, qh_ASvoronoi);  /* must be before qh_triangulate */
+    qh_vertexneighbors(qh);
+  }
+  if (qh->TRIangulate && !qh->hasTriangulation) {
+    qh_triangulate(qh);
+    if (qh->VERIFYoutput && !qh->CHECKfrequently)
+      qh_checkpolygon(qh, qh->facet_list);
+  }
+  qh_findgood_all(qh, qh->facet_list);
+  if (qh->GETarea)
+    qh_getarea(qh, qh->facet_list);
+  if (qh->KEEParea || qh->KEEPmerge || qh->KEEPminArea < REALmax/2)
+    qh_markkeep(qh, qh->facet_list);
+  if (qh->PRINTstatistics)
+    qh_collectstatistics(qh);
+}
+
+/*-<a                             href="qh-io_r.htm#TOC"
+  >-------------------------------</a><a name="printafacet">-</a>
+
+  qh_printafacet(qh, fp, format, facet, printall )
+    print facet to fp in given output format (see qh.PRINTout)
+
+  returns:
+    nop if !printall and qh_skipfacet()
+    nop if visible facet and NEWfacets and format != PRINTfacets
+    must match qh_countfacets
+
+  notes
+    preserves qh.visit_id
+    facet->normal may be null if PREmerge/MERGEexact and STOPcone before merge
+
+  see
+    qh_printbegin() and qh_printend()
+
+  design:
+    test for printing facet
+    call appropriate routine for format
+    or output results directly
+*/
+void qh_printafacet(qhT *qh, FILE *fp, qh_PRINT format, facetT *facet, boolT printall) {
+  realT color[4], offset, dist, outerplane, innerplane;
+  boolT zerodiv;
+  coordT *point, *normp, *coordp, **pointp, *feasiblep;
+  int k;
+  vertexT *vertex, **vertexp;
+  facetT *neighbor, **neighborp;
+
+  if (!printall && qh_skipfacet(qh, facet))
+    return;
+  if (facet->visible && qh->NEWfacets && format != qh_PRINTfacets)
+    return;
+  qh->printoutnum++;
+  switch (format) {
+  case qh_PRINTarea:
+    if (facet->isarea) {
+      qh_fprintf(qh, fp, 9009, qh_REAL_1, facet->f.area);
+      qh_fprintf(qh, fp, 9010, "\n");
+    }else
+      qh_fprintf(qh, fp, 9011, "0\n");
+    break;
+  case qh_PRINTcoplanars:
+    qh_fprintf(qh, fp, 9012, "%d", qh_setsize(qh, facet->coplanarset));
+    FOREACHpoint_(facet->coplanarset)
+      qh_fprintf(qh, fp, 9013, " %d", qh_pointid(qh, point));
+    qh_fprintf(qh, fp, 9014, "\n");
+    break;
+  case qh_PRINTcentrums:
+    qh_printcenter(qh, fp, format, NULL, facet);
+    break;
+  case qh_PRINTfacets:
+    qh_printfacet(qh, fp, facet);
+    break;
+  case qh_PRINTfacets_xridge:
+    qh_printfacetheader(qh, fp, facet);
+    break;
+  case qh_PRINTgeom:  /* either 2 , 3, or 4-d by qh_printbegin */
+    if (!facet->normal)
+      break;
+    for (k=qh->hull_dim; k--; ) {
+      color[k]= (facet->normal[k]+1.0)/2.0;
+      maximize_(color[k], -1.0);
+      minimize_(color[k], +1.0);
+    }
+    qh_projectdim3(qh, color, color);
+    if (qh->PRINTdim != qh->hull_dim)
+      qh_normalize2(qh, color, 3, True, NULL, NULL);
+    if (qh->hull_dim <= 2)
+      qh_printfacet2geom(qh, fp, facet, color);
+    else if (qh->hull_dim == 3) {
+      if (facet->simplicial)
+        qh_printfacet3geom_simplicial(qh, fp, facet, color);
+      else
+        qh_printfacet3geom_nonsimplicial(qh, fp, facet, color);
+    }else {
+      if (facet->simplicial)
+        qh_printfacet4geom_simplicial(qh, fp, facet, color);
+      else
+        qh_printfacet4geom_nonsimplicial(qh, fp, facet, color);
+    }
+    break;
+  case qh_PRINTids:
+    qh_fprintf(qh, fp, 9015, "%d\n", facet->id);
+    break;
+  case qh_PRINTincidences:
+  case qh_PRINToff:
+  case qh_PRINTtriangles:
+    if (qh->hull_dim == 3 && format != qh_PRINTtriangles)
+      qh_printfacet3vertex(qh, fp, facet, format);
+    else if (facet->simplicial || qh->hull_dim == 2 || format == qh_PRINToff)
+      qh_printfacetNvertex_simplicial(qh, fp, facet, format);
+    else
+      qh_printfacetNvertex_nonsimplicial(qh, fp, facet, qh->printoutvar++, format);
+    break;
+  case qh_PRINTinner:
+    qh_outerinner(qh, facet, NULL, &innerplane);
+    offset= facet->offset - innerplane;
+    goto LABELprintnorm;
+    break; /* prevent warning */
+  case qh_PRINTmerges:
+    qh_fprintf(qh, fp, 9016, "%d\n", facet->nummerge);
+    break;
+  case qh_PRINTnormals:
+    offset= facet->offset;
+    goto LABELprintnorm;
+    break; /* prevent warning */
+  case qh_PRINTouter:
+    qh_outerinner(qh, facet, &outerplane, NULL);
+    offset= facet->offset - outerplane;
+  LABELprintnorm:
+    if (!facet->normal) {
+      qh_fprintf(qh, fp, 9017, "no normal for facet f%d\n", facet->id);
+      break;
+    }
+    if (qh->CDDoutput) {
+      qh_fprintf(qh, fp, 9018, qh_REAL_1, -offset);
+      for (k=0; k < qh->hull_dim; k++)
+        qh_fprintf(qh, fp, 9019, qh_REAL_1, -facet->normal[k]);
+    }else {
+      for (k=0; k < qh->hull_dim; k++)
+        qh_fprintf(qh, fp, 9020, qh_REAL_1, facet->normal[k]);
+      qh_fprintf(qh, fp, 9021, qh_REAL_1, offset);
+    }
+    qh_fprintf(qh, fp, 9022, "\n");
+    break;
+  case qh_PRINTmathematica:  /* either 2 or 3-d by qh_printbegin */
+  case qh_PRINTmaple:
+    if (qh->hull_dim == 2)
+      qh_printfacet2math(qh, fp, facet, format, qh->printoutvar++);
+    else
+      qh_printfacet3math(qh, fp, facet, format, qh->printoutvar++);
+    break;
+  case qh_PRINTneighbors:
+    qh_fprintf(qh, fp, 9023, "%d", qh_setsize(qh, facet->neighbors));
+    FOREACHneighbor_(facet)
+      qh_fprintf(qh, fp, 9024, " %d",
+               neighbor->visitid ? neighbor->visitid - 1: 0 - neighbor->id);
+    qh_fprintf(qh, fp, 9025, "\n");
+    break;
+  case qh_PRINTpointintersect:
+    if (!qh->feasible_point) {
+      qh_fprintf(qh, qh->ferr, 6067, "qhull input error (qh_printafacet): option 'Fp' needs qh->feasible_point\n");
+      qh_errexit(qh, qh_ERRinput, NULL, NULL);
+    }
+    if (facet->offset > 0)
+      goto LABELprintinfinite;
+    point= coordp= (coordT *)qh_memalloc(qh, qh->normal_size);
+    normp= facet->normal;
+    feasiblep= qh->feasible_point;
+    if (facet->offset < -qh->MINdenom) {
+      for (k=qh->hull_dim; k--; )
+        *(coordp++)= (*(normp++) / - facet->offset) + *(feasiblep++);
+    }else {
+      for (k=qh->hull_dim; k--; ) {
+        *(coordp++)= qh_divzero(*(normp++), facet->offset, qh->MINdenom_1,
+                                 &zerodiv) + *(feasiblep++);
+        if (zerodiv) {
+          qh_memfree(qh, point, qh->normal_size);
+          goto LABELprintinfinite;
+        }
+      }
+    }
+    qh_printpoint(qh, fp, NULL, point);
+    qh_memfree(qh, point, qh->normal_size);
+    break;
+  LABELprintinfinite:
+    for (k=qh->hull_dim; k--; )
+      qh_fprintf(qh, fp, 9026, qh_REAL_1, qh_INFINITE);
+    qh_fprintf(qh, fp, 9027, "\n");
+    break;
+  case qh_PRINTpointnearest:
+    FOREACHpoint_(facet->coplanarset) {
+      int id, id2;
+      vertex= qh_nearvertex(qh, facet, point, &dist);
+      id= qh_pointid(qh, vertex->point);
+      id2= qh_pointid(qh, point);
+      qh_fprintf(qh, fp, 9028, "%d %d %d " qh_REAL_1 "\n", id, id2, facet->id, dist);
+    }
+    break;
+  case qh_PRINTpoints:  /* VORONOI only by qh_printbegin */
+    if (qh->CDDoutput)
+      qh_fprintf(qh, fp, 9029, "1 ");
+    qh_printcenter(qh, fp, format, NULL, facet);
+    break;
+  case qh_PRINTvertices:
+    qh_fprintf(qh, fp, 9030, "%d", qh_setsize(qh, facet->vertices));
+    FOREACHvertex_(facet->vertices)
+      qh_fprintf(qh, fp, 9031, " %d", qh_pointid(qh, vertex->point));
+    qh_fprintf(qh, fp, 9032, "\n");
+    break;
+  default:
+    break;
+  }
+} /* printafacet */
+
+/*-<a                             href="qh-io_r.htm#TOC"
+  >-------------------------------</a><a name="printbegin">-</a>
+
+  qh_printbegin(qh )
+    prints header for all output formats
+
+  returns:
+    checks for valid format
+
+  notes:
+    uses qh.visit_id for 3/4off
+    changes qh.interior_point if printing centrums
+    qh_countfacets clears facet->visitid for non-good facets
+
+  see
+    qh_printend() and qh_printafacet()
+
+  design:
+    count facets and related statistics
+    print header for format
+*/
+void qh_printbegin(qhT *qh, FILE *fp, qh_PRINT format, facetT *facetlist, setT *facets, boolT printall) {
+  int numfacets, numsimplicial, numridges, totneighbors, numcoplanars, numtricoplanars;
+  int i, num;
+  facetT *facet, **facetp;
+  vertexT *vertex, **vertexp;
+  setT *vertices;
+  pointT *point, **pointp, *pointtemp;
+
+  qh->printoutnum= 0;
+  qh_countfacets(qh, facetlist, facets, printall, &numfacets, &numsimplicial,
+      &totneighbors, &numridges, &numcoplanars, &numtricoplanars);
+  switch (format) {
+  case qh_PRINTnone:
+    break;
+  case qh_PRINTarea:
+    qh_fprintf(qh, fp, 9033, "%d\n", numfacets);
+    break;
+  case qh_PRINTcoplanars:
+    qh_fprintf(qh, fp, 9034, "%d\n", numfacets);
+    break;
+  case qh_PRINTcentrums:
+    if (qh->CENTERtype == qh_ASnone)
+      qh_clearcenters(qh, qh_AScentrum);
+    qh_fprintf(qh, fp, 9035, "%d\n%d\n", qh->hull_dim, numfacets);
+    break;
+  case qh_PRINTfacets:
+  case qh_PRINTfacets_xridge:
+    if (facetlist)
+      qh_printvertexlist(qh, fp, "Vertices and facets:\n", facetlist, facets, printall);
+    break;
+  case qh_PRINTgeom:
+    if (qh->hull_dim > 4)  /* qh_initqhull_globals also checks */
+      goto LABELnoformat;
+    if (qh->VORONOI && qh->hull_dim > 3)  /* PRINTdim == DROPdim == hull_dim-1 */
+      goto LABELnoformat;
+    if (qh->hull_dim == 2 && (qh->PRINTridges || qh->DOintersections))
+      qh_fprintf(qh, qh->ferr, 7049, "qhull warning: output for ridges and intersections not implemented in 2-d\n");
+    if (qh->hull_dim == 4 && (qh->PRINTinner || qh->PRINTouter ||
+                             (qh->PRINTdim == 4 && qh->PRINTcentrums)))
+      qh_fprintf(qh, qh->ferr, 7050, "qhull warning: output for outer/inner planes and centrums not implemented in 4-d\n");
+    if (qh->PRINTdim == 4 && (qh->PRINTspheres))
+      qh_fprintf(qh, qh->ferr, 7051, "qhull warning: output for vertices not implemented in 4-d\n");
+    if (qh->PRINTdim == 4 && qh->DOintersections && qh->PRINTnoplanes)
+      qh_fprintf(qh, qh->ferr, 7052, "qhull warning: 'Gnh' generates no output in 4-d\n");
+    if (qh->PRINTdim == 2) {
+      qh_fprintf(qh, fp, 9036, "{appearance {linewidth 3} LIST # %s | %s\n",
+              qh->rbox_command, qh->qhull_command);
+    }else if (qh->PRINTdim == 3) {
+      qh_fprintf(qh, fp, 9037, "{appearance {+edge -evert linewidth 2} LIST # %s | %s\n",
+              qh->rbox_command, qh->qhull_command);
+    }else if (qh->PRINTdim == 4) {
+      qh->visit_id++;
+      num= 0;
+      FORALLfacet_(facetlist)    /* get number of ridges to be printed */
+        qh_printend4geom(qh, NULL, facet, &num, printall);
+      FOREACHfacet_(facets)
+        qh_printend4geom(qh, NULL, facet, &num, printall);
+      qh->ridgeoutnum= num;
+      qh->printoutvar= 0;  /* counts number of ridges in output */
+      qh_fprintf(qh, fp, 9038, "LIST # %s | %s\n", qh->rbox_command, qh->qhull_command);
+    }
+
+    if (qh->PRINTdots) {
+      qh->printoutnum++;
+      num= qh->num_points + qh_setsize(qh, qh->other_points);
+      if (qh->DELAUNAY && qh->ATinfinity)
+        num--;
+      if (qh->PRINTdim == 4)
+        qh_fprintf(qh, fp, 9039, "4VECT %d %d 1\n", num, num);
+      else
+        qh_fprintf(qh, fp, 9040, "VECT %d %d 1\n", num, num);
+
+      for (i=num; i--; ) {
+        if (i % 20 == 0)
+          qh_fprintf(qh, fp, 9041, "\n");
+        qh_fprintf(qh, fp, 9042, "1 ");
+      }
+      qh_fprintf(qh, fp, 9043, "# 1 point per line\n1 ");
+      for (i=num-1; i--; ) { /* num at least 3 for D2 */
+        if (i % 20 == 0)
+          qh_fprintf(qh, fp, 9044, "\n");
+        qh_fprintf(qh, fp, 9045, "0 ");
+      }
+      qh_fprintf(qh, fp, 9046, "# 1 color for all\n");
+      FORALLpoints {
+        if (!qh->DELAUNAY || !qh->ATinfinity || qh_pointid(qh, point) != qh->num_points-1) {
+          if (qh->PRINTdim == 4)
+            qh_printpoint(qh, fp, NULL, point);
+            else
+              qh_printpoint3(qh, fp, point);
+        }
+      }
+      FOREACHpoint_(qh->other_points) {
+        if (qh->PRINTdim == 4)
+          qh_printpoint(qh, fp, NULL, point);
+        else
+          qh_printpoint3(qh, fp, point);
+      }
+      qh_fprintf(qh, fp, 9047, "0 1 1 1  # color of points\n");
+    }
+
+    if (qh->PRINTdim == 4  && !qh->PRINTnoplanes)
+      /* 4dview loads up multiple 4OFF objects slowly */
+      qh_fprintf(qh, fp, 9048, "4OFF %d %d 1\n", 3*qh->ridgeoutnum, qh->ridgeoutnum);
+    qh->PRINTcradius= 2 * qh->DISTround;  /* include test DISTround */
+    if (qh->PREmerge) {
+      maximize_(qh->PRINTcradius, qh->premerge_centrum + qh->DISTround);
+    }else if (qh->POSTmerge)
+      maximize_(qh->PRINTcradius, qh->postmerge_centrum + qh->DISTround);
+    qh->PRINTradius= qh->PRINTcradius;
+    if (qh->PRINTspheres + qh->PRINTcoplanar)
+      maximize_(qh->PRINTradius, qh->MAXabs_coord * qh_MINradius);
+    if (qh->premerge_cos < REALmax/2) {
+      maximize_(qh->PRINTradius, (1- qh->premerge_cos) * qh->MAXabs_coord);
+    }else if (!qh->PREmerge && qh->POSTmerge && qh->postmerge_cos < REALmax/2) {
+      maximize_(qh->PRINTradius, (1- qh->postmerge_cos) * qh->MAXabs_coord);
+    }
+    maximize_(qh->PRINTradius, qh->MINvisible);
+    if (qh->JOGGLEmax < REALmax/2)
+      qh->PRINTradius += qh->JOGGLEmax * sqrt((realT)qh->hull_dim);
+    if (qh->PRINTdim != 4 &&
+        (qh->PRINTcoplanar || qh->PRINTspheres || qh->PRINTcentrums)) {
+      vertices= qh_facetvertices(qh, facetlist, facets, printall);
+      if (qh->PRINTspheres && qh->PRINTdim <= 3)
+        qh_printspheres(qh, fp, vertices, qh->PRINTradius);
+      if (qh->PRINTcoplanar || qh->PRINTcentrums) {
+        qh->firstcentrum= True;
+        if (qh->PRINTcoplanar&& !qh->PRINTspheres) {
+          FOREACHvertex_(vertices)
+            qh_printpointvect2(qh, fp, vertex->point, NULL, qh->interior_point, qh->PRINTradius);
+        }
+        FORALLfacet_(facetlist) {
+          if (!printall && qh_skipfacet(qh, facet))
+            continue;
+          if (!facet->normal)
+            continue;
+          if (qh->PRINTcentrums && qh->PRINTdim <= 3)
+            qh_printcentrum(qh, fp, facet, qh->PRINTcradius);
+          if (!qh->PRINTcoplanar)
+            continue;
+          FOREACHpoint_(facet->coplanarset)
+            qh_printpointvect2(qh, fp, point, facet->normal, NULL, qh->PRINTradius);
+          FOREACHpoint_(facet->outsideset)
+            qh_printpointvect2(qh, fp, point, facet->normal, NULL, qh->PRINTradius);
+        }
+        FOREACHfacet_(facets) {
+          if (!printall && qh_skipfacet(qh, facet))
+            continue;
+          if (!facet->normal)
+            continue;
+          if (qh->PRINTcentrums && qh->PRINTdim <= 3)
+            qh_printcentrum(qh, fp, facet, qh->PRINTcradius);
+          if (!qh->PRINTcoplanar)
+            continue;
+          FOREACHpoint_(facet->coplanarset)
+            qh_printpointvect2(qh, fp, point, facet->normal, NULL, qh->PRINTradius);
+          FOREACHpoint_(facet->outsideset)
+            qh_printpointvect2(qh, fp, point, facet->normal, NULL, qh->PRINTradius);
+        }
+      }
+      qh_settempfree(qh, &vertices);
+    }
+    qh->visit_id++; /* for printing hyperplane intersections */
+    break;
+  case qh_PRINTids:
+    qh_fprintf(qh, fp, 9049, "%d\n", numfacets);
+    break;
+  case qh_PRINTincidences:
+    if (qh->VORONOI && qh->PRINTprecision)
+      qh_fprintf(qh, qh->ferr, 7053, "qhull warning: input sites of Delaunay regions (option 'i').  Use option 'p' or 'o' for Voronoi centers.  Disable warning with option 'Pp'\n");
+    qh->printoutvar= (int)qh->vertex_id;  /* centrum id for 4-d+, non-simplicial facets */
+    if (qh->hull_dim <= 3)
+      qh_fprintf(qh, fp, 9050, "%d\n", numfacets);
+    else
+      qh_fprintf(qh, fp, 9051, "%d\n", numsimplicial+numridges);
+    break;
+  case qh_PRINTinner:
+  case qh_PRINTnormals:
+  case qh_PRINTouter:
+    if (qh->CDDoutput)
+      qh_fprintf(qh, fp, 9052, "%s | %s\nbegin\n    %d %d real\n", qh->rbox_command,
+            qh->qhull_command, numfacets, qh->hull_dim+1);
+    else
+      qh_fprintf(qh, fp, 9053, "%d\n%d\n", qh->hull_dim+1, numfacets);
+    break;
+  case qh_PRINTmathematica:
+  case qh_PRINTmaple:
+    if (qh->hull_dim > 3)  /* qh_initbuffers also checks */
+      goto LABELnoformat;
+    if (qh->VORONOI)
+      qh_fprintf(qh, qh->ferr, 7054, "qhull warning: output is the Delaunay triangulation\n");
+    if (format == qh_PRINTmaple) {
+      if (qh->hull_dim == 2)
+        qh_fprintf(qh, fp, 9054, "PLOT(CURVES(\n");
+      else
+        qh_fprintf(qh, fp, 9055, "PLOT3D(POLYGONS(\n");
+    }else
+      qh_fprintf(qh, fp, 9056, "{\n");
+    qh->printoutvar= 0;   /* counts number of facets for notfirst */
+    break;
+  case qh_PRINTmerges:
+    qh_fprintf(qh, fp, 9057, "%d\n", numfacets);
+    break;
+  case qh_PRINTpointintersect:
+    qh_fprintf(qh, fp, 9058, "%d\n%d\n", qh->hull_dim, numfacets);
+    break;
+  case qh_PRINTneighbors:
+    qh_fprintf(qh, fp, 9059, "%d\n", numfacets);
+    break;
+  case qh_PRINToff:
+  case qh_PRINTtriangles:
+    if (qh->VORONOI)
+      goto LABELnoformat;
+    num= qh->hull_dim;
+    if (format == qh_PRINToff || qh->hull_dim == 2)
+      qh_fprintf(qh, fp, 9060, "%d\n%d %d %d\n", num,
+        qh->num_points+qh_setsize(qh, qh->other_points), numfacets, totneighbors/2);
+    else { /* qh_PRINTtriangles */
+      qh->printoutvar= qh->num_points+qh_setsize(qh, qh->other_points); /* first centrum */
+      if (qh->DELAUNAY)
+        num--;  /* drop last dimension */
+      qh_fprintf(qh, fp, 9061, "%d\n%d %d %d\n", num, qh->printoutvar
+        + numfacets - numsimplicial, numsimplicial + numridges, totneighbors/2);
+    }
+    FORALLpoints
+      qh_printpointid(qh, qh->fout, NULL, num, point, qh_IDunknown);
+    FOREACHpoint_(qh->other_points)
+      qh_printpointid(qh, qh->fout, NULL, num, point, qh_IDunknown);
+    if (format == qh_PRINTtriangles && qh->hull_dim > 2) {
+      FORALLfacets {
+        if (!facet->simplicial && facet->visitid)
+          qh_printcenter(qh, qh->fout, format, NULL, facet);
+      }
+    }
+    break;
+  case qh_PRINTpointnearest:
+    qh_fprintf(qh, fp, 9062, "%d\n", numcoplanars);
+    break;
+  case qh_PRINTpoints:
+    if (!qh->VORONOI)
+      goto LABELnoformat;
+    if (qh->CDDoutput)
+      qh_fprintf(qh, fp, 9063, "%s | %s\nbegin\n%d %d real\n", qh->rbox_command,
+           qh->qhull_command, numfacets, qh->hull_dim);
+    else
+      qh_fprintf(qh, fp, 9064, "%d\n%d\n", qh->hull_dim-1, numfacets);
+    break;
+  case qh_PRINTvertices:
+    qh_fprintf(qh, fp, 9065, "%d\n", numfacets);
+    break;
+  case qh_PRINTsummary:
+  default:
+  LABELnoformat:
+    qh_fprintf(qh, qh->ferr, 6068, "qhull internal error (qh_printbegin): can not use this format for dimension %d\n",
+         qh->hull_dim);
+    qh_errexit(qh, qh_ERRqhull, NULL, NULL);
+  }
+} /* printbegin */
+
+/*-<a                             href="qh-io_r.htm#TOC"
+  >-------------------------------</a><a name="printcenter">-</a>
+
+  qh_printcenter(qh, fp, string, facet )
+    print facet->center as centrum or Voronoi center
+    string may be NULL.  Don't include '%' codes.
+    nop if qh->CENTERtype neither CENTERvoronoi nor CENTERcentrum
+    if upper envelope of Delaunay triangulation and point at-infinity
+      prints qh_INFINITE instead;
+
+  notes:
+    defines facet->center if needed
+    if format=PRINTgeom, adds a 0 if would otherwise be 2-d
+    Same as QhullFacet::printCenter
+*/
+void qh_printcenter(qhT *qh, FILE *fp, qh_PRINT format, const char *string, facetT *facet) {
+  int k, num;
+
+  if (qh->CENTERtype != qh_ASvoronoi && qh->CENTERtype != qh_AScentrum)
+    return;
+  if (string)
+    qh_fprintf(qh, fp, 9066, string);
+  if (qh->CENTERtype == qh_ASvoronoi) {
+    num= qh->hull_dim-1;
+    if (!facet->normal || !facet->upperdelaunay || !qh->ATinfinity) {
+      if (!facet->center)
+        facet->center= qh_facetcenter(qh, facet->vertices);
+      for (k=0; k < num; k++)
+        qh_fprintf(qh, fp, 9067, qh_REAL_1, facet->center[k]);
+    }else {
+      for (k=0; k < num; k++)
+        qh_fprintf(qh, fp, 9068, qh_REAL_1, qh_INFINITE);
+    }
+  }else /* qh.CENTERtype == qh_AScentrum */ {
+    num= qh->hull_dim;
+    if (format == qh_PRINTtriangles && qh->DELAUNAY)
+      num--;
+    if (!facet->center)
+      facet->center= qh_getcentrum(qh, facet);
+    for (k=0; k < num; k++)
+      qh_fprintf(qh, fp, 9069, qh_REAL_1, facet->center[k]);
+  }
+  if (format == qh_PRINTgeom && num == 2)
+    qh_fprintf(qh, fp, 9070, " 0\n");
+  else
+    qh_fprintf(qh, fp, 9071, "\n");
+} /* printcenter */
+
+/*-<a                             href="qh-io_r.htm#TOC"
+  >-------------------------------</a><a name="printcentrum">-</a>
+
+  qh_printcentrum(qh, fp, facet, radius )
+    print centrum for a facet in OOGL format
+    radius defines size of centrum
+    2-d or 3-d only
+
+  returns:
+    defines facet->center if needed
+*/
+void qh_printcentrum(qhT *qh, FILE *fp, facetT *facet, realT radius) {
+  pointT *centrum, *projpt;
+  boolT tempcentrum= False;
+  realT xaxis[4], yaxis[4], normal[4], dist;
+  realT green[3]={0, 1, 0};
+  vertexT *apex;
+  int k;
+
+  if (qh->CENTERtype == qh_AScentrum) {
+    if (!facet->center)
+      facet->center= qh_getcentrum(qh, facet);
+    centrum= facet->center;
+  }else {
+    centrum= qh_getcentrum(qh, facet);
+    tempcentrum= True;
+  }
+  qh_fprintf(qh, fp, 9072, "{appearance {-normal -edge normscale 0} ");
+  if (qh->firstcentrum) {
+    qh->firstcentrum= False;
+    qh_fprintf(qh, fp, 9073, "{INST geom { define centrum CQUAD  # f%d\n\
+-0.3 -0.3 0.0001     0 0 1 1\n\
+ 0.3 -0.3 0.0001     0 0 1 1\n\
+ 0.3  0.3 0.0001     0 0 1 1\n\
+-0.3  0.3 0.0001     0 0 1 1 } transform { \n", facet->id);
+  }else
+    qh_fprintf(qh, fp, 9074, "{INST geom { : centrum } transform { # f%d\n", facet->id);
+  apex= SETfirstt_(facet->vertices, vertexT);
+  qh_distplane(qh, apex->point, facet, &dist);
+  projpt= qh_projectpoint(qh, apex->point, facet, dist);
+  for (k=qh->hull_dim; k--; ) {
+    xaxis[k]= projpt[k] - centrum[k];
+    normal[k]= facet->normal[k];
+  }
+  if (qh->hull_dim == 2) {
+    xaxis[2]= 0;
+    normal[2]= 0;
+  }else if (qh->hull_dim == 4) {
+    qh_projectdim3(qh, xaxis, xaxis);
+    qh_projectdim3(qh, normal, normal);
+    qh_normalize2(qh, normal, qh->PRINTdim, True, NULL, NULL);
+  }
+  qh_crossproduct(3, xaxis, normal, yaxis);
+  qh_fprintf(qh, fp, 9075, "%8.4g %8.4g %8.4g 0\n", xaxis[0], xaxis[1], xaxis[2]);
+  qh_fprintf(qh, fp, 9076, "%8.4g %8.4g %8.4g 0\n", yaxis[0], yaxis[1], yaxis[2]);
+  qh_fprintf(qh, fp, 9077, "%8.4g %8.4g %8.4g 0\n", normal[0], normal[1], normal[2]);
+  qh_printpoint3(qh, fp, centrum);
+  qh_fprintf(qh, fp, 9078, "1 }}}\n");
+  qh_memfree(qh, projpt, qh->normal_size);
+  qh_printpointvect(qh, fp, centrum, facet->normal, NULL, radius, green);
+  if (tempcentrum)
+    qh_memfree(qh, centrum, qh->normal_size);
+} /* printcentrum */
+
+/*-<a                             href="qh-io_r.htm#TOC"
+  >-------------------------------</a><a name="printend">-</a>
+
+  qh_printend(qh, fp, format )
+    prints trailer for all output formats
+
+  see:
+    qh_printbegin() and qh_printafacet()
+
+*/
+void qh_printend(qhT *qh, FILE *fp, qh_PRINT format, facetT *facetlist, setT *facets, boolT printall) {
+  int num;
+  facetT *facet, **facetp;
+
+  if (!qh->printoutnum)
+    qh_fprintf(qh, qh->ferr, 7055, "qhull warning: no facets printed\n");
+  switch (format) {
+  case qh_PRINTgeom:
+    if (qh->hull_dim == 4 && qh->DROPdim < 0  && !qh->PRINTnoplanes) {
+      qh->visit_id++;
+      num= 0;
+      FORALLfacet_(facetlist)
+        qh_printend4geom(qh, fp, facet,&num, printall);
+      FOREACHfacet_(facets)
+        qh_printend4geom(qh, fp, facet, &num, printall);
+      if (num != qh->ridgeoutnum || qh->printoutvar != qh->ridgeoutnum) {
+        qh_fprintf(qh, qh->ferr, 6069, "qhull internal error (qh_printend): number of ridges %d != number printed %d and at end %d\n", qh->ridgeoutnum, qh->printoutvar, num);
+        qh_errexit(qh, qh_ERRqhull, NULL, NULL);
+      }
+    }else
+      qh_fprintf(qh, fp, 9079, "}\n");
+    break;
+  case qh_PRINTinner:
+  case qh_PRINTnormals:
+  case qh_PRINTouter:
+    if (qh->CDDoutput)
+      qh_fprintf(qh, fp, 9080, "end\n");
+    break;
+  case qh_PRINTmaple:
+    qh_fprintf(qh, fp, 9081, "));\n");
+    break;
+  case qh_PRINTmathematica:
+    qh_fprintf(qh, fp, 9082, "}\n");
+    break;
+  case qh_PRINTpoints:
+    if (qh->CDDoutput)
+      qh_fprintf(qh, fp, 9083, "end\n");
+    break;
+  default:
+    break;
+  }
+} /* printend */
+
+/*-<a                             href="qh-io_r.htm#TOC"
+  >-------------------------------</a><a name="printend4geom">-</a>
+
+  qh_printend4geom(qh, fp, facet, numridges, printall )
+    helper function for qh_printbegin/printend
+
+  returns:
+    number of printed ridges
+
+  notes:
+    just counts printed ridges if fp=NULL
+    uses facet->visitid
+    must agree with qh_printfacet4geom...
+
+  design:
+    computes color for facet from its normal
+    prints each ridge of facet
+*/
+void qh_printend4geom(qhT *qh, FILE *fp, facetT *facet, int *nump, boolT printall) {
+  realT color[3];
+  int i, num= *nump;
+  facetT *neighbor, **neighborp;
+  ridgeT *ridge, **ridgep;
+
+  if (!printall && qh_skipfacet(qh, facet))
+    return;
+  if (qh->PRINTnoplanes || (facet->visible && qh->NEWfacets))
+    return;
+  if (!facet->normal)
+    return;
+  if (fp) {
+    for (i=0; i < 3; i++) {
+      color[i]= (facet->normal[i]+1.0)/2.0;
+      maximize_(color[i], -1.0);
+      minimize_(color[i], +1.0);
+    }
+  }
+  facet->visitid= qh->visit_id;
+  if (facet->simplicial) {
+    FOREACHneighbor_(facet) {
+      if (neighbor->visitid != qh->visit_id) {
+        if (fp)
+          qh_fprintf(qh, fp, 9084, "3 %d %d %d %8.4g %8.4g %8.4g 1 # f%d f%d\n",
+                 3*num, 3*num+1, 3*num+2, color[0], color[1], color[2],
+                 facet->id, neighbor->id);
+        num++;
+      }
+    }
+  }else {
+    FOREACHridge_(facet->ridges) {
+      neighbor= otherfacet_(ridge, facet);
+      if (neighbor->visitid != qh->visit_id) {
+        if (fp)
+          qh_fprintf(qh, fp, 9085, "3 %d %d %d %8.4g %8.4g %8.4g 1 #r%d f%d f%d\n",
+                 3*num, 3*num+1, 3*num+2, color[0], color[1], color[2],
+                 ridge->id, facet->id, neighbor->id);
+        num++;
+      }
+    }
+  }
+  *nump= num;
+} /* printend4geom */
+
+/*-<a                             href="qh-io_r.htm#TOC"
+  >-------------------------------</a><a name="printextremes">-</a>
+
+  qh_printextremes(qh, fp, facetlist, facets, printall )
+    print extreme points for convex hulls or halfspace intersections
+
+  notes:
+    #points, followed by ids, one per line
+
+    sorted by id
+    same order as qh_printpoints_out if no coplanar/interior points
+*/
+void qh_printextremes(qhT *qh, FILE *fp, facetT *facetlist, setT *facets, boolT printall) {
+  setT *vertices, *points;
+  pointT *point;
+  vertexT *vertex, **vertexp;
+  int id;
+  int numpoints=0, point_i, point_n;
+  int allpoints= qh->num_points + qh_setsize(qh, qh->other_points);
+
+  points= qh_settemp(qh, allpoints);
+  qh_setzero(qh, points, 0, allpoints);
+  vertices= qh_facetvertices(qh, facetlist, facets, printall);
+  FOREACHvertex_(vertices) {
+    id= qh_pointid(qh, vertex->point);
+    if (id >= 0) {
+      SETelem_(points, id)= vertex->point;
+      numpoints++;
+    }
+  }
+  qh_settempfree(qh, &vertices);
+  qh_fprintf(qh, fp, 9086, "%d\n", numpoints);
+  FOREACHpoint_i_(qh, points) {
+    if (point)
+      qh_fprintf(qh, fp, 9087, "%d\n", point_i);
+  }
+  qh_settempfree(qh, &points);
+} /* printextremes */
+
+/*-<a                             href="qh-io_r.htm#TOC"
+  >-------------------------------</a><a name="printextremes_2d">-</a>
+
+  qh_printextremes_2d(qh, fp, facetlist, facets, printall )
+    prints point ids for facets in qh_ORIENTclock order
+
+  notes:
+    #points, followed by ids, one per line
+    if facetlist/facets are disjoint than the output includes skips
+    errors if facets form a loop
+    does not print coplanar points
+*/
+void qh_printextremes_2d(qhT *qh, FILE *fp, facetT *facetlist, setT *facets, boolT printall) {
+  int numfacets, numridges, totneighbors, numcoplanars, numsimplicial, numtricoplanars;
+  setT *vertices;
+  facetT *facet, *startfacet, *nextfacet;
+  vertexT *vertexA, *vertexB;
+
+  qh_countfacets(qh, facetlist, facets, printall, &numfacets, &numsimplicial,
+      &totneighbors, &numridges, &numcoplanars, &numtricoplanars); /* marks qh->visit_id */
+  vertices= qh_facetvertices(qh, facetlist, facets, printall);
+  qh_fprintf(qh, fp, 9088, "%d\n", qh_setsize(qh, vertices));
+  qh_settempfree(qh, &vertices);
+  if (!numfacets)
+    return;
+  facet= startfacet= facetlist ? facetlist : SETfirstt_(facets, facetT);
+  qh->vertex_visit++;
+  qh->visit_id++;
+  do {
+    if (facet->toporient ^ qh_ORIENTclock) {
+      vertexA= SETfirstt_(facet->vertices, vertexT);
+      vertexB= SETsecondt_(facet->vertices, vertexT);
+      nextfacet= SETfirstt_(facet->neighbors, facetT);
+    }else {
+      vertexA= SETsecondt_(facet->vertices, vertexT);
+      vertexB= SETfirstt_(facet->vertices, vertexT);
+      nextfacet= SETsecondt_(facet->neighbors, facetT);
+    }
+    if (facet->visitid == qh->visit_id) {
+      qh_fprintf(qh, qh->ferr, 6218, "qhull internal error (qh_printextremes_2d): loop in facet list.  facet %d nextfacet %d\n",
+                 facet->id, nextfacet->id);
+      qh_errexit2(qh, qh_ERRqhull, facet, nextfacet);
+    }
+    if (facet->visitid) {
+      if (vertexA->visitid != qh->vertex_visit) {
+        vertexA->visitid= qh->vertex_visit;
+        qh_fprintf(qh, fp, 9089, "%d\n", qh_pointid(qh, vertexA->point));
+      }
+      if (vertexB->visitid != qh->vertex_visit) {
+        vertexB->visitid= qh->vertex_visit;
+        qh_fprintf(qh, fp, 9090, "%d\n", qh_pointid(qh, vertexB->point));
+      }
+    }
+    facet->visitid= qh->visit_id;
+    facet= nextfacet;
+  }while (facet && facet != startfacet);
+} /* printextremes_2d */
+
+/*-<a                             href="qh-io_r.htm#TOC"
+  >-------------------------------</a><a name="printextremes_d">-</a>
+
+  qh_printextremes_d(qh, fp, facetlist, facets, printall )
+    print extreme points of input sites for Delaunay triangulations
+
+  notes:
+    #points, followed by ids, one per line
+
+    unordered
+*/
+void qh_printextremes_d(qhT *qh, FILE *fp, facetT *facetlist, setT *facets, boolT printall) {
+  setT *vertices;
+  vertexT *vertex, **vertexp;
+  boolT upperseen, lowerseen;
+  facetT *neighbor, **neighborp;
+  int numpoints=0;
+
+  vertices= qh_facetvertices(qh, facetlist, facets, printall);
+  qh_vertexneighbors(qh);
+  FOREACHvertex_(vertices) {
+    upperseen= lowerseen= False;
+    FOREACHneighbor_(vertex) {
+      if (neighbor->upperdelaunay)
+        upperseen= True;
+      else
+        lowerseen= True;
+    }
+    if (upperseen && lowerseen) {
+      vertex->seen= True;
+      numpoints++;
+    }else
+      vertex->seen= False;
+  }
+  qh_fprintf(qh, fp, 9091, "%d\n", numpoints);
+  FOREACHvertex_(vertices) {
+    if (vertex->seen)
+      qh_fprintf(qh, fp, 9092, "%d\n", qh_pointid(qh, vertex->point));
+  }
+  qh_settempfree(qh, &vertices);
+} /* printextremes_d */
+
+/*-<a                             href="qh-io_r.htm#TOC"
+  >-------------------------------</a><a name="printfacet">-</a>
+
+  qh_printfacet(qh, fp, facet )
+    prints all fields of a facet to fp
+
+  notes:
+    ridges printed in neighbor order
+*/
+void qh_printfacet(qhT *qh, FILE *fp, facetT *facet) {
+
+  qh_printfacetheader(qh, fp, facet);
+  if (facet->ridges)
+    qh_printfacetridges(qh, fp, facet);
+} /* printfacet */
+
+
+/*-<a                             href="qh-io_r.htm#TOC"
+  >-------------------------------</a><a name="printfacet2geom">-</a>
+
+  qh_printfacet2geom(qh, fp, facet, color )
+    print facet as part of a 2-d VECT for Geomview
+
+    notes:
+      assume precise calculations in io_r.c with roundoff covered by qh_GEOMepsilon
+      mindist is calculated within io_r.c.  maxoutside is calculated elsewhere
+      so a DISTround error may have occurred.
+*/
+void qh_printfacet2geom(qhT *qh, FILE *fp, facetT *facet, realT color[3]) {
+  pointT *point0, *point1;
+  realT mindist, innerplane, outerplane;
+  int k;
+
+  qh_facet2point(qh, facet, &point0, &point1, &mindist);
+  qh_geomplanes(qh, facet, &outerplane, &innerplane);
+  if (qh->PRINTouter || (!qh->PRINTnoplanes && !qh->PRINTinner))
+    qh_printfacet2geom_points(qh, fp, point0, point1, facet, outerplane, color);
+  if (qh->PRINTinner || (!qh->PRINTnoplanes && !qh->PRINTouter &&
+                outerplane - innerplane > 2 * qh->MAXabs_coord * qh_GEOMepsilon)) {
+    for (k=3; k--; )
+      color[k]= 1.0 - color[k];
+    qh_printfacet2geom_points(qh, fp, point0, point1, facet, innerplane, color);
+  }
+  qh_memfree(qh, point1, qh->normal_size);
+  qh_memfree(qh, point0, qh->normal_size);
+} /* printfacet2geom */
+
+/*-<a                             href="qh-io_r.htm#TOC"
+  >-------------------------------</a><a name="printfacet2geom_points">-</a>
+
+  qh_printfacet2geom_points(qh, fp, point1, point2, facet, offset, color )
+    prints a 2-d facet as a VECT with 2 points at some offset.
+    The points are on the facet's plane.
+*/
+void qh_printfacet2geom_points(qhT *qh, FILE *fp, pointT *point1, pointT *point2,
+                               facetT *facet, realT offset, realT color[3]) {
+  pointT *p1= point1, *p2= point2;
+
+  qh_fprintf(qh, fp, 9093, "VECT 1 2 1 2 1 # f%d\n", facet->id);
+  if (offset != 0.0) {
+    p1= qh_projectpoint(qh, p1, facet, -offset);
+    p2= qh_projectpoint(qh, p2, facet, -offset);
+  }
+  qh_fprintf(qh, fp, 9094, "%8.4g %8.4g %8.4g\n%8.4g %8.4g %8.4g\n",
+           p1[0], p1[1], 0.0, p2[0], p2[1], 0.0);
+  if (offset != 0.0) {
+    qh_memfree(qh, p1, qh->normal_size);
+    qh_memfree(qh, p2, qh->normal_size);
+  }
+  qh_fprintf(qh, fp, 9095, "%8.4g %8.4g %8.4g 1.0\n", color[0], color[1], color[2]);
+} /* printfacet2geom_points */
+
+
+/*-<a                             href="qh-io_r.htm#TOC"
+  >-------------------------------</a><a name="printfacet2math">-</a>
+
+  qh_printfacet2math(qh, fp, facet, format, notfirst )
+    print 2-d Maple or Mathematica output for a facet
+    may be non-simplicial
+
+  notes:
+    use %16.8f since Mathematica 2.2 does not handle exponential format
+    see qh_printfacet3math
+*/
+void qh_printfacet2math(qhT *qh, FILE *fp, facetT *facet, qh_PRINT format, int notfirst) {
+  pointT *point0, *point1;
+  realT mindist;
+  const char *pointfmt;
+
+  qh_facet2point(qh, facet, &point0, &point1, &mindist);
+  if (notfirst)
+    qh_fprintf(qh, fp, 9096, ",");
+  if (format == qh_PRINTmaple)
+    pointfmt= "[[%16.8f, %16.8f], [%16.8f, %16.8f]]\n";
+  else
+    pointfmt= "Line[{{%16.8f, %16.8f}, {%16.8f, %16.8f}}]\n";
+  qh_fprintf(qh, fp, 9097, pointfmt, point0[0], point0[1], point1[0], point1[1]);
+  qh_memfree(qh, point1, qh->normal_size);
+  qh_memfree(qh, point0, qh->normal_size);
+} /* printfacet2math */
+
+
+/*-<a                             href="qh-io_r.htm#TOC"
+  >-------------------------------</a><a name="printfacet3geom_nonsimplicial">-</a>
+
+  qh_printfacet3geom_nonsimplicial(qh, fp, facet, color )
+    print Geomview OFF for a 3-d nonsimplicial facet.
+    if DOintersections, prints ridges to unvisited neighbors(qh->visit_id)
+
+  notes
+    uses facet->visitid for intersections and ridges
+*/
+void qh_printfacet3geom_nonsimplicial(qhT *qh, FILE *fp, facetT *facet, realT color[3]) {
+  ridgeT *ridge, **ridgep;
+  setT *projectedpoints, *vertices;
+  vertexT *vertex, **vertexp, *vertexA, *vertexB;
+  pointT *projpt, *point, **pointp;
+  facetT *neighbor;
+  realT dist, outerplane, innerplane;
+  int cntvertices, k;
+  realT black[3]={0, 0, 0}, green[3]={0, 1, 0};
+
+  qh_geomplanes(qh, facet, &outerplane, &innerplane);
+  vertices= qh_facet3vertex(qh, facet); /* oriented */
+  cntvertices= qh_setsize(qh, vertices);
+  projectedpoints= qh_settemp(qh, cntvertices);
+  FOREACHvertex_(vertices) {
+    zinc_(Zdistio);
+    qh_distplane(qh, vertex->point, facet, &dist);
+    projpt= qh_projectpoint(qh, vertex->point, facet, dist);
+    qh_setappend(qh, &projectedpoints, projpt);
+  }
+  if (qh->PRINTouter || (!qh->PRINTnoplanes && !qh->PRINTinner))
+    qh_printfacet3geom_points(qh, fp, projectedpoints, facet, outerplane, color);
+  if (qh->PRINTinner || (!qh->PRINTnoplanes && !qh->PRINTouter &&
+                outerplane - innerplane > 2 * qh->MAXabs_coord * qh_GEOMepsilon)) {
+    for (k=3; k--; )
+      color[k]= 1.0 - color[k];
+    qh_printfacet3geom_points(qh, fp, projectedpoints, facet, innerplane, color);
+  }
+  FOREACHpoint_(projectedpoints)
+    qh_memfree(qh, point, qh->normal_size);
+  qh_settempfree(qh, &projectedpoints);
+  qh_settempfree(qh, &vertices);
+  if ((qh->DOintersections || qh->PRINTridges)
+  && (!facet->visible || !qh->NEWfacets)) {
+    facet->visitid= qh->visit_id;
+    FOREACHridge_(facet->ridges) {
+      neighbor= otherfacet_(ridge, facet);
+      if (neighbor->visitid != qh->visit_id) {
+        if (qh->DOintersections)
+          qh_printhyperplaneintersection(qh, fp, facet, neighbor, ridge->vertices, black);
+        if (qh->PRINTridges) {
+          vertexA= SETfirstt_(ridge->vertices, vertexT);
+          vertexB= SETsecondt_(ridge->vertices, vertexT);
+          qh_printline3geom(qh, fp, vertexA->point, vertexB->point, green);
+        }
+      }
+    }
+  }
+} /* printfacet3geom_nonsimplicial */
+
+/*-<a                             href="qh-io_r.htm#TOC"
+  >-------------------------------</a><a name="printfacet3geom_points">-</a>
+
+  qh_printfacet3geom_points(qh, fp, points, facet, offset )
+    prints a 3-d facet as OFF Geomview object.
+    offset is relative to the facet's hyperplane
+    Facet is determined as a list of points
+*/
+void qh_printfacet3geom_points(qhT *qh, FILE *fp, setT *points, facetT *facet, realT offset, realT color[3]) {
+  int k, n= qh_setsize(qh, points), i;
+  pointT *point, **pointp;
+  setT *printpoints;
+
+  qh_fprintf(qh, fp, 9098, "{ OFF %d 1 1 # f%d\n", n, facet->id);
+  if (offset != 0.0) {
+    printpoints= qh_settemp(qh, n);
+    FOREACHpoint_(points)
+      qh_setappend(qh, &printpoints, qh_projectpoint(qh, point, facet, -offset));
+  }else
+    printpoints= points;
+  FOREACHpoint_(printpoints) {
+    for (k=0; k < qh->hull_dim; k++) {
+      if (k == qh->DROPdim)
+        qh_fprintf(qh, fp, 9099, "0 ");
+      else
+        qh_fprintf(qh, fp, 9100, "%8.4g ", point[k]);
+    }
+    if (printpoints != points)
+      qh_memfree(qh, point, qh->normal_size);
+    qh_fprintf(qh, fp, 9101, "\n");
+  }
+  if (printpoints != points)
+    qh_settempfree(qh, &printpoints);
+  qh_fprintf(qh, fp, 9102, "%d ", n);
+  for (i=0; i < n; i++)
+    qh_fprintf(qh, fp, 9103, "%d ", i);
+  qh_fprintf(qh, fp, 9104, "%8.4g %8.4g %8.4g 1.0 }\n", color[0], color[1], color[2]);
+} /* printfacet3geom_points */
+
+
+/*-<a                             href="qh-io_r.htm#TOC"
+  >-------------------------------</a><a name="printfacet3geom_simplicial">-</a>
+
+  qh_printfacet3geom_simplicial(qh )
+    print Geomview OFF for a 3-d simplicial facet.
+
+  notes:
+    may flip color
+    uses facet->visitid for intersections and ridges
+
+    assume precise calculations in io_r.c with roundoff covered by qh_GEOMepsilon
+    innerplane may be off by qh->DISTround.  Maxoutside is calculated elsewhere
+    so a DISTround error may have occurred.
+*/
+void qh_printfacet3geom_simplicial(qhT *qh, FILE *fp, facetT *facet, realT color[3]) {
+  setT *points, *vertices;
+  vertexT *vertex, **vertexp, *vertexA, *vertexB;
+  facetT *neighbor, **neighborp;
+  realT outerplane, innerplane;
+  realT black[3]={0, 0, 0}, green[3]={0, 1, 0};
+  int k;
+
+  qh_geomplanes(qh, facet, &outerplane, &innerplane);
+  vertices= qh_facet3vertex(qh, facet);
+  points= qh_settemp(qh, qh->TEMPsize);
+  FOREACHvertex_(vertices)
+    qh_setappend(qh, &points, vertex->point);
+  if (qh->PRINTouter || (!qh->PRINTnoplanes && !qh->PRINTinner))
+    qh_printfacet3geom_points(qh, fp, points, facet, outerplane, color);
+  if (qh->PRINTinner || (!qh->PRINTnoplanes && !qh->PRINTouter &&
+              outerplane - innerplane > 2 * qh->MAXabs_coord * qh_GEOMepsilon)) {
+    for (k=3; k--; )
+      color[k]= 1.0 - color[k];
+    qh_printfacet3geom_points(qh, fp, points, facet, innerplane, color);
+  }
+  qh_settempfree(qh, &points);
+  qh_settempfree(qh, &vertices);
+  if ((qh->DOintersections || qh->PRINTridges)
+  && (!facet->visible || !qh->NEWfacets)) {
+    facet->visitid= qh->visit_id;
+    FOREACHneighbor_(facet) {
+      if (neighbor->visitid != qh->visit_id) {
+        vertices= qh_setnew_delnthsorted(qh, facet->vertices, qh->hull_dim,
+                          SETindex_(facet->neighbors, neighbor), 0);
+        if (qh->DOintersections)
+           qh_printhyperplaneintersection(qh, fp, facet, neighbor, vertices, black);
+        if (qh->PRINTridges) {
+          vertexA= SETfirstt_(vertices, vertexT);
+          vertexB= SETsecondt_(vertices, vertexT);
+          qh_printline3geom(qh, fp, vertexA->point, vertexB->point, green);
+        }
+        qh_setfree(qh, &vertices);
+      }
+    }
+  }
+} /* printfacet3geom_simplicial */
+
+/*-<a                             href="qh-io_r.htm#TOC"
+  >-------------------------------</a><a name="printfacet3math">-</a>
+
+  qh_printfacet3math(qh, fp, facet, notfirst )
+    print 3-d Maple or Mathematica output for a facet
+
+  notes:
+    may be non-simplicial
+    use %16.8f since Mathematica 2.2 does not handle exponential format
+    see qh_printfacet2math
+*/
+void qh_printfacet3math(qhT *qh, FILE *fp, facetT *facet, qh_PRINT format, int notfirst) {
+  vertexT *vertex, **vertexp;
+  setT *points, *vertices;
+  pointT *point, **pointp;
+  boolT firstpoint= True;
+  realT dist;
+  const char *pointfmt, *endfmt;
+
+  if (notfirst)
+    qh_fprintf(qh, fp, 9105, ",\n");
+  vertices= qh_facet3vertex(qh, facet);
+  points= qh_settemp(qh, qh_setsize(qh, vertices));
+  FOREACHvertex_(vertices) {
+    zinc_(Zdistio);
+    qh_distplane(qh, vertex->point, facet, &dist);
+    point= qh_projectpoint(qh, vertex->point, facet, dist);
+    qh_setappend(qh, &points, point);
+  }
+  if (format == qh_PRINTmaple) {
+    qh_fprintf(qh, fp, 9106, "[");
+    pointfmt= "[%16.8f, %16.8f, %16.8f]";
+    endfmt= "]";
+  }else {
+    qh_fprintf(qh, fp, 9107, "Polygon[{");
+    pointfmt= "{%16.8f, %16.8f, %16.8f}";
+    endfmt= "}]";
+  }
+  FOREACHpoint_(points) {
+    if (firstpoint)
+      firstpoint= False;
+    else
+      qh_fprintf(qh, fp, 9108, ",\n");
+    qh_fprintf(qh, fp, 9109, pointfmt, point[0], point[1], point[2]);
+  }
+  FOREACHpoint_(points)
+    qh_memfree(qh, point, qh->normal_size);
+  qh_settempfree(qh, &points);
+  qh_settempfree(qh, &vertices);
+  qh_fprintf(qh, fp, 9110, "%s", endfmt);
+} /* printfacet3math */
+
+
+/*-<a                             href="qh-io_r.htm#TOC"
+  >-------------------------------</a><a name="printfacet3vertex">-</a>
+
+  qh_printfacet3vertex(qh, fp, facet, format )
+    print vertices in a 3-d facet as point ids
+
+  notes:
+    prints number of vertices first if format == qh_PRINToff
+    the facet may be non-simplicial
+*/
+void qh_printfacet3vertex(qhT *qh, FILE *fp, facetT *facet, qh_PRINT format) {
+  vertexT *vertex, **vertexp;
+  setT *vertices;
+
+  vertices= qh_facet3vertex(qh, facet);
+  if (format == qh_PRINToff)
+    qh_fprintf(qh, fp, 9111, "%d ", qh_setsize(qh, vertices));
+  FOREACHvertex_(vertices)
+    qh_fprintf(qh, fp, 9112, "%d ", qh_pointid(qh, vertex->point));
+  qh_fprintf(qh, fp, 9113, "\n");
+  qh_settempfree(qh, &vertices);
+} /* printfacet3vertex */
+
+
+/*-<a                             href="qh-io_r.htm#TOC"
+  >-------------------------------</a><a name="printfacet4geom_nonsimplicial">-</a>
+
+  qh_printfacet4geom_nonsimplicial(qh )
+    print Geomview 4OFF file for a 4d nonsimplicial facet
+    prints all ridges to unvisited neighbors (qh.visit_id)
+    if qh.DROPdim
+      prints in OFF format
+
+  notes:
+    must agree with printend4geom()
+*/
+void qh_printfacet4geom_nonsimplicial(qhT *qh, FILE *fp, facetT *facet, realT color[3]) {
+  facetT *neighbor;
+  ridgeT *ridge, **ridgep;
+  vertexT *vertex, **vertexp;
+  pointT *point;
+  int k;
+  realT dist;
+
+  facet->visitid= qh->visit_id;
+  if (qh->PRINTnoplanes || (facet->visible && qh->NEWfacets))
+    return;
+  FOREACHridge_(facet->ridges) {
+    neighbor= otherfacet_(ridge, facet);
+    if (neighbor->visitid == qh->visit_id)
+      continue;
+    if (qh->PRINTtransparent && !neighbor->good)
+      continue;
+    if (qh->DOintersections)
+      qh_printhyperplaneintersection(qh, fp, facet, neighbor, ridge->vertices, color);
+    else {
+      if (qh->DROPdim >= 0)
+        qh_fprintf(qh, fp, 9114, "OFF 3 1 1 # f%d\n", facet->id);
+      else {
+        qh->printoutvar++;
+        qh_fprintf(qh, fp, 9115, "# r%d between f%d f%d\n", ridge->id, facet->id, neighbor->id);
+      }
+      FOREACHvertex_(ridge->vertices) {
+        zinc_(Zdistio);
+        qh_distplane(qh, vertex->point,facet, &dist);
+        point=qh_projectpoint(qh, vertex->point,facet, dist);
+        for (k=0; k < qh->hull_dim; k++) {
+          if (k != qh->DROPdim)
+            qh_fprintf(qh, fp, 9116, "%8.4g ", point[k]);
+        }
+        qh_fprintf(qh, fp, 9117, "\n");
+        qh_memfree(qh, point, qh->normal_size);
+      }
+      if (qh->DROPdim >= 0)
+        qh_fprintf(qh, fp, 9118, "3 0 1 2 %8.4g %8.4g %8.4g\n", color[0], color[1], color[2]);
+    }
+  }
+} /* printfacet4geom_nonsimplicial */
+
+
+/*-<a                             href="qh-io_r.htm#TOC"
+  >-------------------------------</a><a name="printfacet4geom_simplicial">-</a>
+
+  qh_printfacet4geom_simplicial(qh, fp, facet, color )
+    print Geomview 4OFF file for a 4d simplicial facet
+    prints triangles for unvisited neighbors (qh.visit_id)
+
+  notes:
+    must agree with printend4geom()
+*/
+void qh_printfacet4geom_simplicial(qhT *qh, FILE *fp, facetT *facet, realT color[3]) {
+  setT *vertices;
+  facetT *neighbor, **neighborp;
+  vertexT *vertex, **vertexp;
+  int k;
+
+  facet->visitid= qh->visit_id;
+  if (qh->PRINTnoplanes || (facet->visible && qh->NEWfacets))
+    return;
+  FOREACHneighbor_(facet) {
+    if (neighbor->visitid == qh->visit_id)
+      continue;
+    if (qh->PRINTtransparent && !neighbor->good)
+      continue;
+    vertices= qh_setnew_delnthsorted(qh, facet->vertices, qh->hull_dim,
+                          SETindex_(facet->neighbors, neighbor), 0);
+    if (qh->DOintersections)
+      qh_printhyperplaneintersection(qh, fp, facet, neighbor, vertices, color);
+    else {
+      if (qh->DROPdim >= 0)
+        qh_fprintf(qh, fp, 9119, "OFF 3 1 1 # ridge between f%d f%d\n",
+                facet->id, neighbor->id);
+      else {
+        qh->printoutvar++;
+        qh_fprintf(qh, fp, 9120, "# ridge between f%d f%d\n", facet->id, neighbor->id);
+      }
+      FOREACHvertex_(vertices) {
+        for (k=0; k < qh->hull_dim; k++) {
+          if (k != qh->DROPdim)
+            qh_fprintf(qh, fp, 9121, "%8.4g ", vertex->point[k]);
+        }
+        qh_fprintf(qh, fp, 9122, "\n");
+      }
+      if (qh->DROPdim >= 0)
+        qh_fprintf(qh, fp, 9123, "3 0 1 2 %8.4g %8.4g %8.4g\n", color[0], color[1], color[2]);
+    }
+    qh_setfree(qh, &vertices);
+  }
+} /* printfacet4geom_simplicial */
+
+
+/*-<a                             href="qh-io_r.htm#TOC"
+  >-------------------------------</a><a name="printfacetNvertex_nonsimplicial">-</a>
+
+  qh_printfacetNvertex_nonsimplicial(qh, fp, facet, id, format )
+    print vertices for an N-d non-simplicial facet
+    triangulates each ridge to the id
+*/
+void qh_printfacetNvertex_nonsimplicial(qhT *qh, FILE *fp, facetT *facet, int id, qh_PRINT format) {
+  vertexT *vertex, **vertexp;
+  ridgeT *ridge, **ridgep;
+
+  if (facet->visible && qh->NEWfacets)
+    return;
+  FOREACHridge_(facet->ridges) {
+    if (format == qh_PRINTtriangles)
+      qh_fprintf(qh, fp, 9124, "%d ", qh->hull_dim);
+    qh_fprintf(qh, fp, 9125, "%d ", id);
+    if ((ridge->top == facet) ^ qh_ORIENTclock) {
+      FOREACHvertex_(ridge->vertices)
+        qh_fprintf(qh, fp, 9126, "%d ", qh_pointid(qh, vertex->point));
+    }else {
+      FOREACHvertexreverse12_(ridge->vertices)
+        qh_fprintf(qh, fp, 9127, "%d ", qh_pointid(qh, vertex->point));
+    }
+    qh_fprintf(qh, fp, 9128, "\n");
+  }
+} /* printfacetNvertex_nonsimplicial */
+
+
+/*-<a                             href="qh-io_r.htm#TOC"
+  >-------------------------------</a><a name="printfacetNvertex_simplicial">-</a>
+
+  qh_printfacetNvertex_simplicial(qh, fp, facet, format )
+    print vertices for an N-d simplicial facet
+    prints vertices for non-simplicial facets
+      2-d facets (orientation preserved by qh_mergefacet2d)
+      PRINToff ('o') for 4-d and higher
+*/
+void qh_printfacetNvertex_simplicial(qhT *qh, FILE *fp, facetT *facet, qh_PRINT format) {
+  vertexT *vertex, **vertexp;
+
+  if (format == qh_PRINToff || format == qh_PRINTtriangles)
+    qh_fprintf(qh, fp, 9129, "%d ", qh_setsize(qh, facet->vertices));
+  if ((facet->toporient ^ qh_ORIENTclock)
+  || (qh->hull_dim > 2 && !facet->simplicial)) {
+    FOREACHvertex_(facet->vertices)
+      qh_fprintf(qh, fp, 9130, "%d ", qh_pointid(qh, vertex->point));
+  }else {
+    FOREACHvertexreverse12_(facet->vertices)
+      qh_fprintf(qh, fp, 9131, "%d ", qh_pointid(qh, vertex->point));
+  }
+  qh_fprintf(qh, fp, 9132, "\n");
+} /* printfacetNvertex_simplicial */
+
+
+/*-<a                             href="qh-io_r.htm#TOC"
+  >-------------------------------</a><a name="printfacetheader">-</a>
+
+  qh_printfacetheader(qh, fp, facet )
+    prints header fields of a facet to fp
+
+  notes:
+    for 'f' output and debugging
+    Same as QhullFacet::printHeader()
+*/
+void qh_printfacetheader(qhT *qh, FILE *fp, facetT *facet) {
+  pointT *point, **pointp, *furthest;
+  facetT *neighbor, **neighborp;
+  realT dist;
+
+  if (facet == qh_MERGEridge) {
+    qh_fprintf(qh, fp, 9133, " MERGEridge\n");
+    return;
+  }else if (facet == qh_DUPLICATEridge) {
+    qh_fprintf(qh, fp, 9134, " DUPLICATEridge\n");
+    return;
+  }else if (!facet) {
+    qh_fprintf(qh, fp, 9135, " NULLfacet\n");
+    return;
+  }
+  qh->old_randomdist= qh->RANDOMdist;
+  qh->RANDOMdist= False;
+  qh_fprintf(qh, fp, 9136, "- f%d\n", facet->id);
+  qh_fprintf(qh, fp, 9137, "    - flags:");
+  if (facet->toporient)
+    qh_fprintf(qh, fp, 9138, " top");
+  else
+    qh_fprintf(qh, fp, 9139, " bottom");
+  if (facet->simplicial)
+    qh_fprintf(qh, fp, 9140, " simplicial");
+  if (facet->tricoplanar)
+    qh_fprintf(qh, fp, 9141, " tricoplanar");
+  if (facet->upperdelaunay)
+    qh_fprintf(qh, fp, 9142, " upperDelaunay");
+  if (facet->visible)
+    qh_fprintf(qh, fp, 9143, " visible");
+  if (facet->newfacet)
+    qh_fprintf(qh, fp, 9144, " newfacet");
+  if (facet->tested)
+    qh_fprintf(qh, fp, 9145, " tested");
+  if (!facet->good)
+    qh_fprintf(qh, fp, 9146, " notG");
+  if (facet->seen && qh->IStracing)
+    qh_fprintf(qh, fp, 9147, " seen");
+  if (facet->seen2 && qh->IStracing)
+    qh_fprintf(qh, fp, 9418, " seen2");
+  if (facet->isarea)
+    qh_fprintf(qh, fp, 9419, " isarea");
+  if (facet->coplanarhorizon)
+    qh_fprintf(qh, fp, 9148, " coplanarhorizon");
+  if (facet->mergehorizon)
+    qh_fprintf(qh, fp, 9149, " mergehorizon");
+  if (facet->cycledone)
+    qh_fprintf(qh, fp, 9420, " cycledone");
+  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_MAXnummerge)
+    qh_fprintf(qh, fp, 9427, "    - merges: %dmax\n", qh_MAXnummerge);
+  else 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) /* initial value */
+    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, " MERGEridge");
+    else if (neighbor == qh_DUPLICATEridge)
+      qh_fprintf(qh, fp, 9176, " DUPLICATEridge");
+    else
+      qh_fprintf(qh, fp, 9177, " f%d", neighbor->id);
+  }
+  qh_fprintf(qh, fp, 9178, "\n");
+  qh->RANDOMdist= qh->old_randomdist;
+} /* printfacetheader */
+
+
+/*-<a                             href="qh-io_r.htm#TOC"
+  >-------------------------------</a><a name="printfacetridges">-</a>
+
+  qh_printfacetridges(qh, fp, facet )
+    prints ridges of a facet to fp
+
+  notes:
+    ridges printed in neighbor order
+    assumes the ridges exist
+    for 'f' output
+    same as QhullFacet::printRidges
+*/
+void qh_printfacetridges(qhT *qh, FILE *fp, facetT *facet) {
+  facetT *neighbor, **neighborp;
+  ridgeT *ridge, **ridgep;
+  int numridges= 0;
+  int n;
+
+  if (facet->visible && qh->NEWfacets) {
+    qh_fprintf(qh, fp, 9179, "    - ridges (tentative ids):");
+    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) {
+            ridge->seen= True;
+            qh_printridge(qh, fp, ridge);
+            numridges++;
+          }
+        }
+      }
+    }
+    n= qh_setsize(qh, facet->ridges);
+    if (n == 1 && facet->newfacet && qh->NEWtentative) {
+      qh_fprintf(qh, fp, 9411, "     - horizon ridge to visible facet\n");
+    }
+    if (numridges != n) {
+      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");
+    }
+    /* non-3d ridges w/o non-simplicial neighbors */
+    FOREACHridge_(facet->ridges) {
+      if (!ridge->seen)
+        qh_printridge(qh, fp, ridge);
+    }
+  }
+} /* printfacetridges */
+
+/*-<a                             href="qh-io_r.htm#TOC"
+  >-------------------------------</a><a name="printfacets">-</a>
+
+  qh_printfacets(qh, fp, format, facetlist, facets, printall )
+    prints facetlist and/or facet set in output format
+
+  notes:
+    also used for specialized formats ('FO' and summary)
+    turns off 'Rn' option since want actual numbers
+*/
+void qh_printfacets(qhT *qh, FILE *fp, qh_PRINT format, facetT *facetlist, setT *facets, boolT printall) {
+  int numfacets, numsimplicial, numridges, totneighbors, numcoplanars, numtricoplanars;
+  facetT *facet, **facetp;
+  setT *vertices;
+  coordT *center;
+  realT outerplane, innerplane;
+
+  qh->old_randomdist= qh->RANDOMdist;
+  qh->RANDOMdist= False;
+  if (qh->CDDoutput && (format == qh_PRINTcentrums || format == qh_PRINTpointintersect || format == qh_PRINToff))
+    qh_fprintf(qh, qh->ferr, 7056, "qhull warning: CDD format is not available for centrums, halfspace\nintersections, and OFF file format.\n");
+  if (format == qh_PRINTnone)
+    ; /* print nothing */
+  else if (format == qh_PRINTaverage) {
+    vertices= qh_facetvertices(qh, facetlist, facets, printall);
+    center= qh_getcenter(qh, vertices);
+    qh_fprintf(qh, fp, 9186, "%d 1\n", qh->hull_dim);
+    qh_printpointid(qh, fp, NULL, qh->hull_dim, center, qh_IDunknown);
+    qh_memfree(qh, center, qh->normal_size);
+    qh_settempfree(qh, &vertices);
+  }else if (format == qh_PRINTextremes) {
+    if (qh->DELAUNAY)
+      qh_printextremes_d(qh, fp, facetlist, facets, printall);
+    else if (qh->hull_dim == 2)
+      qh_printextremes_2d(qh, fp, facetlist, facets, printall);
+    else
+      qh_printextremes(qh, fp, facetlist, facets, printall);
+  }else if (format == qh_PRINToptions)
+    qh_fprintf(qh, fp, 9187, "Options selected for Qhull %s:\n%s\n", qh_version, qh->qhull_options);
+  else if (format == qh_PRINTpoints && !qh->VORONOI)
+    qh_printpoints_out(qh, fp, facetlist, facets, printall);
+  else if (format == qh_PRINTqhull)
+    qh_fprintf(qh, fp, 9188, "%s | %s\n", qh->rbox_command, qh->qhull_command);
+  else if (format == qh_PRINTsize) {
+    qh_fprintf(qh, fp, 9189, "0\n2 ");
+    qh_fprintf(qh, fp, 9190, qh_REAL_1, qh->totarea);
+    qh_fprintf(qh, fp, 9191, qh_REAL_1, qh->totvol);
+    qh_fprintf(qh, fp, 9192, "\n");
+  }else if (format == qh_PRINTsummary) {
+    qh_countfacets(qh, facetlist, facets, printall, &numfacets, &numsimplicial,
+      &totneighbors, &numridges, &numcoplanars, &numtricoplanars);
+    vertices= qh_facetvertices(qh, facetlist, facets, printall);
+    qh_fprintf(qh, fp, 9193, "10 %d %d %d %d %d %d %d %d %d %d\n2 ", qh->hull_dim,
+                qh->num_points + qh_setsize(qh, qh->other_points),
+                qh->num_vertices, qh->num_facets - qh->num_visible,
+                qh_setsize(qh, vertices), numfacets, numcoplanars,
+                numfacets - numsimplicial, zzval_(Zdelvertextot),
+                numtricoplanars);
+    qh_settempfree(qh, &vertices);
+    qh_outerinner(qh, NULL, &outerplane, &innerplane);
+    qh_fprintf(qh, fp, 9194, qh_REAL_2n, outerplane, innerplane);
+  }else if (format == qh_PRINTvneighbors)
+    qh_printvneighbors(qh, fp, facetlist, facets, printall);
+  else if (qh->VORONOI && format == qh_PRINToff)
+    qh_printvoronoi(qh, fp, format, facetlist, facets, printall);
+  else if (qh->VORONOI && format == qh_PRINTgeom) {
+    qh_printbegin(qh, fp, format, facetlist, facets, printall);
+    qh_printvoronoi(qh, fp, format, facetlist, facets, printall);
+    qh_printend(qh, fp, format, facetlist, facets, printall);
+  }else if (qh->VORONOI
+  && (format == qh_PRINTvertices || format == qh_PRINTinner || format == qh_PRINTouter))
+    qh_printvdiagram(qh, fp, format, facetlist, facets, printall);
+  else {
+    qh_printbegin(qh, fp, format, facetlist, facets, printall);
+    FORALLfacet_(facetlist)
+      qh_printafacet(qh, fp, format, facet, printall);
+    FOREACHfacet_(facets)
+      qh_printafacet(qh, fp, format, facet, printall);
+    qh_printend(qh, fp, format, facetlist, facets, printall);
+  }
+  qh->RANDOMdist= qh->old_randomdist;
+} /* printfacets */
+
+
+/*-<a                             href="qh-io_r.htm#TOC"
+  >-------------------------------</a><a name="printhyperplaneintersection">-</a>
+
+  qh_printhyperplaneintersection(qh, fp, facet1, facet2, vertices, color )
+    print Geomview OFF or 4OFF for the intersection of two hyperplanes in 3-d or 4-d
+*/
+void qh_printhyperplaneintersection(qhT *qh, FILE *fp, facetT *facet1, facetT *facet2,
+                   setT *vertices, realT color[3]) {
+  realT costheta, denominator, dist1, dist2, s, t, mindenom, p[4];
+  vertexT *vertex, **vertexp;
+  int i, k;
+  boolT nearzero1, nearzero2;
+
+  costheta= qh_getangle(qh, facet1->normal, facet2->normal);
+  denominator= 1 - costheta * costheta;
+  i= qh_setsize(qh, vertices);
+  if (qh->hull_dim == 3)
+    qh_fprintf(qh, fp, 9195, "VECT 1 %d 1 %d 1 ", i, i);
+  else if (qh->hull_dim == 4 && qh->DROPdim >= 0)
+    qh_fprintf(qh, fp, 9196, "OFF 3 1 1 ");
+  else
+    qh->printoutvar++;
+  qh_fprintf(qh, fp, 9197, "# intersect f%d f%d\n", facet1->id, facet2->id);
+  mindenom= 1 / (10.0 * qh->MAXabs_coord);
+  FOREACHvertex_(vertices) {
+    zadd_(Zdistio, 2);
+    qh_distplane(qh, vertex->point, facet1, &dist1);
+    qh_distplane(qh, vertex->point, facet2, &dist2);
+    s= qh_divzero(-dist1 + costheta * dist2, denominator,mindenom,&nearzero1);
+    t= qh_divzero(-dist2 + costheta * dist1, denominator,mindenom,&nearzero2);
+    if (nearzero1 || nearzero2)
+      s= t= 0.0;
+    for (k=qh->hull_dim; k--; )
+      p[k]= vertex->point[k] + facet1->normal[k] * s + facet2->normal[k] * t;
+    if (qh->PRINTdim <= 3) {
+      qh_projectdim3(qh, p, p);
+      qh_fprintf(qh, fp, 9198, "%8.4g %8.4g %8.4g # ", p[0], p[1], p[2]);
+    }else
+      qh_fprintf(qh, fp, 9199, "%8.4g %8.4g %8.4g %8.4g # ", p[0], p[1], p[2], p[3]);
+    if (nearzero1+nearzero2)
+      qh_fprintf(qh, fp, 9200, "p%d(coplanar facets)\n", qh_pointid(qh, vertex->point));
+    else
+      qh_fprintf(qh, fp, 9201, "projected p%d\n", qh_pointid(qh, vertex->point));
+  }
+  if (qh->hull_dim == 3)
+    qh_fprintf(qh, fp, 9202, "%8.4g %8.4g %8.4g 1.0\n", color[0], color[1], color[2]);
+  else if (qh->hull_dim == 4 && qh->DROPdim >= 0)
+    qh_fprintf(qh, fp, 9203, "3 0 1 2 %8.4g %8.4g %8.4g 1.0\n", color[0], color[1], color[2]);
+} /* printhyperplaneintersection */
+
+/*-<a                             href="qh-io_r.htm#TOC"
+  >-------------------------------</a><a name="printline3geom">-</a>
+
+  qh_printline3geom(qh, fp, pointA, pointB, color )
+    prints a line as a VECT
+    prints 0's for qh.DROPdim
+
+  notes:
+    if pointA == pointB,
+      it's a 1 point VECT
+*/
+void qh_printline3geom(qhT *qh, FILE *fp, pointT *pointA, pointT *pointB, realT color[3]) {
+  int k;
+  realT pA[4], pB[4];
+
+  qh_projectdim3(qh, pointA, pA);
+  qh_projectdim3(qh, pointB, pB);
+  if ((fabs(pA[0] - pB[0]) > 1e-3) ||
+      (fabs(pA[1] - pB[1]) > 1e-3) ||
+      (fabs(pA[2] - pB[2]) > 1e-3)) {
+    qh_fprintf(qh, fp, 9204, "VECT 1 2 1 2 1\n");
+    for (k=0; k < 3; k++)
+       qh_fprintf(qh, fp, 9205, "%8.4g ", pB[k]);
+    qh_fprintf(qh, fp, 9206, " # p%d\n", qh_pointid(qh, pointB));
+  }else
+    qh_fprintf(qh, fp, 9207, "VECT 1 1 1 1 1\n");
+  for (k=0; k < 3; k++)
+    qh_fprintf(qh, fp, 9208, "%8.4g ", pA[k]);
+  qh_fprintf(qh, fp, 9209, " # p%d\n", qh_pointid(qh, pointA));
+  qh_fprintf(qh, fp, 9210, "%8.4g %8.4g %8.4g 1\n", color[0], color[1], color[2]);
+}
+
+/*-<a                             href="qh-io_r.htm#TOC"
+  >-------------------------------</a><a name="printneighborhood">-</a>
+
+  qh_printneighborhood(qh, fp, format, facetA, facetB, printall )
+    print neighborhood of one or two facets
+
+  notes:
+    calls qh_findgood_all()
+    bumps qh.visit_id
+*/
+void qh_printneighborhood(qhT *qh, FILE *fp, qh_PRINT format, facetT *facetA, facetT *facetB, boolT printall) {
+  facetT *neighbor, **neighborp, *facet;
+  setT *facets;
+
+  if (format == qh_PRINTnone)
+    return;
+  qh_findgood_all(qh, qh->facet_list);
+  if (facetA == facetB)
+    facetB= NULL;
+  facets= qh_settemp(qh, 2*(qh_setsize(qh, facetA->neighbors)+1));
+  qh->visit_id++;
+  for (facet=facetA; facet; facet= ((facet == facetA) ? facetB : NULL)) {
+    if (facet->visitid != qh->visit_id) {
+      facet->visitid= qh->visit_id;
+      qh_setappend(qh, &facets, facet);
+    }
+    FOREACHneighbor_(facet) {
+      if (neighbor->visitid == qh->visit_id)
+        continue;
+      neighbor->visitid= qh->visit_id;
+      if (printall || !qh_skipfacet(qh, neighbor))
+        qh_setappend(qh, &facets, neighbor);
+    }
+  }
+  qh_printfacets(qh, fp, format, NULL, facets, printall);
+  qh_settempfree(qh, &facets);
+} /* printneighborhood */
+
+/*-<a                             href="qh-io_r.htm#TOC"
+  >-------------------------------</a><a name="printpoint">-</a>
+
+  qh_printpoint(qh, fp, string, point )
+  qh_printpointid(qh, fp, string, dim, point, id )
+    prints the coordinates of a point
+
+  returns:
+    if string is defined
+      prints 'string p%d'.  Skips p%d if id=qh_IDunknown(-1) or qh_IDnone(-3)
+
+  notes:
+    nop if point is NULL
+    Same as QhullPoint's printPoint
+*/
+void qh_printpoint(qhT *qh, FILE *fp, const char *string, pointT *point) {
+  int id= qh_pointid(qh, point);
+
+  qh_printpointid(qh, fp, string, qh->hull_dim, point, id);
+} /* printpoint */
+
+void qh_printpointid(qhT *qh, FILE *fp, const char *string, int dim, pointT *point, int id) {
+  int k;
+  realT r; /*bug fix*/
+
+  if (!point)
+    return;
+  if (string) {
+    qh_fprintf(qh, fp, 9211, "%s", string);
+    if (id != qh_IDunknown && id != qh_IDnone)
+      qh_fprintf(qh, fp, 9212, " p%d: ", id);
+  }
+  for (k=dim; k--; ) {
+    r= *point++;
+    if (string)
+      qh_fprintf(qh, fp, 9213, " %8.4g", r);
+    else
+      qh_fprintf(qh, fp, 9214, qh_REAL_1, r);
+  }
+  qh_fprintf(qh, fp, 9215, "\n");
+} /* printpointid */
+
+/*-<a                             href="qh-io_r.htm#TOC"
+  >-------------------------------</a><a name="printpoint3">-</a>
+
+  qh_printpoint3(qh, fp, point )
+    prints 2-d, 3-d, or 4-d point as Geomview 3-d coordinates
+*/
+void qh_printpoint3(qhT *qh, FILE *fp, pointT *point) {
+  int k;
+  realT p[4];
+
+  qh_projectdim3(qh, point, p);
+  for (k=0; k < 3; k++)
+    qh_fprintf(qh, fp, 9216, "%8.4g ", p[k]);
+  qh_fprintf(qh, fp, 9217, " # p%d\n", qh_pointid(qh, point));
+} /* printpoint3 */
+
+/*----------------------------------------
+-printpoints- print pointids for a set of points starting at index
+   see geom_r.c
+*/
+
+/*-<a                             href="qh-io_r.htm#TOC"
+  >-------------------------------</a><a name="printpoints_out">-</a>
+
+  qh_printpoints_out(qh, fp, facetlist, facets, printall )
+    prints vertices, coplanar/inside points, for facets by their point coordinates
+    allows qh.CDDoutput
+
+  notes:
+    same format as qhull input
+    if no coplanar/interior points,
+      same order as qh_printextremes
+*/
+void qh_printpoints_out(qhT *qh, FILE *fp, facetT *facetlist, setT *facets, boolT printall) {
+  int allpoints= qh->num_points + qh_setsize(qh, qh->other_points);
+  int numpoints=0, point_i, point_n;
+  setT *vertices, *points;
+  facetT *facet, **facetp;
+  pointT *point, **pointp;
+  vertexT *vertex, **vertexp;
+  int id;
+
+  points= qh_settemp(qh, allpoints);
+  qh_setzero(qh, points, 0, allpoints);
+  vertices= qh_facetvertices(qh, facetlist, facets, printall);
+  FOREACHvertex_(vertices) {
+    id= qh_pointid(qh, vertex->point);
+    if (id >= 0)
+      SETelem_(points, id)= vertex->point;
+  }
+  if (qh->KEEPinside || qh->KEEPcoplanar || qh->KEEPnearinside) {
+    FORALLfacet_(facetlist) {
+      if (!printall && qh_skipfacet(qh, facet))
+        continue;
+      FOREACHpoint_(facet->coplanarset) {
+        id= qh_pointid(qh, point);
+        if (id >= 0)
+          SETelem_(points, id)= point;
+      }
+    }
+    FOREACHfacet_(facets) {
+      if (!printall && qh_skipfacet(qh, facet))
+        continue;
+      FOREACHpoint_(facet->coplanarset) {
+        id= qh_pointid(qh, point);
+        if (id >= 0)
+          SETelem_(points, id)= point;
+      }
+    }
+  }
+  qh_settempfree(qh, &vertices);
+  FOREACHpoint_i_(qh, points) {
+    if (point)
+      numpoints++;
+  }
+  if (qh->CDDoutput)
+    qh_fprintf(qh, fp, 9218, "%s | %s\nbegin\n%d %d real\n", qh->rbox_command,
+             qh->qhull_command, numpoints, qh->hull_dim + 1);
+  else
+    qh_fprintf(qh, fp, 9219, "%d\n%d\n", qh->hull_dim, numpoints);
+  FOREACHpoint_i_(qh, points) {
+    if (point) {
+      if (qh->CDDoutput)
+        qh_fprintf(qh, fp, 9220, "1 ");
+      qh_printpoint(qh, fp, NULL, point);
+    }
+  }
+  if (qh->CDDoutput)
+    qh_fprintf(qh, fp, 9221, "end\n");
+  qh_settempfree(qh, &points);
+} /* printpoints_out */
+
+
+/*-<a                             href="qh-io_r.htm#TOC"
+  >-------------------------------</a><a name="printpointvect">-</a>
+
+  qh_printpointvect(qh, fp, point, normal, center, radius, color )
+    prints a 2-d, 3-d, or 4-d point as 3-d VECT's relative to normal or to center point
+*/
+void qh_printpointvect(qhT *qh, FILE *fp, pointT *point, coordT *normal, pointT *center, realT radius, realT color[3]) {
+  realT diff[4], pointA[4];
+  int k;
+
+  for (k=qh->hull_dim; k--; ) {
+    if (center)
+      diff[k]= point[k]-center[k];
+    else if (normal)
+      diff[k]= normal[k];
+    else
+      diff[k]= 0;
+  }
+  if (center)
+    qh_normalize2(qh, diff, qh->hull_dim, True, NULL, NULL);
+  for (k=qh->hull_dim; k--; )
+    pointA[k]= point[k]+diff[k] * radius;
+  qh_printline3geom(qh, fp, point, pointA, color);
+} /* printpointvect */
+
+/*-<a                             href="qh-io_r.htm#TOC"
+  >-------------------------------</a><a name="printpointvect2">-</a>
+
+  qh_printpointvect2(qh, fp, point, normal, center, radius )
+    prints a 2-d, 3-d, or 4-d point as 2 3-d VECT's for an imprecise point
+*/
+void qh_printpointvect2(qhT *qh, FILE *fp, pointT *point, coordT *normal, pointT *center, realT radius) {
+  realT red[3]={1, 0, 0}, yellow[3]={1, 1, 0};
+
+  qh_printpointvect(qh, fp, point, normal, center, radius, red);
+  qh_printpointvect(qh, fp, point, normal, center, -radius, yellow);
+} /* printpointvect2 */
+
+/*-<a                             href="qh-io_r.htm#TOC"
+  >-------------------------------</a><a name="printridge">-</a>
+
+  qh_printridge(qh, fp, ridge )
+    prints the information in a ridge
+
+  notes:
+    for qh_printfacetridges()
+    same as operator<< [QhullRidge.cpp]
+*/
+void qh_printridge(qhT *qh, FILE *fp, ridgeT *ridge) {
+
+  qh_fprintf(qh, fp, 9222, "     - r%d", ridge->id);
+  if (ridge->tested)
+    qh_fprintf(qh, fp, 9223, " tested");
+  if (ridge->nonconvex)
+    qh_fprintf(qh, fp, 9224, " nonconvex");
+  if (ridge->mergevertex)
+    qh_fprintf(qh, fp, 9421, " mergevertex");
+  if (ridge->mergevertex2)
+    qh_fprintf(qh, fp, 9422, " mergevertex2");
+  if (ridge->simplicialtop)
+    qh_fprintf(qh, fp, 9425, " simplicialtop");
+  if (ridge->simplicialbot)
+    qh_fprintf(qh, fp, 9423, " simplicialbot");
+  qh_fprintf(qh, fp, 9225, "\n");
+  qh_printvertices(qh, fp, "           vertices:", ridge->vertices);
+  if (ridge->top && ridge->bottom)
+    qh_fprintf(qh, fp, 9226, "           between f%d and f%d\n",
+            ridge->top->id, ridge->bottom->id);
+} /* printridge */
+
+/*-<a                             href="qh-io_r.htm#TOC"
+  >-------------------------------</a><a name="printspheres">-</a>
+
+  qh_printspheres(qh, fp, vertices, radius )
+    prints 3-d vertices as OFF spheres
+
+  notes:
+    inflated octahedron from Stuart Levy earth/mksphere2
+*/
+void qh_printspheres(qhT *qh, FILE *fp, setT *vertices, realT radius) {
+  vertexT *vertex, **vertexp;
+
+  qh->printoutnum++;
+  qh_fprintf(qh, fp, 9227, "{appearance {-edge -normal normscale 0} {\n\
+INST geom {define vsphere OFF\n\
+18 32 48\n\
+\n\
+0 0 1\n\
+1 0 0\n\
+0 1 0\n\
+-1 0 0\n\
+0 -1 0\n\
+0 0 -1\n\
+0.707107 0 0.707107\n\
+0 -0.707107 0.707107\n\
+0.707107 -0.707107 0\n\
+-0.707107 0 0.707107\n\
+-0.707107 -0.707107 0\n\
+0 0.707107 0.707107\n\
+-0.707107 0.707107 0\n\
+0.707107 0.707107 0\n\
+0.707107 0 -0.707107\n\
+0 0.707107 -0.707107\n\
+-0.707107 0 -0.707107\n\
+0 -0.707107 -0.707107\n\
+\n\
+3 0 6 11\n\
+3 0 7 6 \n\
+3 0 9 7 \n\
+3 0 11 9\n\
+3 1 6 8 \n\
+3 1 8 14\n\
+3 1 13 6\n\
+3 1 14 13\n\
+3 2 11 13\n\
+3 2 12 11\n\
+3 2 13 15\n\
+3 2 15 12\n\
+3 3 9 12\n\
+3 3 10 9\n\
+3 3 12 16\n\
+3 3 16 10\n\
+3 4 7 10\n\
+3 4 8 7\n\
+3 4 10 17\n\
+3 4 17 8\n\
+3 5 14 17\n\
+3 5 15 14\n\
+3 5 16 15\n\
+3 5 17 16\n\
+3 6 13 11\n\
+3 7 8 6\n\
+3 9 10 7\n\
+3 11 12 9\n\
+3 14 8 17\n\
+3 15 13 14\n\
+3 16 12 15\n\
+3 17 10 16\n} transforms { TLIST\n");
+  FOREACHvertex_(vertices) {
+    qh_fprintf(qh, fp, 9228, "%8.4g 0 0 0 # v%d\n 0 %8.4g 0 0\n0 0 %8.4g 0\n",
+      radius, vertex->id, radius, radius);
+    qh_printpoint3(qh, fp, vertex->point);
+    qh_fprintf(qh, fp, 9229, "1\n");
+  }
+  qh_fprintf(qh, fp, 9230, "}}}\n");
+} /* printspheres */
+
+
+/*----------------------------------------------
+-printsummary-
+                see libqhull_r.c
+*/
+
+/*-<a                             href="qh-io_r.htm#TOC"
+  >-------------------------------</a><a name="printvdiagram">-</a>
+
+  qh_printvdiagram(qh, fp, format, facetlist, facets, printall )
+    print voronoi diagram
+      # of pairs of input sites
+      #indices site1 site2 vertex1 ...
+
+    sites indexed by input point id
+      point 0 is the first input point
+    vertices indexed by 'o' and 'p' order
+      vertex 0 is the 'vertex-at-infinity'
+      vertex 1 is the first Voronoi vertex
+
+  see:
+    qh_printvoronoi()
+    qh_eachvoronoi_all()
+
+  notes:
+    if all facets are upperdelaunay,
+      prints upper hull (furthest-site Voronoi diagram)
+*/
+void qh_printvdiagram(qhT *qh, FILE *fp, qh_PRINT format, facetT *facetlist, setT *facets, boolT printall) {
+  setT *vertices;
+  int totcount, numcenters;
+  boolT isLower;
+  qh_RIDGE innerouter= qh_RIDGEall;
+  printvridgeT printvridge= NULL;
+
+  if (format == qh_PRINTvertices) {
+    innerouter= qh_RIDGEall;
+    printvridge= qh_printvridge;
+  }else if (format == qh_PRINTinner) {
+    innerouter= qh_RIDGEinner;
+    printvridge= qh_printvnorm;
+  }else if (format == qh_PRINTouter) {
+    innerouter= qh_RIDGEouter;
+    printvridge= qh_printvnorm;
+  }else {
+    qh_fprintf(qh, qh->ferr, 6219, "qhull internal error (qh_printvdiagram): unknown print format %d.\n", format);
+    qh_errexit(qh, qh_ERRqhull, NULL, NULL);
+  }
+  vertices= qh_markvoronoi(qh, facetlist, facets, printall, &isLower, &numcenters);
+  totcount= qh_printvdiagram2(qh, NULL, NULL, vertices, innerouter, False);
+  qh_fprintf(qh, fp, 9231, "%d\n", totcount);
+  totcount= qh_printvdiagram2(qh, fp, printvridge, vertices, innerouter, True /* inorder*/);
+  qh_settempfree(qh, &vertices);
+#if 0  /* for testing qh_eachvoronoi_all */
+  qh_fprintf(qh, fp, 9232, "\n");
+  totcount= qh_eachvoronoi_all(qh, fp, printvridge, qh->UPPERdelaunay, innerouter, True /* inorder*/);
+  qh_fprintf(qh, fp, 9233, "%d\n", totcount);
+#endif
+} /* printvdiagram */
+
+/*-<a                             href="qh-io_r.htm#TOC"
+  >-------------------------------</a><a name="printvdiagram2">-</a>
+
+  qh_printvdiagram2(qh, fp, printvridge, vertices, innerouter, inorder )
+    visit all pairs of input sites (vertices) for selected Voronoi vertices
+    vertices may include NULLs
+
+  innerouter:
+    qh_RIDGEall   print inner ridges(bounded) and outer ridges(unbounded)
+    qh_RIDGEinner print only inner ridges
+    qh_RIDGEouter print only outer ridges
+
+  inorder:
+    print 3-d Voronoi vertices in order
+
+  assumes:
+    qh_markvoronoi marked facet->visitid for Voronoi vertices
+    all facet->seen= False
+    all facet->seen2= True
+
+  returns:
+    total number of Voronoi ridges
+    if printvridge,
+      calls printvridge( fp, vertex, vertexA, centers) for each ridge
+      [see qh_eachvoronoi()]
+
+  see:
+    qh_eachvoronoi_all()
+*/
+int qh_printvdiagram2(qhT *qh, FILE *fp, printvridgeT printvridge, setT *vertices, qh_RIDGE innerouter, boolT inorder) {
+  int totcount= 0;
+  int vertex_i, vertex_n;
+  vertexT *vertex;
+
+  FORALLvertices
+    vertex->seen= False;
+  FOREACHvertex_i_(qh, vertices) {
+    if (vertex) {
+      if (qh->GOODvertex > 0 && qh_pointid(qh, vertex->point)+1 != qh->GOODvertex)
+        continue;
+      totcount += qh_eachvoronoi(qh, fp, printvridge, vertex, !qh_ALL, innerouter, inorder);
+    }
+  }
+  return totcount;
+} /* printvdiagram2 */
+
+/*-<a                             href="qh-io_r.htm#TOC"
+  >-------------------------------</a><a name="printvertex">-</a>
+
+  qh_printvertex(qh, fp, vertex )
+    prints the information in a vertex
+    Duplicated as operator<< [QhullVertex.cpp]
+*/
+void qh_printvertex(qhT *qh, FILE *fp, vertexT *vertex) {
+  pointT *point;
+  int k, count= 0;
+  facetT *neighbor, **neighborp;
+  realT r; /*bug fix*/
+
+  if (!vertex) {
+    qh_fprintf(qh, fp, 9234, "  NULLvertex\n");
+    return;
+  }
+  qh_fprintf(qh, fp, 9235, "- p%d(v%d):", qh_pointid(qh, vertex->point), vertex->id);
+  point= vertex->point;
+  if (point) {
+    for (k=qh->hull_dim; k--; ) {
+      r= *point++;
+      qh_fprintf(qh, fp, 9236, " %5.2g", r);
+    }
+  }
+  if (vertex->deleted)
+    qh_fprintf(qh, fp, 9237, " deleted");
+  if (vertex->delridge)
+    qh_fprintf(qh, fp, 9238, " delridge");
+  if (vertex->newfacet)
+    qh_fprintf(qh, fp, 9415, " newfacet");
+  if (vertex->seen && qh->IStracing)
+    qh_fprintf(qh, fp, 9416, " seen");
+  if (vertex->seen2 && qh->IStracing)
+    qh_fprintf(qh, fp, 9417, " seen2");
+  qh_fprintf(qh, fp, 9239, "\n");
+  if (vertex->neighbors) {
+    qh_fprintf(qh, fp, 9240, "  neighbors:");
+    FOREACHneighbor_(vertex) {
+      if (++count % 100 == 0)
+        qh_fprintf(qh, fp, 9241, "\n     ");
+      qh_fprintf(qh, fp, 9242, " f%d", neighbor->id);
+    }
+    qh_fprintf(qh, fp, 9243, "\n");
+  }
+} /* printvertex */
+
+
+/*-<a                             href="qh-io_r.htm#TOC"
+  >-------------------------------</a><a name="printvertexlist">-</a>
+
+  qh_printvertexlist(qh, fp, string, facetlist, facets, printall )
+    prints vertices used by a facetlist or facet set
+    tests qh_skipfacet() if !printall
+*/
+void qh_printvertexlist(qhT *qh, FILE *fp, const char* string, facetT *facetlist,
+                         setT *facets, boolT printall) {
+  vertexT *vertex, **vertexp;
+  setT *vertices;
+
+  vertices= qh_facetvertices(qh, facetlist, facets, printall);
+  qh_fprintf(qh, fp, 9244, "%s", string);
+  FOREACHvertex_(vertices)
+    qh_printvertex(qh, fp, vertex);
+  qh_settempfree(qh, &vertices);
+} /* printvertexlist */
+
+
+/*-<a                             href="qh-io_r.htm#TOC"
+  >-------------------------------</a><a name="printvertices">-</a>
+
+  qh_printvertices(qh, fp, string, vertices )
+    prints vertices in a set
+    duplicated as printVertexSet [QhullVertex.cpp]
+*/
+void qh_printvertices(qhT *qh, FILE *fp, const char* string, setT *vertices) {
+  vertexT *vertex, **vertexp;
+
+  qh_fprintf(qh, fp, 9245, "%s", string);
+  FOREACHvertex_(vertices)
+    qh_fprintf(qh, fp, 9246, " p%d(v%d)", qh_pointid(qh, vertex->point), vertex->id);
+  qh_fprintf(qh, fp, 9247, "\n");
+} /* printvertices */
+
+/*-<a                             href="qh-io_r.htm#TOC"
+  >-------------------------------</a><a name="printvneighbors">-</a>
+
+  qh_printvneighbors(qh, fp, facetlist, facets, printall )
+    print vertex neighbors of vertices in facetlist and facets ('FN')
+
+  notes:
+    qh_countfacets clears facet->visitid for non-printed facets
+
+  design:
+    collect facet count and related statistics
+    if necessary, build neighbor sets for each vertex
+    collect vertices in facetlist and facets
+    build a point array for point->vertex and point->coplanar facet
+    for each point
+      list vertex neighbors or coplanar facet
+*/
+void qh_printvneighbors(qhT *qh, FILE *fp, facetT* facetlist, setT *facets, boolT printall) {
+  int numfacets, numsimplicial, numridges, totneighbors, numneighbors, numcoplanars, numtricoplanars;
+  setT *vertices, *vertex_points, *coplanar_points;
+  int numpoints= qh->num_points + qh_setsize(qh, qh->other_points);
+  vertexT *vertex, **vertexp;
+  int vertex_i, vertex_n;
+  facetT *facet, **facetp, *neighbor, **neighborp;
+  pointT *point, **pointp;
+
+  qh_countfacets(qh, facetlist, facets, printall, &numfacets, &numsimplicial,
+      &totneighbors, &numridges, &numcoplanars, &numtricoplanars);  /* sets facet->visitid */
+  qh_fprintf(qh, fp, 9248, "%d\n", numpoints);
+  qh_vertexneighbors(qh);
+  vertices= qh_facetvertices(qh, facetlist, facets, printall);
+  vertex_points= qh_settemp(qh, numpoints);
+  coplanar_points= qh_settemp(qh, numpoints);
+  qh_setzero(qh, vertex_points, 0, numpoints);
+  qh_setzero(qh, coplanar_points, 0, numpoints);
+  FOREACHvertex_(vertices)
+    qh_point_add(qh, vertex_points, vertex->point, vertex);
+  FORALLfacet_(facetlist) {
+    FOREACHpoint_(facet->coplanarset)
+      qh_point_add(qh, coplanar_points, point, facet);
+  }
+  FOREACHfacet_(facets) {
+    FOREACHpoint_(facet->coplanarset)
+      qh_point_add(qh, coplanar_points, point, facet);
+  }
+  FOREACHvertex_i_(qh, vertex_points) {
+    if (vertex) {
+      numneighbors= qh_setsize(qh, vertex->neighbors);
+      qh_fprintf(qh, fp, 9249, "%d", numneighbors);
+      qh_order_vertexneighbors(qh, vertex);
+      FOREACHneighbor_(vertex)
+        qh_fprintf(qh, fp, 9250, " %d",
+                 neighbor->visitid ? neighbor->visitid - 1 : 0 - neighbor->id);
+      qh_fprintf(qh, fp, 9251, "\n");
+    }else if ((facet= SETelemt_(coplanar_points, vertex_i, facetT)))
+      qh_fprintf(qh, fp, 9252, "1 %d\n",
+                  facet->visitid ? facet->visitid - 1 : 0 - facet->id);
+    else
+      qh_fprintf(qh, fp, 9253, "0\n");
+  }
+  qh_settempfree(qh, &coplanar_points);
+  qh_settempfree(qh, &vertex_points);
+  qh_settempfree(qh, &vertices);
+} /* printvneighbors */
+
+/*-<a                             href="qh-io_r.htm#TOC"
+  >-------------------------------</a><a name="printvoronoi">-</a>
+
+  qh_printvoronoi(qh, fp, format, facetlist, facets, printall )
+    print voronoi diagram in 'o' or 'G' format
+    for 'o' format
+      prints voronoi centers for each facet and for infinity
+      for each vertex, lists ids of printed facets or infinity
+      assumes facetlist and facets are disjoint
+    for 'G' format
+      prints an OFF object
+      adds a 0 coordinate to center
+      prints infinity but does not list in vertices
+
+  see:
+    qh_printvdiagram()
+
+  notes:
+    if 'o',
+      prints a line for each point except "at-infinity"
+    if all facets are upperdelaunay,
+      reverses lower and upper hull
+*/
+void qh_printvoronoi(qhT *qh, FILE *fp, qh_PRINT format, facetT *facetlist, setT *facets, boolT printall) {
+  int k, numcenters, numvertices= 0, numneighbors, numinf, vid=1, vertex_i, vertex_n;
+  facetT *facet, **facetp, *neighbor, **neighborp;
+  setT *vertices;
+  vertexT *vertex;
+  boolT isLower;
+  unsigned int numfacets= (unsigned int)qh->num_facets;
+
+  vertices= qh_markvoronoi(qh, facetlist, facets, printall, &isLower, &numcenters);
+  FOREACHvertex_i_(qh, vertices) {
+    if (vertex) {
+      numvertices++;
+      numneighbors= numinf= 0;
+      FOREACHneighbor_(vertex) {
+        if (neighbor->visitid == 0)
+          numinf= 1;
+        else if (neighbor->visitid < numfacets)
+          numneighbors++;
+      }
+      if (numinf && !numneighbors) {
+        SETelem_(vertices, vertex_i)= NULL;
+        numvertices--;
+      }
+    }
+  }
+  if (format == qh_PRINTgeom)
+    qh_fprintf(qh, fp, 9254, "{appearance {+edge -face} OFF %d %d 1 # Voronoi centers and cells\n",
+                numcenters, numvertices);
+  else
+    qh_fprintf(qh, fp, 9255, "%d\n%d %d 1\n", qh->hull_dim-1, numcenters, qh_setsize(qh, vertices));
+  if (format == qh_PRINTgeom) {
+    for (k=qh->hull_dim-1; k--; )
+      qh_fprintf(qh, fp, 9256, qh_REAL_1, 0.0);
+    qh_fprintf(qh, fp, 9257, " 0 # infinity not used\n");
+  }else {
+    for (k=qh->hull_dim-1; k--; )
+      qh_fprintf(qh, fp, 9258, qh_REAL_1, qh_INFINITE);
+    qh_fprintf(qh, fp, 9259, "\n");
+  }
+  FORALLfacet_(facetlist) {
+    if (facet->visitid && facet->visitid < numfacets) {
+      if (format == qh_PRINTgeom)
+        qh_fprintf(qh, fp, 9260, "# %d f%d\n", vid++, facet->id);
+      qh_printcenter(qh, fp, format, NULL, facet);
+    }
+  }
+  FOREACHfacet_(facets) {
+    if (facet->visitid && facet->visitid < numfacets) {
+      if (format == qh_PRINTgeom)
+        qh_fprintf(qh, fp, 9261, "# %d f%d\n", vid++, facet->id);
+      qh_printcenter(qh, fp, format, NULL, facet);
+    }
+  }
+  FOREACHvertex_i_(qh, vertices) {
+    numneighbors= 0;
+    numinf=0;
+    if (vertex) {
+      qh_order_vertexneighbors(qh, vertex);
+      FOREACHneighbor_(vertex) {
+        if (neighbor->visitid == 0)
+          numinf= 1;
+        else if (neighbor->visitid < numfacets)
+          numneighbors++;
+      }
+    }
+    if (format == qh_PRINTgeom) {
+      if (vertex) {
+        qh_fprintf(qh, fp, 9262, "%d", numneighbors);
+        FOREACHneighbor_(vertex) {
+          if (neighbor->visitid && neighbor->visitid < numfacets)
+            qh_fprintf(qh, fp, 9263, " %d", neighbor->visitid);
+        }
+        qh_fprintf(qh, fp, 9264, " # p%d(v%d)\n", vertex_i, vertex->id);
+      }else
+        qh_fprintf(qh, fp, 9265, " # p%d is coplanar or isolated\n", vertex_i);
+    }else {
+      if (numinf)
+        numneighbors++;
+      qh_fprintf(qh, fp, 9266, "%d", numneighbors);
+      if (vertex) {
+        FOREACHneighbor_(vertex) {
+          if (neighbor->visitid == 0) {
+            if (numinf) {
+              numinf= 0;
+              qh_fprintf(qh, fp, 9267, " %d", neighbor->visitid);
+            }
+          }else if (neighbor->visitid < numfacets)
+            qh_fprintf(qh, fp, 9268, " %d", neighbor->visitid);
+        }
+      }
+      qh_fprintf(qh, fp, 9269, "\n");
+    }
+  }
+  if (format == qh_PRINTgeom)
+    qh_fprintf(qh, fp, 9270, "}\n");
+  qh_settempfree(qh, &vertices);
+} /* printvoronoi */
+
+/*-<a                             href="qh-io_r.htm#TOC"
+  >-------------------------------</a><a name="printvnorm">-</a>
+
+  qh_printvnorm(qh, fp, vertex, vertexA, centers, unbounded )
+    print one separating plane of the Voronoi diagram for a pair of input sites
+    unbounded==True if centers includes vertex-at-infinity
+
+  assumes:
+    qh_ASvoronoi and qh_vertexneighbors() already set
+
+  note:
+    parameter unbounded is UNUSED by this callback
+
+  see:
+    qh_printvdiagram()
+    qh_eachvoronoi()
+*/
+void qh_printvnorm(qhT *qh, FILE *fp, vertexT *vertex, vertexT *vertexA, setT *centers, boolT unbounded) {
+  pointT *normal;
+  realT offset;
+  int k;
+  QHULL_UNUSED(unbounded);
+
+  normal= qh_detvnorm(qh, vertex, vertexA, centers, &offset);
+  qh_fprintf(qh, fp, 9271, "%d %d %d ",
+      2+qh->hull_dim, qh_pointid(qh, vertex->point), qh_pointid(qh, vertexA->point));
+  for (k=0; k< qh->hull_dim-1; k++)
+    qh_fprintf(qh, fp, 9272, qh_REAL_1, normal[k]);
+  qh_fprintf(qh, fp, 9273, qh_REAL_1, offset);
+  qh_fprintf(qh, fp, 9274, "\n");
+} /* printvnorm */
+
+/*-<a                             href="qh-io_r.htm#TOC"
+  >-------------------------------</a><a name="printvridge">-</a>
+
+  qh_printvridge(qh, fp, vertex, vertexA, centers, unbounded )
+    print one ridge of the Voronoi diagram for a pair of input sites
+    unbounded==True if centers includes vertex-at-infinity
+
+  see:
+    qh_printvdiagram()
+
+  notes:
+    the user may use a different function
+    parameter unbounded is UNUSED
+*/
+void qh_printvridge(qhT *qh, FILE *fp, vertexT *vertex, vertexT *vertexA, setT *centers, boolT unbounded) {
+  facetT *facet, **facetp;
+  QHULL_UNUSED(unbounded);
+
+  qh_fprintf(qh, fp, 9275, "%d %d %d", qh_setsize(qh, centers)+2,
+       qh_pointid(qh, vertex->point), qh_pointid(qh, vertexA->point));
+  FOREACHfacet_(centers)
+    qh_fprintf(qh, fp, 9276, " %d", facet->visitid);
+  qh_fprintf(qh, fp, 9277, "\n");
+} /* printvridge */
+
+/*-<a                             href="qh-io_r.htm#TOC"
+  >-------------------------------</a><a name="projectdim3">-</a>
+
+  qh_projectdim3(qh, source, destination )
+    project 2-d 3-d or 4-d point to a 3-d point
+    uses qh.DROPdim and qh.hull_dim
+    source and destination may be the same
+
+  notes:
+    allocate 4 elements to destination just in case
+*/
+void qh_projectdim3(qhT *qh, pointT *source, pointT *destination) {
+  int i,k;
+
+  for (k=0, i=0; k < qh->hull_dim; k++) {
+    if (qh->hull_dim == 4) {
+      if (k != qh->DROPdim)
+        destination[i++]= source[k];
+    }else if (k == qh->DROPdim)
+      destination[i++]= 0;
+    else
+      destination[i++]= source[k];
+  }
+  while (i < 3)
+    destination[i++]= 0.0;
+} /* projectdim3 */
+
+/*-<a                             href="qh-io_r.htm#TOC"
+  >-------------------------------</a><a name="readfeasible">-</a>
+
+  qh_readfeasible(qh, dim, curline )
+    read feasible point from current line and qh.fin
+
+  returns:
+    number of lines read from qh.fin
+    sets qh.feasible_point with malloc'd coordinates
+
+  notes:
+    checks for qh.HALFspace
+    assumes dim > 1
+
+  see:
+    qh_setfeasible
+*/
+int qh_readfeasible(qhT *qh, int dim, const char *curline) {
+  boolT isfirst= True;
+  int linecount= 0, tokcount= 0;
+  const char *s;
+  char *t, firstline[qh_MAXfirst+1];
+  coordT *coords, value;
+
+  if (!qh->HALFspace) {
+    qh_fprintf(qh, qh->ferr, 6070, "qhull input error: feasible point(dim 1 coords) is only valid for halfspace intersection\n");
+    qh_errexit(qh, qh_ERRinput, NULL, NULL);
+  }
+  if (qh->feasible_string)
+    qh_fprintf(qh, qh->ferr, 7057, "qhull input warning: feasible point(dim 1 coords) overrides 'Hn,n,n' feasible point for halfspace intersection\n");
+  if (!(qh->feasible_point= (coordT *)qh_malloc((size_t)dim * sizeof(coordT)))) {
+    qh_fprintf(qh, qh->ferr, 6071, "qhull error: insufficient memory for feasible point\n");
+    qh_errexit(qh, qh_ERRmem, NULL, NULL);
+  }
+  coords= qh->feasible_point;
+  while ((s= (isfirst ?  curline : fgets(firstline, qh_MAXfirst, qh->fin)))) {
+    if (isfirst)
+      isfirst= False;
+    else
+      linecount++;
+    while (*s) {
+      while (isspace(*s))
+        s++;
+      value= qh_strtod(s, &t);
+      if (s == t)
+        break;
+      s= t;
+      *(coords++)= value;
+      if (++tokcount == dim) {
+        while (isspace(*s))
+          s++;
+        qh_strtod(s, &t);
+        if (s != t) {
+          qh_fprintf(qh, qh->ferr, 6072, "qhull input error: coordinates for feasible point do not finish out the line: %s\n",
+               s);
+          qh_errexit(qh, qh_ERRinput, NULL, NULL);
+        }
+        return linecount;
+      }
+    }
+  }
+  qh_fprintf(qh, qh->ferr, 6073, "qhull input error: only %d coordinates.  Could not read %d-d feasible point.\n",
+           tokcount, dim);
+  qh_errexit(qh, qh_ERRinput, NULL, NULL);
+  return 0;
+} /* readfeasible */
+
+/*-<a                             href="qh-io_r.htm#TOC"
+  >-------------------------------</a><a name="readpoints">-</a>
+
+  qh_readpoints(qh, numpoints, dimension, ismalloc )
+    read points from qh.fin into qh.first_point, qh.num_points
+    qh.fin is lines of coordinates, one per vertex, first line number of points
+    if 'rbox D4',
+      gives message
+    if qh.ATinfinity,
+      adds point-at-infinity for Delaunay triangulations
+
+  returns:
+    number of points, array of point coordinates, dimension, ismalloc True
+    if qh.DELAUNAY & !qh.PROJECTinput, projects points to paraboloid
+        and clears qh.PROJECTdelaunay
+    if qh.HALFspace, reads optional feasible point, reads halfspaces,
+        converts to dual.
+
+  for feasible point in "cdd format" in 3-d:
+    3 1
+    coordinates
+    comments
+    begin
+    n 4 real/integer
+    ...
+    end
+
+  notes:
+    dimension will change in qh_initqhull_globals if qh.PROJECTinput
+    uses malloc() since qh_mem not initialized
+    QH11012 FIX: 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 or smaller number) should be at least 2\n",
+            diminput);
+    qh_errexit(qh, qh_ERRinput, NULL, NULL);
+  }
+  if (numinput < 1 || numinput > qh_POINTSmax) {
+    qh_fprintf(qh, qh->ferr, 6411, "qhull input error: expecting between 1 and %d points.  Got %d %d-d points\n",
+      qh_POINTSmax, numinput, diminput);
+    qh_errexit(qh, qh_ERRinput, NULL, NULL);
+    /* same error message in qh_initqhull_globals */
+  }
+
+  if (isdelaunay && qh->HALFspace) {
+    qh_fprintf(qh, qh->ferr, 6037, "qhull option error (qh_readpoints): can not use Delaunay('d') or Voronoi('v') with halfspace intersection('H')\n");
+    qh_errexit(qh, qh_ERRinput, NULL, NULL);
+    /* otherwise corrupted memory allocations, same error message as in qh_initqhull_globals */
+  }else 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 * (int)sizeof(coordT); /* for tracing with qh_printpoint */
+  if (qh->HALFspace) {
+    qh->half_space= coordp= (coordT *)qh_malloc((size_t)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((size_t)(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((size_t)((*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 (qh->rbox_command[0])
+    qh->rbox_command[strlen(qh->rbox_command)-1]= '\0'; /* remove \n, previous qh_errexit's display command as two lines */
+  if (tokcount != maxcount) {
+    newnum= fmin_(numinput, tokcount/diminput);
+    if (qh->ALLOWshort)
+      qh_fprintf(qh, qh->ferr, 7073, "qhull warning: instead of %d points in %d-d, input contains %d points and %d extra coordinates.\n",
+          numinput, diminput, tokcount/diminput, tokcount % diminput);
+    else
+      qh_fprintf(qh, qh->ferr, 6410, "qhull error: instead of %d points in %d-d, input contains %d points and %d extra coordinates.\n",
+          numinput, diminput, tokcount/diminput, tokcount % diminput);
+    if (firsttext)
+      qh_fprintf(qh, qh->ferr, 8051, "    Line %d is the first comment.\n", firsttext);
+    qh_fprintf(qh, qh->ferr, 8033,   "    Line %d is the first point.\n", firstpoint);
+    if (firstshort)
+      qh_fprintf(qh, qh->ferr, 8052, "    Line %d is the first short line.\n", firstshort);
+    if (firstlong)
+      qh_fprintf(qh, qh->ferr, 8053, "    Line %d is the first long line.\n", firstlong);
+    if (qh->ALLOWshort)
+      qh_fprintf(qh, qh->ferr, 8054, "    Continuing with %d points.\n", newnum);
+    else {
+      qh_fprintf(qh, qh->ferr, 8077, "    Override with option 'Qa' (allow-short)\n");
+      qh_errexit(qh, qh_ERRinput, NULL, NULL);
+    }
+    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 (!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_r.h.  If you discover the source of the problem,\n\
+please send mail to qhull_bug@qhull.org.\n\
+\n\
+Type 'qhull' for a short list of options.\n");
+  qh_free(qh->line);
+  qh->line= NULL;
+  if (qh->half_space) {
+    qh_free(qh->half_space);
+    qh->half_space= NULL;
+  }
+  qh->temp_malloc= NULL;
+  trace1((qh, qh->ferr, 1008,"qh_readpoints: read in %d %d-dimensional points\n",
+          numinput, diminput));
+  return(points);
+} /* readpoints */
+
+
+/*-<a                             href="qh-io_r.htm#TOC"
+  >-------------------------------</a><a name="setfeasible">-</a>
+
+  qh_setfeasible(qh, dim )
+    set qh.feasible_point from qh.feasible_string in "n,n,n" or "n n n" format
+
+  notes:
+    "n,n,n" already checked by qh_initflags()
+    see qh_readfeasible()
+    called only once from qh_new_qhull, otherwise leaks memory
+*/
+void qh_setfeasible(qhT *qh, int dim) {
+  int tokcount= 0;
+  char *s;
+  coordT *coords, value;
+
+  if (!(s= qh->feasible_string)) {
+    qh_fprintf(qh, qh->ferr, 6223, "qhull input error: halfspace intersection needs a feasible point.  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((size_t)dim * sizeof(coordT)))) {
+    qh_fprintf(qh, qh->ferr, 6079, "qhull error: insufficient memory for 'Hn,n,n'\n");
+    qh_errexit(qh, qh_ERRmem, NULL, NULL);
+  }
+  coords= qh->feasible_point;
+  while (*s) {
+    value= qh_strtod(s, &s);
+    if (++tokcount > dim) {
+      qh_fprintf(qh, qh->ferr, 7059, "qhull input warning: more coordinates for 'H%s' than dimension %d\n",
+          qh->feasible_string, dim);
+      break;
+    }
+    *(coords++)= value;
+    if (*s)
+      s++;
+  }
+  while (++tokcount <= dim)
+    *(coords++)= 0.0;
+} /* setfeasible */
+
+/*-<a                             href="qh-io_r.htm#TOC"
+  >-------------------------------</a><a name="skipfacet">-</a>
+
+  qh_skipfacet(qh, facet )
+    returns 'True' if this facet is not to be printed
+
+  notes:
+    based on the user provided slice thresholds and 'good' specifications
+*/
+boolT qh_skipfacet(qhT *qh, facetT *facet) {
+  facetT *neighbor, **neighborp;
+
+  if (qh->PRINTneighbors) {
+    if (facet->good)
+      return !qh->PRINTgood;
+    FOREACHneighbor_(facet) {
+      if (neighbor->good)
+        return False;
+    }
+    return True;
+  }else if (qh->PRINTgood)
+    return !facet->good;
+  else if (!facet->normal)
+    return True;
+  return(!qh_inthresholds(qh, facet->normal, NULL));
+} /* skipfacet */
+
+/*-<a                             href="qh-io_r.htm#TOC"
+  >-------------------------------</a><a name="skipfilename">-</a>
+
+  qh_skipfilename(qh, string )
+    returns pointer to character after filename
+
+  notes:
+    skips leading spaces
+    ends with spacing or eol
+    if starts with ' or " ends with the same, skipping \' or \"
+    For qhull, qh_argv_to_command() only uses double quotes
+*/
+char *qh_skipfilename(qhT *qh, char *filename) {
+  char *s= filename;  /* non-const due to return */
+  char c;
+
+  while (*s && isspace(*s))
+    s++;
+  c= *s++;
+  if (c == '\0') {
+    qh_fprintf(qh, qh->ferr, 6204, "qhull input error: filename expected, none found.\n");
+    qh_errexit(qh, qh_ERRinput, NULL, NULL);
+  }
+  if (c == '\'' || c == '"') {
+    while (*s !=c || s[-1] == '\\') {
+      if (!*s) {
+        qh_fprintf(qh, qh->ferr, 6203, "qhull input error: missing quote after filename -- %s\n", filename);
+        qh_errexit(qh, qh_ERRinput, NULL, NULL);
+      }
+      s++;
+    }
+    s++;
+  }
+  else while (*s && !isspace(*s))
+      s++;
+  return s;
+} /* skipfilename */
+

+ 166 - 0
contrib/libs/qhull/libqhull_r/io_r.h

@@ -0,0 +1,166 @@
+/*<html><pre>  -<a                             href="qh-io_r.htm"
+  >-------------------------------</a><a name="TOP">-</a>
+
+   io_r.h
+   declarations of Input/Output functions
+
+   see README, libqhull_r.h and io_r.c
+
+   Copyright (c) 1993-2020 The Geometry Center.
+   $Id: //main/2019/qhull/src/libqhull_r/io_r.h#3 $$Change: 2953 $
+   $DateTime: 2020/05/21 22:05:32 $$Author: bbarber $
+*/
+
+#ifndef qhDEFio
+#define qhDEFio 1
+
+#include "libqhull_r.h"
+
+/*============ constants and flags ==================*/
+
+/*-<a                             href="qh-io_r.htm#TOC"
+  >--------------------------------</a><a name="qh_MAXfirst">-</a>
+
+  qh_MAXfirst
+    maximum length of first two lines of stdin
+*/
+#define qh_MAXfirst  200
+
+/*-<a                             href="qh-io_r.htm#TOC"
+  >--------------------------------</a><a name="qh_MINradius">-</a>
+
+  qh_MINradius
+    min radius for Gp and Gv, fraction of maxcoord
+*/
+#define qh_MINradius 0.02
+
+/*-<a                             href="qh-io_r.htm#TOC"
+  >--------------------------------</a><a name="qh_GEOMepsilon">-</a>
+
+  qh_GEOMepsilon
+    adjust outer planes for 'lines closer' and geomview roundoff.
+    This prevents bleed through.
+*/
+#define qh_GEOMepsilon 2e-3
+
+/*-<a                             href="qh-io_r.htm#TOC"
+  >--------------------------------</a><a name="qh_WHITESPACE">-</a>
+
+  qh_WHITESPACE
+    possible values of white space
+*/
+#define qh_WHITESPACE " \n\t\v\r\f"
+
+
+/*-<a                             href="qh-io_r.htm#TOC"
+  >--------------------------------</a><a name="RIDGE">-</a>
+
+  qh_RIDGE
+    to select which ridges to print in qh_eachvoronoi
+*/
+typedef enum
+{
+    qh_RIDGEall= 0, qh_RIDGEinner, qh_RIDGEouter
+}
+qh_RIDGE;
+
+/*-<a                             href="qh-io_r.htm#TOC"
+  >--------------------------------</a><a name="printvridgeT">-</a>
+
+  printvridgeT
+    prints results of qh_printvdiagram
+
+  see:
+    <a href="io_r.c#printvridge">qh_printvridge</a> 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 int id);
+void    qh_dvertex(qhT *qh, unsigned int id);
+int     qh_compare_facetarea(const void *p1, const void *p2);
+int     qh_compare_facetvisit(const void *p1, const void *p2);
+int     qh_compare_nummerge(const void *p1, const void *p2);
+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_printline3geom(qhT *qh, FILE *fp, pointT *pointA, pointT *pointB, realT color[3]);
+void    qh_printneighborhood(qhT *qh, FILE *fp, qh_PRINT format, facetT *facetA, facetT *facetB, boolT printall);
+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 */

Некоторые файлы не были показаны из-за большого количества измененных файлов