diff --git a/Common/CommandLine.cpp b/Common/CommandLine.cpp
index 0b93ad25ad5a12820d030b6d9385649081b7a994..3730714ccebf80534a9d8da9f2500fbe91e9dbd3 100644
--- a/Common/CommandLine.cpp
+++ b/Common/CommandLine.cpp
@@ -1020,6 +1020,8 @@ void GetOptions(int argc, char *argv[], bool readConfigFiles, bool exitOnError)
             CTX::instance()->mesh.algo2d = ALGO_2D_BAMG;
           else if(!strncmp(argv[i], "del3d", 5) || !strncmp(argv[i], "gmsh3d", 6))
             CTX::instance()->mesh.algo3d = ALGO_3D_DELAUNAY;
+          else if(!strncmp(argv[i], "hxt", 3) )
+            CTX::instance()->mesh.algo3d = ALGO_3D_HXT;
           else if(!strncmp(argv[i], "front3d", 7) || !strncmp(argv[i], "netgen", 6))
             CTX::instance()->mesh.algo3d = ALGO_3D_FRONTAL;
           else if(!strncmp(argv[i], "mmg3d", 5))
diff --git a/Common/GmshDefines.h b/Common/GmshDefines.h
index bc2efb2ec3e943ffd0518bd637048acc9b0d15e6..2f784793b13f340886545d496161d75b475e4eed 100644
--- a/Common/GmshDefines.h
+++ b/Common/GmshDefines.h
@@ -247,6 +247,7 @@
 #define ALGO_3D_FRONTAL_HEX    6
 #define ALGO_3D_MMG3D          7
 #define ALGO_3D_RTREE          9
+#define ALGO_3D_HXT           10
 
 // Meshing methods
 #define MESH_NONE         0
diff --git a/Mesh/CMakeLists.txt b/Mesh/CMakeLists.txt
index e1cbd98d062168a3e2b97ea6d140ed665fac8e46..17e8c936e87029b119d5fe4188c4e145c53ade96 100644
--- a/Mesh/CMakeLists.txt
+++ b/Mesh/CMakeLists.txt
@@ -15,6 +15,7 @@ set(SRC
       meshGFaceBamg.cpp meshGFaceBDS.cpp meshGFaceDelaunayInsertion.cpp 
       meshGFaceLloyd.cpp meshGFaceOptimize.cpp 
     meshGRegion.cpp 
+    meshGRegionHxt.cpp 
     meshDiscreteRegion.cpp 
       meshGRegionDelaunayInsertion.cpp meshGRegionTransfinite.cpp 
       meshGRegionExtruded.cpp  meshGRegionCarveHole.cpp 
diff --git a/Mesh/meshGFace.cpp b/Mesh/meshGFace.cpp
index 7dc4bb9ccb15f0404901206183269189ee417dd1..814abfd48ae97a579dbdd6566aa733d8be4a9ddf 100644
--- a/Mesh/meshGFace.cpp
+++ b/Mesh/meshGFace.cpp
@@ -1964,7 +1964,7 @@ static bool buildConsecutiveListOfVertices(
   return true;
 }
 
-static bool meshGeneratorPeriodic(GFace *gf, bool debug = true)
+static bool meshGeneratorPeriodic(GFace *gf, bool repairSelfIntersecting1dMesh, bool debug = true)
 {
   if(CTX::instance()->debugSurface > 0 &&
      gf->tag() != CTX::instance()->debugSurface) {
@@ -2382,6 +2382,9 @@ static bool meshGeneratorPeriodic(GFace *gf, bool debug = true)
       e->g = &CLASS_E;
   }
 
+
+  //  std::vector<EdgeToRecover> edgesNotRecovered;
+
   for(unsigned int i = 0; i < edgeLoops_BDS.size(); i++) {
     std::vector<BDS_Point *> &edgeLoop_BDS = edgeLoops_BDS[i];
     for(unsigned int j = 0; j < edgeLoop_BDS.size(); j++) {
@@ -2389,6 +2392,9 @@ static bool meshGeneratorPeriodic(GFace *gf, bool debug = true)
         edgeLoop_BDS[j]->iD, edgeLoop_BDS[(j + 1) % edgeLoop_BDS.size()]->iD,
         _fatallyFailed);
       if(!e) {
+	//	edgesNotRecovered.push_back(EdgeToRecover(edgeLoop_BDS[j]->iD,
+	//						  edgeLoop_BDS[(j + 1) % edgeLoop_BDS.size()]->iD));
+	
         Msg::Error("Impossible to recover the edge %d %d", edgeLoop_BDS[j]->iD,
                    edgeLoop_BDS[(j + 1) % edgeLoop_BDS.size()]->iD);
         gf->meshStatistics.status = GFace::FAILED;
@@ -2847,7 +2853,7 @@ void meshGFace::operator()(GFace *gf, bool print)
 
   if(gf->getNativeType() != GEntity::GmshModel &&
      (gf->periodic(0) || gf->periodic(1) || singularEdges)) {
-    if(!meshGeneratorPeriodic(gf, debugSurface >= 0 || debugSurface == -100))
+    if(!meshGeneratorPeriodic(gf, repairSelfIntersecting1dMesh, debugSurface >= 0 || debugSurface == -100))
       Msg::Error("Impossible to mesh periodic face %d", gf->tag());
   }
   else {
diff --git a/Mesh/meshGRegion.cpp b/Mesh/meshGRegion.cpp
index 5a9eb534e93e5cae94beaf20dfabe7cbf39874ca..ac08173db32a7016f8e00b0088d49ada3d2ed1bd 100644
--- a/Mesh/meshGRegion.cpp
+++ b/Mesh/meshGRegion.cpp
@@ -8,6 +8,7 @@
 #include "GmshConfig.h"
 #include "GmshMessage.h"
 #include "meshGRegion.h"
+#include "meshGRegionHxt.h"
 #include "meshGFace.h"
 #include "meshGFaceOptimize.h"
 #include "boundaryLayersData.h"
@@ -306,7 +307,12 @@ void MeshDelaunayVolume(std::vector<GRegion *> &regions)
 
   // now do insertion of points
 
-  if(CTX::instance()->mesh.algo3d == ALGO_3D_MMG3D) {
+  if(CTX::instance()->mesh.algo3d == ALGO_3D_HXT) {
+    if (meshGRegionHxt (gr) != 0){
+      Msg::Error ("HXT 3D mesh failed");
+    }
+  }
+  else if(CTX::instance()->mesh.algo3d == ALGO_3D_MMG3D) {
     refineMeshMMG(gr);
   }
   else if(CTX::instance()->mesh.oldRefinement) {
diff --git a/contrib/hxt/CMakeLists.txt b/contrib/hxt/CMakeLists.txt
index 44868e22b8e49d13e028771a189176263d39ac50..91c518bcf397de49c06b6230d336f09cfe93ab1e 100644
--- a/contrib/hxt/CMakeLists.txt
+++ b/contrib/hxt/CMakeLists.txt
@@ -8,15 +8,32 @@ set(SRC
   hxt_context.c
   hxt_curvature.c
   hxt_edge.c
+  hxt_laplace.c
   hxt_linear_system.c
   hxt_linear_system_lu.c
+  hxt_linear_system_petsc.c
+  hxt_mesh.c
   hxt_message.c
+  hxt_non_linear_solver.c
   hxt_option.c
-  hxt_parametrization.c
-  hxt_mean_values.c
+  hxt_opt.c
+  hxt_sort.c
   hxt_tools.c
-  hxt_mesh.c
+  predicates.c
+  hxt_mesh3d.c
+  hxt_mesh3d_main.c
+  hxt_mesh_size.c
+  hxt_tetOpti.c
+  hxt_tetrahedra.c
+  hxt_tetRepair.c
+  hxt_tetUtils.c
+  hxt_tetFlag.c
+  hxt_tetPostpro.c
+  hxt_tet_aspect_ratio.c
   hxt_vertices.c
+  hxt_parametrization.c
+  hxt_mean_values.c 
+  hxt_boundary_recovery.cxx
 )
 
 file(GLOB_RECURSE HDR RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} *.h)
diff --git a/contrib/hxt/CREDITS.txt b/contrib/hxt/CREDITS.txt
new file mode 100644
index 0000000000000000000000000000000000000000..69efff767077185bbe4694740eb08246985b40e2
--- /dev/null
+++ b/contrib/hxt/CREDITS.txt
@@ -0,0 +1,10 @@
+                 Hxt copyright (C) 2017-2018
+
+		       Universite catholique de Louvain
+
+The TetGen/BR code (tetgenBR.{cxx,h}) is copyright (c) 2016 Hang Si,
+Weierstrass Institute for Applied Analysis and Stochatics. It is relicensed
+under the terms of gmsh/LICENSE.txt for use in Gmsh thanks to a Software License
+Agreement between Weierstrass Institute for Applied Analysis and Stochastics and
+GMESH SPRL.
+ 
diff --git a/contrib/hxt/LICENSE.txt b/contrib/hxt/LICENSE.txt
new file mode 100644
index 0000000000000000000000000000000000000000..bf09ce0b65a149590607aaf92998891dabd7c481
--- /dev/null
+++ b/contrib/hxt/LICENSE.txt
@@ -0,0 +1,340 @@
+		    GNU GENERAL PUBLIC LICENSE
+		       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+			    Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+		    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+			    NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+		     END OF TERMS AND CONDITIONS
+
+	    How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/contrib/hxt/hxt_api.h b/contrib/hxt/hxt_api.h
index ed7f6175f5c137ee7fc4f30677164e36458affb3..3d74b382b3171faec3c99166762311f20ca9ce4c 100644
--- a/contrib/hxt/hxt_api.h
+++ b/contrib/hxt/hxt_api.h
@@ -29,12 +29,17 @@ typedef enum
   HXT_STATUS_OUT_OF_MEMORY         = -4,
   HXT_STATUS_FILE_CANNOT_BE_OPENED = -5,
   HXT_STATUS_POINTER_ERROR         = -6,
+  HXT_STATUS_READ_ERROR            = -7,
+  HXT_STATUS_WRITE_ERROR           = -8,
+  HXT_STATUS_RANGE_ERROR           = -9,
+  HXT_STATUS_FORMAT_ERROR          = -10,
 
 
   // INTERNAL Errors (<= HXT_STATUS_INTERNAL) => HXT_CHECK does not give trace message but returns... should be catched internally !
   HXT_STATUS_INTERNAL              = -1024,
-  HXT_STATUS_SKIP                  = -1025,
-  HXT_STATUS_TRYAGAIN              = -1026
+  HXT_STATUS_CONFLICT              = -1025,
+  HXT_STATUS_SKIP                  = -1026,
+  HXT_STATUS_TRYAGAIN              = -1027
 
 
 }HXTStatus;
diff --git a/contrib/hxt/hxt_bbox.c b/contrib/hxt/hxt_bbox.c
index a833fd784dcde1c1cc9e5dd332198f40c1cd25d4..87b078b403d38917c6a593e4c3a6f9b4e630109d 100644
--- a/contrib/hxt/hxt_bbox.c
+++ b/contrib/hxt/hxt_bbox.c
@@ -1,9 +1,9 @@
 #include "hxt_bbox.h"
 
 // /* create a new bounding box */
-// HXTStatus hxtBboxCreate(hxtBbox** bboxP){
+// HXTStatus hxtBboxCreate(HXTBbox** bboxP){
 //         HXT_CHECK(
-//                 hxtMalloc((void **) bboxP, sizeof(hxtBbox)) );
+//                 hxtMalloc((void **) bboxP, sizeof(HXTBbox)) );
 
 //         unsigned i;
 //         for (i=0; i<3; i++)
@@ -16,7 +16,7 @@
 // }
 
 
-// HXTStatus hxtBboxDelete(hxtBbox** bboxP){
+// HXTStatus hxtBboxDelete(HXTBbox** bboxP){
 //         HXT_CHECK(
 //                 hxtFree((void **) bboxP) );
 
@@ -25,7 +25,7 @@
 
 
 /* update the bounding box with one new vertex */
-HXTStatus hxtBboxAddOne(hxtBbox* bbox, double* coord){
+HXTStatus hxtBboxAddOne(HXTBbox* bbox, double* coord){
         unsigned i;
         for (i=0; i<3; i++)
         {
@@ -41,7 +41,7 @@ HXTStatus hxtBboxAddOne(hxtBbox* bbox, double* coord){
 
 
 /* update the bounding box with an array of n vertices at once (far quicker) */
-HXTStatus hxtBboxAdd(hxtBbox* bbox, double* coord, const uint32_t n){
+HXTStatus hxtBboxAdd(HXTBbox* bbox, double* coord, const uint32_t n){
 
         #pragma omp parallel
         {
@@ -51,19 +51,19 @@ HXTStatus hxtBboxAdd(hxtBbox* bbox, double* coord, const uint32_t n){
                 #pragma omp for nowait
                 for (uint32_t i=0; i<n; i++)
                 {
-                        if(coord[4*i  ]<min[0])
-                                min[0]=coord[4*i  ];
-                        if(coord[4*i+1]<min[1])
-                                min[1]=coord[4*i+1];
-                        if(coord[4*i+2]<min[2])
-                                min[2]=coord[4*i+2];
-
-                        if(coord[4*i  ]>max[0])
-                                max[0]=coord[4*i  ];
-                        if(coord[4*i+1]>max[1])
-                                max[1]=coord[4*i+1];
-                        if(coord[4*i+2]>max[2])
-                                max[2]=coord[4*i+2];
+                        if(coord[(size_t) 4*i  ]<min[0])
+                                min[0]=coord[(size_t) 4*i  ];
+                        if(coord[(size_t) 4*i+1]<min[1])
+                                min[1]=coord[(size_t) 4*i+1];
+                        if(coord[(size_t) 4*i+2]<min[2])
+                                min[2]=coord[(size_t) 4*i+2];
+
+                        if(coord[(size_t) 4*i  ]>max[0])
+                                max[0]=coord[(size_t) 4*i  ];
+                        if(coord[(size_t) 4*i+1]>max[1])
+                                max[1]=coord[(size_t) 4*i+1];
+                        if(coord[(size_t) 4*i+2]>max[2])
+                                max[2]=coord[(size_t) 4*i+2];
                 }
 
                 #pragma omp critical
diff --git a/contrib/hxt/hxt_bbox.h b/contrib/hxt/hxt_bbox.h
index 1159c69de43993cc35bcfa035807cab6a1bbeffb..6db445d5f09eace24f96323e05a2df9a2d4b33a8 100644
--- a/contrib/hxt/hxt_bbox.h
+++ b/contrib/hxt/hxt_bbox.h
@@ -12,15 +12,15 @@ extern "C" {
 typedef struct hxtBboxStruct{
     double hxtDeclareAligned min[3];
     double hxtDeclareAligned max[3];
-} hxtBbox;
+} HXTBbox;
 
 // /* create a new bounding box */
-// HXTStatus hxtBboxCreate(hxtBbox** bboxP);
+// HXTStatus hxtBboxCreate(HXTBbox** bboxP);
 
 // /* delete a bounding box */
-// HXTStatus hxtBboxDelete(hxtBbox** bboxP);
+// HXTStatus hxtBboxDelete(HXTBbox** bboxP);
 
-static inline void hxtBboxInit(hxtBbox* bbox){
+static inline void hxtBboxInit(HXTBbox* bbox){
   bbox->min[0] = DBL_MAX;
   bbox->min[1] = DBL_MAX;
   bbox->min[2] = DBL_MAX;
@@ -30,10 +30,10 @@ static inline void hxtBboxInit(hxtBbox* bbox){
 }
 
 /* update the bounding box with one new vertex */
-HXTStatus hxtBboxAddOne(hxtBbox* bbox, double* coord);
+HXTStatus hxtBboxAddOne(HXTBbox* bbox, double* coord);
 
 /* update the bounding box with an array of n vertices at once (far quicker) */
-HXTStatus hxtBboxAdd(hxtBbox* bbox, double* coord, uint32_t n);
+HXTStatus hxtBboxAdd(HXTBbox* bbox, double* coord, uint32_t n);
 
 #ifdef __cplusplus
 }
diff --git a/contrib/hxt/hxt_boundary_recovery.cxx b/contrib/hxt/hxt_boundary_recovery.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..52c7d0142250f81f49f456b871fdafceb94d7631
--- /dev/null
+++ b/contrib/hxt/hxt_boundary_recovery.cxx
@@ -0,0 +1,643 @@
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+#include <math.h>
+#include <set>
+#include <vector>
+#include <time.h>
+#if !defined(HAVE_NO_STDINT_H)
+#include <stdint.h>
+#elif defined(HAVE_NO_INTPTR_T)
+typedef unsigned long intptr_t;
+#endif
+extern "C" {
+#include "hxt_mesh.h"
+#include "predicates.h"
+#include "hxt_api.h"
+#include "hxt_boundary_recovery.h"
+}
+
+#define REAL double
+
+// dummy tetgenio class
+class tetgenio{
+public:
+  int firstnumber;
+  int numberofpointattributes;
+  int numberoftetrahedronattributes;
+  int numberofsegmentconstraints;
+  REAL *segmentconstraintlist;
+  int numberoffacetconstraints;
+  REAL *facetconstraintlist;
+  int numberofpoints;
+  int *pointlist;
+  int *pointattributelist;
+  int numberofpointmakers;
+  int *pointmarkerlist;
+  int numberofpointmtrs;
+  int *pointmtrlist;
+  int numberofedges;
+  int *edgelist;
+  int *edgemarkerlist;
+  int numberofholes;
+  REAL *holelist;
+  int numberofregions;
+  REAL *regionlist;
+  int mesh_dim;
+  tetgenio()
+  {
+    firstnumber = 0;
+    numberofpointattributes = 0;
+    numberoftetrahedronattributes = 0;
+    numberofsegmentconstraints = 0;
+    segmentconstraintlist = 0;
+    numberoffacetconstraints = 0;
+    facetconstraintlist = 0;
+    numberofpoints = 0;
+    pointlist = 0;
+    pointattributelist = 0;
+    numberofpointmakers = 0;
+    pointmarkerlist = 0;
+    numberofpointmtrs = 0;
+    pointmtrlist = 0;
+    numberofedges = 0;
+    edgelist = 0;
+    edgemarkerlist = 0;
+    numberofholes = 0;
+    holelist = 0;
+    numberofregions = 0;
+    regionlist = 0;
+    mesh_dim = 0;
+  }
+};
+
+static double orient4d(double*, double *, double *, double *, double *,
+           double, double, double, double, double){ return 0.; }
+#if !defined(TETLIBRARY)
+#define TETLIBRARY
+#endif
+#include "tetgenBR.h"
+#include "tetgenBR.cxx"
+
+// int _edges [12][2] = {{3,2},{3,0},{1,0},{1,2},{2,1},{0,2},{0,3},{2,0},{1,3},{2,3},{3,1},{0,1}};
+
+// int computeTetGenVersion (const int iface1, uint32_t *v1,
+//         uint32_t *v2){
+
+//   uint32_t i1 = v1[_edges[iface1][0]];
+//   uint32_t i2 = v1[_edges[iface1][1]];
+//   for (int i=0;i<12;i++){
+//     if (i2 == v2[_edges[i][0]] &&
+//   i1 == v2[_edges[i][1]]) return i;
+//   }
+//   printf("oulalalala\n");
+//   return -1;
+
+// }
+
+static inline int computeTetGenVersion2(uint32_t v1, uint32_t* v2Choices, const int iface2){
+  int i;
+  for (i=0; i<3; i++) {
+      if(v1==v2Choices[i]){
+        break;
+      }
+  }
+#ifdef DEBUG
+  if(i==3)
+    HXT_WARNING("should never happen (file:%s line:%s)\n", __FILE__, __LINE__);
+#endif
+
+  return 4*i + iface2;
+}
+
+
+bool tetgenmesh::reconstructmesh(void *p){
+  HXTMesh *mesh = (HXTMesh*) p;
+  in = new tetgenio();
+  b = new tetgenbehavior();
+  char opts[128];
+  sprintf(opts, "YpeQT%g", 1.e-12);
+  b->parse_commandline(opts);
+
+  initializepools();
+
+  //  printf("we have %u vertices\n", mesh->vertices.num);
+  
+  {
+    point pointloop;
+    REAL x, y, z;    
+    // Read the points.
+    for (uint32_t i = 0; i < mesh->vertices.num; i++) {
+      makepoint(&pointloop, UNUSEDVERTEX);
+      // Read the point coordinates.
+      x = pointloop[0] =mesh->vertices.coord[4*i  ];
+      y = pointloop[1] =mesh->vertices.coord[4*i+1];
+      z = pointloop[2] =mesh->vertices.coord[4*i+2];
+      // Determine the smallest and largest x, y and z coordinates.
+      if (i == 0) {
+        xmin = xmax = x;
+        ymin = ymax = y;
+        zmin = zmax = z;
+      }
+      else {
+        xmin = (x < xmin) ? x : xmin;
+        xmax = (x > xmax) ? x : xmax;
+        ymin = (y < ymin) ? y : ymin;
+        ymax = (y > ymax) ? y : ymax;
+        zmin = (z < zmin) ? z : zmin;
+        zmax = (z > zmax) ? z : zmax;
+      }
+    }
+
+    // 'longest' is the largest possible edge length formed by input vertices.
+    x = xmax - xmin;
+    y = ymax - ymin;
+    z = zmax - zmin;
+    longest = sqrt(x * x + y * y + z * z);
+    if (longest == 0.0) {
+      return true;
+    }
+
+    // Two identical points are distinguished by 'lengthlimit'.
+    if (minedgelength == 0.0) {
+      minedgelength = longest * b->epsilon;
+    }
+  }
+
+  point *idx2verlist;
+  
+  // Create a map from indices to vertices.
+  //  printf("we create a map from indices to vertices\n");
+  makeindex2pointmap(idx2verlist);
+  // 'idx2verlist' has length 'in->numberofpoints + 1'.
+  if (in->firstnumber == 1) {
+    idx2verlist[0] = dummypoint; // Let 0th-entry be dummypoint.
+  }
+  {
+    tetrahedron *ver2tetarray;
+    //point *idx2verlist;
+    triface tetloop, checktet, prevchktet;
+    triface hulltet, face1, face2;
+    tetrahedron tptr;
+    point p[4], q[3];
+    REAL ori; //, attrib, volume;
+    int bondflag;
+    int t1ver;
+    int idx, k;
+    
+    // Allocate an array that maps each vertex to its adjacent tets.
+    //    printf("Allocate an array that maps each vertex to its adjacent tets\n");
+    ver2tetarray = new tetrahedron[mesh->vertices.num + 1];
+    for (unsigned int i = in->firstnumber; i < mesh->vertices.num + in->firstnumber; i++) {
+      setpointtype(idx2verlist[i], VOLVERTEX); // initial type.
+      ver2tetarray[i] = NULL;
+    }
+
+
+    // Create the tetrahedra and connect those that share a common face.
+    //    printf("Connect %d tetrahedra\n", mesh->tetrahedra.num);
+    
+    const int perm[4] = {1,0,2,3};
+    std::vector<triface> ts( mesh->tetrahedra.num );
+    for (uint64_t i = 0; i < mesh->tetrahedra.num; i++) {
+      if (mesh->tetrahedra.node[4*i+3] == HXT_GHOST_VERTEX)
+        continue;
+              
+      maketetrahedron(&tetloop); // tetloop.ver = 11.
+      for (uint64_t j = 0; j < 4; j++) {
+        p[j] = idx2verlist[mesh->tetrahedra.node[j+4*i]];
+      }
+      setvertices(tetloop, p[perm[0]], p[perm[1]], p[perm[2]], p[perm[3]]);
+      ts[i] = tetloop;
+    }      
+
+    for (uint64_t i = 0; i < mesh->tetrahedra.num; i++) {
+      if (mesh->tetrahedra.node[4*i+3] != HXT_GHOST_VERTEX){
+
+        for (int iface1=0; iface1<4; iface1++){
+          uint64_t neigh = mesh->tetrahedra.neigh[4*i + perm[iface1]];
+          // p[1] and p[0] have been exchanged
+          if(neigh!=HXT_NO_ADJACENT) {
+            uint64_t n = neigh >> 2;
+            int iface2 = perm[neigh&3];
+
+            if (mesh->tetrahedra.node[4*n+3] != HXT_GHOST_VERTEX){
+              triface tf1 = ts[i];
+              triface tf2 = ts[n];
+              tf1.ver = iface1;
+
+              // the face of the neighbor tetrahedra that is the same
+              uint32_t face2[3] = {mesh->tetrahedra.node[4*n+perm[(iface2+1)&3]],
+                                   mesh->tetrahedra.node[4*n+perm[((iface2&2)^3)]],
+                                   mesh->tetrahedra.node[4*n+perm[((iface2+3)&2)]]};
+
+              tf2.ver = computeTetGenVersion2 (mesh->tetrahedra.node[4*i+perm[(iface1+1)&3]], face2, iface2);
+              bond(tf1,tf2);
+            }
+          }
+        }
+      }
+    }      
+
+    // printf("Create hull tets, create the point-to-tet map, and clean up the temporary spaces used in each tet\n");
+    // Create hull tets, create the point-to-tet map, and clean up the
+    //   temporary spaces used in each tet.
+    hullsize = tetrahedrons->items;
+    tetrahedrons->traversalinit();
+    tetloop.tet = tetrahedrontraverse();
+    while (tetloop.tet != (tetrahedron *) NULL) {
+      tptr = encode(tetloop);
+      for (tetloop.ver = 0; tetloop.ver < 4; tetloop.ver++) {
+        if (tetloop.tet[tetloop.ver] == NULL) {
+          // Create a hull tet.
+          maketetrahedron(&hulltet);
+          p[0] =  org(tetloop);
+          p[1] = dest(tetloop);
+          p[2] = apex(tetloop);
+          setvertices(hulltet, p[1], p[0], p[2], dummypoint);
+          bond(tetloop, hulltet);
+          // Try connecting this to others that share common hull edges.
+          for (int j = 0; j < 3; j++) {
+            fsym(hulltet, face2);
+            while (1) {
+              if (face2.tet == NULL)
+                break;
+              
+              esymself(face2);
+
+              if (apex(face2) == dummypoint)
+                break;
+              
+              fsymself(face2);
+            }
+            if (face2.tet != NULL) {
+              // Found an adjacent hull tet.
+              assert(face2.tet[face2.ver & 3] == NULL);
+              esym(hulltet, face1);
+              bond(face1, face2);
+            }
+            enextself(hulltet);
+          }
+          //hullsize++;
+        }
+
+        // Create the point-to-tet map.
+        setpoint2tet((point) (tetloop.tet[4 + tetloop.ver]), tptr); 
+  
+        // Clean the temporary used space.
+        tetloop.tet[8 + tetloop.ver] = NULL;
+      }
+      tetloop.tet = tetrahedrontraverse();
+    }
+    
+    hullsize = tetrahedrons->items - hullsize;
+    
+    delete [] ver2tetarray;
+  }
+  {
+    face newsh;
+    face newseg;
+    point p[4];
+    int idx;
+    
+    for (uint64_t i=0;i<mesh->triangles.num;i++){
+      for (uint64_t j = 0; j < 3; j++) {
+        p[j] = idx2verlist[mesh->triangles.node[3*i+j]];
+        if (pointtype(p[j]) == VOLVERTEX) {
+          setpointtype(p[j], FACETVERTEX);
+        }
+      }
+      
+      // Create an initial triangulation.
+      makeshellface(subfaces, &newsh);
+      setshvertices(newsh, p[0], p[1], p[2]);
+      setshellmark(newsh, mesh->triangles.colors[i]);
+      recentsh = newsh;
+
+      for (int j = 0; j < 3; j++) {
+        makeshellface(subsegs, &newseg);
+        setshvertices(newseg, sorg(newsh), sdest(newsh), NULL);
+        // Set the default segment marker '-1'.
+        setshellmark(newseg, -1);
+        ssbond(newsh, newseg);
+        senextself(newsh);
+      }
+    } // i
+
+    unifysegments();
+    
+    
+    face* shperverlist;
+    int* idx2shlist;
+    face searchsh, neighsh;
+    face segloop, checkseg;
+    point checkpt;
+    
+    // Construct a map from points to subfaces.
+    makepoint2submap(subfaces, idx2shlist, shperverlist);
+    
+    // Process the set of PSC edges.
+    // Remeber that all segments have default marker '-1'.
+    //    int COUNTER = 0;
+    //   printf("Add Edges\n");
+    {
+      for (uint64_t i = 0; i< mesh->lines.num;i++) {
+        for (uint64_t j = 0; j < 2; j++) {
+          p[j] = idx2verlist[mesh->lines.node[2*i+j]];
+          setpointtype(p[j], RIDGEVERTEX);
+        }
+
+        if (p[0] == p[1]) {
+          // This is a potential problem in surface mesh.
+          continue; // Skip this edge.
+        }
+        
+        // Find a face contains the edge p[0], p[1].
+        newseg.sh = NULL;
+        searchsh.sh = NULL;
+        idx = pointmark(p[0]) - in->firstnumber;
+        for (int j = idx2shlist[idx]; j < idx2shlist[idx + 1]; j++) {
+          checkpt = sdest(shperverlist[j]);
+
+          if (checkpt == p[1]) {
+            searchsh = shperverlist[j];
+            break; // Found.
+          }
+          else {
+            checkpt = sapex(shperverlist[j]);
+            if (checkpt == p[1]) {
+              senext2(shperverlist[j], searchsh);
+              sesymself(searchsh);
+              break;
+            }
+          }
+        } // j
+
+        if (searchsh.sh != NULL) {
+          // Check if this edge is already a segment of the mesh.
+          sspivot(searchsh, checkseg);
+          if (checkseg.sh != NULL) {
+           // This segment already exist.
+            newseg = checkseg;
+          }
+          else {
+            // Create a new segment at this edge.
+            makeshellface(subsegs, &newseg);
+            setshvertices(newseg, p[0], p[1], NULL);
+            ssbond(searchsh, newseg);
+            spivot(searchsh, neighsh);
+            if (neighsh.sh != NULL) {
+              ssbond(neighsh, newseg);
+            }
+          }
+        }
+        else {
+          // It is a dangling segment (not belong to any facets).
+          // Check if segment [p[0],p[1]] already exists.
+          // TODO: Change the brute-force search. Slow!
+         /*    point *ppt;
+             subsegs->traversalinit();
+             segloop.sh = shellfacetraverse(subsegs);
+             while (segloop.sh != NULL) {
+             ppt = (point *) &(segloop.sh[3]);
+             if (((ppt[0] == p[0]) && (ppt[1] == p[1])) ||
+             ((ppt[0] == p[1]) && (ppt[1] == p[0]))) {
+             // Found!
+             newseg = segloop;
+             break;
+             }
+             segloop.sh = shellfacetraverse(subsegs);
+             }*/
+          if (newseg.sh == NULL) {
+            makeshellface(subsegs, &newseg);
+            setshvertices(newseg, p[0], p[1], NULL);
+          }
+        }
+        setshellmark(newseg, mesh->lines.colors[i]);
+      } // i
+      
+      delete [] shperverlist;
+      delete [] idx2shlist;
+      insegments = subsegs->items;
+    }
+  }
+
+  delete [] idx2verlist;
+  clock_t t = clock();
+  recoverboundary(t);  
+  //  printf("Carve Holes\n");
+  //  carveholes();
+  if (subvertstack->objects > 0l) {
+    HXT_INFO("Suppressing Steiner points...");
+    suppresssteinerpoints();
+  }  
+#if 1
+  HXT_INFO("Recover Delaunay");
+  recoverdelaunay();
+#endif
+  //  printf("Optimize final mesh\n");
+  //  optimizemesh();
+  {
+    //    printf("Write to HXT -->\n");
+    // Write mesh into to HXT.
+    point p[4];
+    std::set<int> /*l_faces, */l_edges;
+    
+    if (points->items > mesh->vertices.num) {
+      mesh->vertices.num = points->items;
+      if(mesh->vertices.num > mesh->vertices.size) {
+        // HXT_CHECK( hxtAlignedFree (&mesh->vertices.coord) );
+        HXT_CHECK( hxtAlignedRealloc(&mesh->vertices.coord,
+                                    4*mesh->vertices.num*sizeof( double )) );
+        mesh->vertices.size = mesh->vertices.num;
+      }
+            
+      face parentseg, parentsh, spinsh;
+      point pointloop;
+      // Create newly added mesh vertices.
+      // The new vertices must be added at the end of the point list.
+      points->traversalinit();
+      pointloop = pointtraverse();
+      int reconstructingTriangularMeshIsRequired = 0;
+      while (pointloop != (point) NULL) {
+        if (issteinerpoint(pointloop)) {
+          // Check if this Steiner point locates on boundary.
+          if (pointtype(pointloop) == FREESEGVERTEX) {
+            HXT_WARNING("Steiner point inserted that splits an existing edge"
+                        "(boundary mesh has changed,"
+                        "JF you should finish that thing and reconstruct edges as well)");
+            reconstructingTriangularMeshIsRequired = 1;
+            sdecode(point2sh(pointloop), parentseg);
+            assert(parentseg.sh != NULL);
+            l_edges.insert(shellmark(parentseg));
+            mesh->vertices.coord[4*pointmark(pointloop)  ] = pointloop[0];
+            mesh->vertices.coord[4*pointmark(pointloop)+1] = pointloop[1];
+            mesh->vertices.coord[4*pointmark(pointloop)+2] = pointloop[2];
+          }
+          else if (pointtype(pointloop) == FREEFACETVERTEX) {
+            //            HXT_INFO("Steiner point inserted that splits an existing facet (boundary mesh has changed, JF you should code that thing)");
+            reconstructingTriangularMeshIsRequired = 1;
+            sdecode(point2sh(pointloop), parentsh);
+            assert(parentsh.sh != NULL);
+            int ftag = shellmark(parentsh);
+            mesh->vertices.coord[4*pointmark(pointloop)  ] = pointloop[0];
+            mesh->vertices.coord[4*pointmark(pointloop)+1] = pointloop[1];
+            mesh->vertices.coord[4*pointmark(pointloop)+2] = pointloop[2];
+          }
+          else if (pointtype(pointloop) == FREEVOLVERTEX) {
+            //            HXT_INFO("Steiner point inserted inside the volume of the mesh (not that much of a problem)");
+            mesh->vertices.coord[4*pointmark(pointloop)  ] = pointloop[0];
+            mesh->vertices.coord[4*pointmark(pointloop)+1] = pointloop[1];
+            mesh->vertices.coord[4*pointmark(pointloop)+2] = pointloop[2];
+          }
+        }
+        else{
+          mesh->vertices.coord[4*pointmark(pointloop)  ] = pointloop[0];
+          mesh->vertices.coord[4*pointmark(pointloop)+1] = pointloop[1];
+          mesh->vertices.coord[4*pointmark(pointloop)+2] = pointloop[2];
+        }
+
+        pointloop = pointtraverse();
+      }
+      if (reconstructingTriangularMeshIsRequired) {
+        // restore 2D mesh ...
+        HXT_CHECK( hxtAlignedFree(&(mesh->triangles.node)));
+        HXT_CHECK( hxtAlignedFree(&(mesh->triangles.colors)));      
+        HXT_INFO("deleting %u triangles",mesh->triangles.num);
+        mesh->triangles.num = 0; // firstindex; // in->firstnumber;
+        {    
+          face subloop;
+          subloop.shver = 0;
+          subfaces->traversalinit();
+          subloop.sh = shellfacetraverse(subfaces);
+          while (subloop.sh != NULL) {
+            mesh->triangles.num ++ ;
+            subloop.sh = shellfacetraverse(subfaces);
+          }
+        }
+        HXT_INFO("creating %u triangles",mesh->triangles.num);
+
+        {
+          face subloop;
+          HXT_CHECK( hxtAlignedMalloc(&mesh->triangles.node,
+                                     (mesh->triangles.num)*3*sizeof(uint32_t)) );
+          if (mesh->triangles.node == NULL)
+            return HXT_ERROR(HXT_STATUS_OUT_OF_MEMORY);
+          HXT_CHECK( hxtAlignedMalloc(&mesh->triangles.colors,
+                                     (mesh->triangles.num)*sizeof(uint16_t)) );
+          if (mesh->triangles.colors == NULL)
+            return HXT_ERROR(HXT_STATUS_OUT_OF_MEMORY);
+          mesh->triangles.size = mesh->triangles.num;
+          subloop.shver = 0;
+          subfaces->traversalinit();
+          subloop.sh = shellfacetraverse(subfaces);
+          int iTriangle = 0;
+          while (subloop.sh != NULL) {
+            p[0] = sorg(subloop);
+            p[1] = sdest(subloop);
+            p[2] = sapex(subloop);
+            mesh->triangles.node [3*iTriangle+0] = pointmark(p[0]);
+            mesh->triangles.node [3*iTriangle+1] = pointmark(p[1]);
+            mesh->triangles.node [3*iTriangle+2] = pointmark(p[2]);
+            mesh->triangles.colors[iTriangle]     = shellmark(subloop);
+            iTriangle++;
+            subloop.sh = shellfacetraverse(subfaces);
+          }
+        }
+      }
+    }
+    
+    int elementnumber = 1; // firstindex; // in->firstnumber;
+    {    
+      // number tets
+      triface tetloop;
+      tetrahedrons->traversalinit();
+      tetloop.tet = tetrahedrontraverse();
+      while (tetloop.tet != (tetrahedron *) NULL) {
+        setelemindex(tetloop.tet, elementnumber);
+        tetloop.tet = tetrahedrontraverse();
+        elementnumber++;
+      }
+    }
+    
+    {
+      // move data to HXT
+      triface tetloop;    
+      tetrahedrons->traversalinit();
+      tetloop.tet = tetrahedrontraverse();
+      mesh->tetrahedra.num  = elementnumber-1;
+      if(mesh->tetrahedra.num > mesh->tetrahedra.size) {
+        HXT_CHECK( hxtAlignedFree(&mesh->tetrahedra.node) );
+        HXT_CHECK( hxtAlignedFree(&mesh->tetrahedra.neigh) );
+        HXT_CHECK( hxtAlignedFree(&mesh->tetrahedra.colors) );
+        HXT_CHECK( hxtAlignedFree(&mesh->tetrahedra.flag) );
+
+        HXT_CHECK( hxtAlignedMalloc(&mesh->tetrahedra.node,
+                                   (mesh->tetrahedra.num)*4*sizeof(uint32_t)) );
+        HXT_CHECK( hxtAlignedMalloc(&mesh->tetrahedra.neigh,
+                                   (mesh->tetrahedra.num)*4*sizeof(uint64_t)) );
+        HXT_CHECK( hxtAlignedMalloc(&mesh->tetrahedra.colors,
+                                   (mesh->tetrahedra.num)*sizeof(uint16_t)) );
+        HXT_CHECK( hxtAlignedMalloc(&mesh->tetrahedra.flag,
+                                   (mesh->tetrahedra.num)*sizeof(uint16_t)) );
+
+        mesh->tetrahedra.size = mesh->tetrahedra.num;
+      }
+      
+      
+      int counter = 0;
+      while (tetloop.tet != (tetrahedron *) NULL) {
+        tetloop.ver = 11;
+        p[0] = org(tetloop);
+        p[1] = dest(tetloop);
+        p[2] = apex(tetloop);
+        p[3] = oppo(tetloop);
+        triface E, N[4];
+        E = tetloop;
+        for (E.ver = 0; E.ver < 4; E.ver++) {
+          fsym(E, N[E.ver]);
+        }
+        int orderHXT[4] = {1,0,2,3};
+        mesh->tetrahedra.colors[counter] = 0;
+        for (int k=0;k<4;k++){
+          mesh->tetrahedra.node[4*counter+k] = pointmark(p[orderHXT[k]]);
+          if (mesh->tetrahedra.node[4*counter+k] >= mesh->vertices.num)
+            HXT_WARNING("ERROR : index %u out of range (%u)\n",mesh->tetrahedra.node[4*counter+k],mesh->vertices.num);
+        }
+        for (int i=0;i<4;i++){
+          int ngh =  elemindex(N[orderHXT[i]].tet);
+          if (ngh) {
+            //      mesh->tetrahedra.neigh[4*counter+i] = 4*(elemindex(N[i].tet)-1)+i;      
+          }   
+          else{
+            //      mesh->tetrahedra.neigh[4*counter+i] = HXT_NO_ADJACENT;
+          }
+        }
+        //  printf("%d --> %d %d %d %d\n", counter,  pointmark(p[0]),pointmark(p[1]),pointmark(p[2]),pointmark(p[3]));
+
+        counter++;
+        tetloop.tet = tetrahedrontraverse();
+      }
+    } // mesh output
+  }
+
+  delete in;
+  delete b;
+  return true;
+}
+
+extern "C" {
+  HXTStatus hxt_boundary_recovery(HXTMesh *mesh)
+  {
+    bool ret = false;
+    try{
+      tetgenmesh *m = new tetgenmesh();
+      ret = m->reconstructmesh((void*)mesh);
+      delete m;
+      return HXT_STATUS_OK ;
+    }
+    catch (...){
+      return HXT_ERROR_MSG(HXT_STATUS_FAILED, "failed to recover constrained lines/triangles") ;
+    }
+  }
+}
diff --git a/contrib/hxt/hxt_boundary_recovery.h b/contrib/hxt/hxt_boundary_recovery.h
new file mode 100644
index 0000000000000000000000000000000000000000..b046b4a7a783f9a0cb5ff4b48291bbf9682895a3
--- /dev/null
+++ b/contrib/hxt/hxt_boundary_recovery.h
@@ -0,0 +1,4 @@
+#ifndef _HXT_BOUNDARY_RECOVERY_
+#define _HXT_BOUNDARY_RECOVERY_
+HXTStatus hxt_boundary_recovery(HXTMesh *mesh);
+#endif
diff --git a/contrib/hxt/hxt_laplace.h b/contrib/hxt/hxt_laplace.h
index 7bed8b91ab9294f7301d931a72ffd589d0b3f109..b6f8a24c69387233c6a440395d867beffc021941 100644
--- a/contrib/hxt/hxt_laplace.h
+++ b/contrib/hxt/hxt_laplace.h
@@ -2,6 +2,7 @@
 #define HEXTREME_LAPLACE_H
 
 #include "hxt_mesh.h"
+
 HXTStatus hxtLaplace(HXTMesh *mesh);
 
 #endif
diff --git a/contrib/hxt/hxt_linear_system_lu.c b/contrib/hxt/hxt_linear_system_lu.c
index 6240723797f1fe9d52fbe8cbcf75893728bef833..ad21fef5def674041642667f06039df3b3d1f105 100644
--- a/contrib/hxt/hxt_linear_system_lu.c
+++ b/contrib/hxt/hxt_linear_system_lu.c
@@ -114,24 +114,34 @@ typedef hxtDeclareAligned double alignedDouble;
 // y[from:to] += a*x[from:to]
 // y and x must be 256-bit aligned
 // memory should be allocated in consequence
-static void rowAxpy(double a, alignedDouble *__restrict__ x, alignedDouble *__restrict__ y, int from, int to)
+static void rowAxpy(double a, double *__restrict__ px, double *__restrict__ py, int from, int to)
 {
+  // Can't use the aligned attribute on function arguments.
+  hxtDeclareAligned double * __restrict__ x = px;
+  hxtDeclareAligned double * __restrict__ y = py;
+
   int i = from;
-  int pfrom = (from+3)&(~3);
+  int pfrom = (from+7)&(~7);
   if (pfrom > to)
     pfrom = to;
   for (; i < pfrom; ++i)
     y[i] += a*x[i];
   for (; i+15 < to; i+=16) {
-    alignedDouble * __restrict__ X = x + i;
-    alignedDouble * __restrict__ Y = y + i;
+    hxtDeclareAligned double * __restrict__ X = x + i;
+    hxtDeclareAligned double * __restrict__ Y = y + i;
     for (int j = 0; j < 16; ++j){
       Y[j] += a * X[j];
     }
   }
+  for (; i+7 < to; i+=8) {
+    hxtDeclareAligned double * __restrict__ X = x + i;
+    hxtDeclareAligned double * __restrict__ Y = y + i;
+    for (int j = 0; j < 8; ++j)
+      Y[j] += a * X[j];
+  }
   for (; i+3 < to; i+=4) {
-    alignedDouble * __restrict__ X = x + i;
-    alignedDouble * __restrict__ Y = y + i;
+    double * __restrict__ X = x + i;
+    double * __restrict__ Y = y + i;
     for (int j = 0; j < 4; ++j)
       Y[j] += a * X[j];
   }
@@ -144,35 +154,40 @@ static int imin(int a, int b) {
   return a < b ? a : b;
 }
 
-static double rowReduction(alignedDouble *__restrict__ x, alignedDouble *__restrict__ y, int from, int to)
+static double rowReduction(double *__restrict__ px, double *__restrict__ py, int from, int to)
 {
-  int i = from;
   double r = 0;
-  int pfrom = (from+3)&(~3);
+  hxtDeclareAligned double * __restrict__ x = px;
+  hxtDeclareAligned double * __restrict__ y = py;
+
+  int i = from;
+  int pfrom = (from+7)&(~7);
   for (; i < imin(pfrom, to); ++i)
     r += x[i] * y[i];
-  double R[4];
-  for (; i+3 < to; i+=4) {
-    alignedDouble * __restrict__ X = x + i;
-    alignedDouble * __restrict__ Y = y + i;
-    for (int j = 0; j < 4; ++j)
+  double R[8];
+  for (; i+7 < to; i+=8) {
+    hxtDeclareAligned double * __restrict__ X = x + i;
+    hxtDeclareAligned double * __restrict__ Y = y + i;
+    for (int j = 0; j < 8; ++j)
       R[j] = X[j]*Y[j];
-    r += R[0]+R[1]+R[2]+R[3];
+    r += R[0]+R[1]+R[2]+R[3]+R[4]+R[5]+R[6]+R[7];
   }
   for (; i < to; ++i)
     r += x[i] * y[i];
   return r;
 }
 
-static void rowZero(alignedDouble *__restrict__ x, int from, int to)
+static void rowZero(double *__restrict__ px, int from, int to)
 {
+  hxtDeclareAligned double * __restrict__ x = px;
+
   int i = from;
-  int pfrom = (from+3)&(~3);
+  int pfrom = (from+7)&(~7);
   for (; i < imin(pfrom, to); ++i)
     x[i] = 0;
-  for (; i+3 < to; i+=4) {
-    alignedDouble * __restrict__ X = x + i;
-    for (int j = 0; j < 4; ++j)
+  for (; i+7 < to; i+=8) {
+    hxtDeclareAligned double * __restrict__ X = x + i;
+    for (int j = 0; j < 8; ++j)
       X[j] = 0;
   }
   for (; i < to; ++i)
@@ -279,7 +294,7 @@ HXTStatus hxtLinearSystemLUCreate(HXTLinearSystemLU **pSystem, int nElements, in
   }
   free(nodeRowStart);
   free(nodeRowEnd);
-  system->M = malloc(sizeof(double)*totalSize); // FIXME Gmsh instead of _mm_malloc
+  system->M = _mm_malloc(sizeof(double)*totalSize, PADDING*8);
   system->rows = malloc(sizeof(double*)*system->n);
   for (int i = 0; i < totalSize; ++i)
     system->M[i] = 0;
@@ -291,7 +306,7 @@ HXTStatus hxtLinearSystemLUCreate(HXTLinearSystemLU **pSystem, int nElements, in
     totalSize += system->rowEnd[i]-system->rowStart[i]+(paddedStart-start);
     system->rows[i] = system->M + paddedStart;
   }
-  system->x = malloc(sizeof(double)*system->n); // FIXME Gmsh instead of _mm_malloc
+  system->x = _mm_malloc(sizeof(double)*system->n, PADDING*8);
   return HXT_STATUS_OK;
 }
 
@@ -384,7 +399,7 @@ HXTStatus hxtLinearSystemLUAddMatrixEntry(HXTLinearSystemLU *system, int node0,
     HXT_ERROR_MSG(HXT_STATUS_FAILED, "node %i or %i not in the domain", node0, node1);
   int row0 = system->nodeMap[node0]*system->nFields + field0;
   int col1 = system->nodeMap[node1]*system->nFields + field1;
-
+  
   system->rows[row0][col1] += v;
   return HXT_STATUS_OK;
 }
@@ -403,7 +418,7 @@ HXTStatus hxtLinearSystemLUSolve(HXTLinearSystemLU *system, double *rhs, double
     LUPDecompose(system);
     system->flaglu=1;
   }
-
+  
   LUPSolve(system, rhs);
   for (int i = 0; i < system->nNodes; ++i){
     int ii = system->nodeMap[i];
diff --git a/contrib/hxt/hxt_linear_system_petsc.c b/contrib/hxt/hxt_linear_system_petsc.c
index d359fc642be4c9e99a2f392ca03c8dad4403b35c..03f2db4cce6c527605a1509752bd4f4c662e260f 100644
--- a/contrib/hxt/hxt_linear_system_petsc.c
+++ b/contrib/hxt/hxt_linear_system_petsc.c
@@ -229,16 +229,30 @@ HXTStatus hxtLinearSystemPETScAddRhsEntry(HXTLinearSystemPETSc *system, double *
 
 HXTStatus hxtLinearSystemPETScSolve(HXTLinearSystemPETSc *system, double *rhs, double *solution){
   Vec b;
+  Vec x;
   HXT_PETSC_CHECK(VecCreateSeqWithArray(PETSC_COMM_SELF, 1, system->nDofs, rhs, &b));
+  HXT_PETSC_CHECK(VecCreateSeqWithArray(PETSC_COMM_SELF, 1, system->nDofs, solution, &x));
+  double normTest=0.0;
+  for (int iv = 0; iv < system->nDofs; ++iv){
+      normTest+=solution[iv]*solution[iv];
+  }
+  /* printf("IN PETSC norm vect sol : %g ; nofs : %i\n",normTest,system->nDofs);     */
   if(system->assemblyNeeded) {
     HXT_PETSC_CHECK(MatAssemblyBegin(system->a, MAT_FINAL_ASSEMBLY));
     HXT_PETSC_CHECK(MatAssemblyEnd(system->a, MAT_FINAL_ASSEMBLY));
     system->assemblyNeeded = 0;
   }
   HXT_PETSC_CHECK(KSPSetOperators(system->ksp, system->a, system->a));
-  HXT_PETSC_CHECK(KSPSolve(system->ksp, b, system->x));
+  /* HXT_PETSC_CHECK(KSPSolve(system->ksp, b, system->x)); */
+  KSPSetInitialGuessNonzero(system->ksp,PETSC_TRUE);
+  PetscBool flag[1];
+  KSPGetInitialGuessNonzero(system->ksp,flag);
+  /* printf("IN PETSC initialguessnonzero : %i\n",flag[0]); */
+  HXT_PETSC_CHECK(KSPSolve(system->ksp, b, x));
   HXT_PETSC_CHECK(VecDestroy(&b));
-  HXT_CHECK(hxtLinearSystemPETScMapFromVec(system, system->x, solution));
+  HXT_CHECK(hxtLinearSystemPETScMapFromVec(system, x, solution));
+  HXT_PETSC_CHECK(VecDestroy(&x));
+  /* HXT_CHECK(hxtLinearSystemPETScMapFromVec(system, system->x, solution)); */
   return HXT_STATUS_OK;
 }
 
diff --git a/contrib/hxt/hxt_mean_values.c b/contrib/hxt/hxt_mean_values.c
index 53cde1182cda47c630e7832be7f6939d637a8ab4..8a662c13c6b45a01925ac26a4cd65b991b0152bc 100644
--- a/contrib/hxt/hxt_mean_values.c
+++ b/contrib/hxt/hxt_mean_values.c
@@ -155,12 +155,12 @@ HXTStatus hxtMeanValuesCompute(HXTMeanValues *meanValues){
     // gather local edge index
     for(int it=0; it<2; it++){
       if(tri[it]==(uint64_t)-1)
-	break;      
+        break;      
       for(int k=0; k<3; k++){
-	if(edges->tri2edg[3*tri[it]+k]==ie){
-	  ik[it] = k;
-	  break;
-	}
+        if(edges->tri2edg[3*tri[it]+k]==ie){
+          ik[it] = k;
+          break;
+        }
       }
     }
 
@@ -170,32 +170,32 @@ HXTStatus hxtMeanValuesCompute(HXTMeanValues *meanValues){
       uint32_t v1 = edges->node[2*ie+(1-ij)];      
 
       if (flag[v0]==1){//boundary nodes/conditons
-	HXT_CHECK(hxtLinearSystemSetMatrixRowIdentity(sys,v0,0));
-	HXT_CHECK(hxtLinearSystemSetRhsEntry(sys,Urhs, v0,0, uv[2*v0+0]));
-	HXT_CHECK(hxtLinearSystemSetRhsEntry(sys,Vrhs, v0,0, uv[2*v0+1]));
+        HXT_CHECK(hxtLinearSystemSetMatrixRowIdentity(sys,v0,0));
+        HXT_CHECK(hxtLinearSystemSetRhsEntry(sys,Urhs, v0,0, uv[2*v0+0]));
+        HXT_CHECK(hxtLinearSystemSetRhsEntry(sys,Vrhs, v0,0, uv[2*v0+1]));
       }      
-      else {// inner node	
-	double e[3] = {mesh->vertices.coord[4*v1+0]-mesh->vertices.coord[4*v0+0],mesh->vertices.coord[4*v1+1]-mesh->vertices.coord[4*v0+1],mesh->vertices.coord[4*v1+2]-mesh->vertices.coord[4*v0+2]};
-	double ne = sqrt(e[0]*e[0]+e[1]*e[1]+e[2]*e[2]);
-	
-	uint32_t vLeft = mesh->triangles.node[3*tri[0] + (ik[0]+2)%3];
-	double a[3] = {mesh->vertices.coord[4*vLeft+0]-mesh->vertices.coord[4*v0+0],mesh->vertices.coord[4*vLeft+1]-mesh->vertices.coord[4*v0+1],mesh->vertices.coord[4*vLeft+2]-mesh->vertices.coord[4*v0+2]};
-	double na = sqrt(a[0]*a[0]+a[1]*a[1]+a[2]*a[2]);      
-	double thetaL = acos((a[0]*e[0]+a[1]*e[1]+a[2]*e[2])/(na*ne));
-
-	double thetaR=0.;
-	if(tri[1]==(uint64_t)-1)
-	  thetaR=0.;
-	else{
-	  uint32_t vRight = mesh->triangles.node[3*tri[1] + (ik[1]+2)%3];	
-	  double b[3] = {mesh->vertices.coord[4*vRight+0]-mesh->vertices.coord[4*v0+0],mesh->vertices.coord[4*vRight+1]-mesh->vertices.coord[4*v0+1],mesh->vertices.coord[4*vRight+2]-mesh->vertices.coord[4*v0+2]};
-	  double nb = sqrt(b[0]*b[0]+b[1]*b[1]+b[2]*b[2]);
-	  thetaR = acos((b[0]*e[0]+b[1]*e[1]+b[2]*e[2])/(nb*ne));
-	}
-	
-	double c = (tan(.5*thetaL)+tan(.5*thetaR))/ne;
-	HXT_CHECK(hxtLinearSystemAddMatrixEntry(sys, v0, 0, v1, 0, -c));
-	HXT_CHECK(hxtLinearSystemAddMatrixEntry(sys, v0, 0, v0, 0, c));         
+      else {// inner node       
+        double e[3] = {mesh->vertices.coord[4*v1+0]-mesh->vertices.coord[4*v0+0],mesh->vertices.coord[4*v1+1]-mesh->vertices.coord[4*v0+1],mesh->vertices.coord[4*v1+2]-mesh->vertices.coord[4*v0+2]};
+        double ne = sqrt(e[0]*e[0]+e[1]*e[1]+e[2]*e[2]);
+        
+        uint32_t vLeft = mesh->triangles.node[3*tri[0] + (ik[0]+2)%3];
+        double a[3] = {mesh->vertices.coord[4*vLeft+0]-mesh->vertices.coord[4*v0+0],mesh->vertices.coord[4*vLeft+1]-mesh->vertices.coord[4*v0+1],mesh->vertices.coord[4*vLeft+2]-mesh->vertices.coord[4*v0+2]};
+        double na = sqrt(a[0]*a[0]+a[1]*a[1]+a[2]*a[2]);      
+        double thetaL = acos((a[0]*e[0]+a[1]*e[1]+a[2]*e[2])/(na*ne));
+
+        double thetaR=0.;
+        if(tri[1]==(uint64_t)-1)
+          thetaR=0.;
+        else{
+          uint32_t vRight = mesh->triangles.node[3*tri[1] + (ik[1]+2)%3];       
+          double b[3] = {mesh->vertices.coord[4*vRight+0]-mesh->vertices.coord[4*v0+0],mesh->vertices.coord[4*vRight+1]-mesh->vertices.coord[4*v0+1],mesh->vertices.coord[4*vRight+2]-mesh->vertices.coord[4*v0+2]};
+          double nb = sqrt(b[0]*b[0]+b[1]*b[1]+b[2]*b[2]);
+          thetaR = acos((b[0]*e[0]+b[1]*e[1]+b[2]*e[2])/(nb*ne));
+        }
+        
+        double c = (tan(.5*thetaL)+tan(.5*thetaR))/ne;
+        HXT_CHECK(hxtLinearSystemAddMatrixEntry(sys, v0, 0, v1, 0, -c));
+        HXT_CHECK(hxtLinearSystemAddMatrixEntry(sys, v0, 0, v0, 0, c));         
       }
     }
   }// end for int ie
@@ -224,7 +224,7 @@ HXTStatus hxtMeanValuesCompute(HXTMeanValues *meanValues){
       HXT_CHECK(hxtLinearSystemAddMatrixEntry(sys, edges->node[2*edges_ll[j]+0], 0, edges->node[2*edges_ll[j]+1], 0, -tan(.5*(M_PI-theta)/2)/le ));
       HXT_CHECK(hxtLinearSystemAddMatrixEntry(sys, edges->node[2*edges_ll[j]+1], 0, edges->node[2*edges_ll[j]+1], 0, tan(.5*(M_PI-theta)/2)/le ));
       HXT_CHECK(hxtLinearSystemAddMatrixEntry(sys, edges->node[2*edges_ll[j]+1], 0, edges->node[2*edges_ll[j]+0], 0, -tan(.5*(M_PI-theta)/2)/le ));
-	
+        
       c[j] = tan(.5*(M_PI-theta)/2)/radius;
       d[j] = tan(.5*(theta))/radius;
     }
@@ -243,7 +243,7 @@ HXTStatus hxtMeanValuesCompute(HXTMeanValues *meanValues){
     }
       
     for(int j=0; j<n; j++){
-	
+        
       uint32_t v = edges->node[2*edges_ll[j]+0];
 
       HXT_CHECK(hxtLinearSystemAddMatrixEntry(sys, v, 0, v, 0, c[j]));
@@ -281,22 +281,60 @@ HXTStatus hxtMeanValueAspectRatio(HXTMeanValues *meanValues, int *aspectRatio)
 {
 
   if(meanValues->aspectRatio<0){    
+    *aspectRatio = 1;
+    HXTMesh *mesh = meanValues->initialEdges->edg2mesh;
+   
 
+    double grad[3][2] = {{-1./2.,-sqrt(3)/6.},{1./2.,-sqrt(3)/6.},{0.,sqrt(3)/3.}};
+    
+    uint64_t nTri = mesh->triangles.num;
+    double *uv = meanValues->uv;
+    for(uint64_t it=0; it<nTri; it++){
+
+      uint32_t *nodes = mesh->triangles.node + 3*it;
+      
+      
+      double jac[2][2] = {{0.,0.},{0.,0.}};
+      for(int i=0; i<3; i++){
+
+                
+        jac[0][0] += uv[2*nodes[i]+0]*grad[i][0];// dx dxi
+        jac[0][1] += uv[2*nodes[i]+0]*grad[i][1];// dx deta
+        jac[1][0] += uv[2*nodes[i]+1]*grad[i][0];// dy dxi
+        jac[1][1] += uv[2*nodes[i]+1]*grad[i][1];// dy deta
+      }
+
+      double det = jac[0][0]*jac[1][1]-jac[1][0]*jac[0][1];
+      double frob = 0.;
+      for(int i =0; i<2; i++)
+        for(int j=0; j<2; j++)
+          frob += jac[i][j]*jac[i][j];
+      
+      double quality = 2*det/frob;
+      if(quality<.1){
+        printf("wrong aspect ratio !!!!!!! D-: \t %f\n",quality);
+        *aspectRatio = 0;
+        break;
+      }
+      
+    }
+    /*
     *aspectRatio = 1;
     
     uint32_t numEdges = meanValues->initialEdges->numEdges;
     double *uv = meanValues->uv;
     uint32_t *nodes = meanValues->initialEdges->node;
     for(uint32_t i=0; i<numEdges; i++){
-      
-      double du = uv[2*nodes[2*i+1]+0] - uv[2*nodes[2*i+0]+0];
-      double dv = uv[2*nodes[2*i+1]+1] - uv[2*nodes[2*i+0]+1];
-
-      if(sqrt(du*du+dv*dv) < 1e-4){
-	*aspectRatio = 0;
-	break;
-      }
-    }      
+    
+    double du = uv[2*nodes[2*i+1]+0] - uv[2*nodes[2*i+0]+0];
+    double dv = uv[2*nodes[2*i+1]+1] - uv[2*nodes[2*i+0]+1];
+    
+    if(sqrt(du*du+dv*dv) < 1e-4){
+        *aspectRatio = 0;
+        break;
+        }
+        }
+    */      
   }
   else
     *aspectRatio = meanValues->aspectRatio;
@@ -329,7 +367,7 @@ HXTStatus hxtMeanValuesGetData(HXTMeanValues *mv, uint64_t **global,uint32_t **g
     global_[ie] = mv->initialEdges->global[ie];
     if(gn!=NULL)
       for(int kk=0; kk<3; kk++)
-	gn_[3*ie+kk] = mv->initialEdges->edg2mesh->triangles.node[3*ie+kk];  
+        gn_[3*ie+kk] = mv->initialEdges->edg2mesh->triangles.node[3*ie+kk];  
     
   }
   
diff --git a/contrib/hxt/hxt_mesh.c b/contrib/hxt/hxt_mesh.c
index 66f1e7bb09d5246d64f6ab67a32d144739a066f2..5c86fbb76af93004c364d7bbdb45b6ce4ce69394 100644
--- a/contrib/hxt/hxt_mesh.c
+++ b/contrib/hxt/hxt_mesh.c
@@ -1,11 +1,15 @@
 #include "hxt_mesh.h"
 #include <float.h>
 
+/* Compatible with HXT_ELT_TYPE enum exposed in header */
 #define POINTID 15
 #define LINEID 1
 #define TRIID 2
-#define TETID 4
 #define QUADID 3
+#define TETID 4
+#define HEXID 5
+#define PRIID 6
+#define PYRID 7
 
 HXTStatus  hxtMeshCreate ( HXTContext* ctx, HXTMesh** mesh) {
   HXT_CHECK( hxtMalloc (mesh, sizeof(HXTMesh)) );
@@ -16,29 +20,63 @@ HXTStatus  hxtMeshCreate ( HXTContext* ctx, HXTMesh** mesh) {
   (*mesh)->vertices.coord = NULL;
   (*mesh)->vertices.num = 0;
   (*mesh)->vertices.size = 0;
-  (*mesh)->typeStat = NULL;
 
   // tetrahedra
   (*mesh)->tetrahedra.node = NULL;
   (*mesh)->tetrahedra.colors = NULL;
+  (*mesh)->tetrahedra.flag = NULL;
   (*mesh)->tetrahedra.neigh = NULL;
+  (*mesh)->tetrahedra.neighType = NULL;
   // (*mesh)->tetrahedra.subdet = NULL;
   (*mesh)->tetrahedra.num = 0;
   (*mesh)->tetrahedra.size = 0;
 
+  // hexahedra
+  (*mesh)->hexahedra.node = NULL;
+  (*mesh)->hexahedra.colors = NULL;
+  (*mesh)->hexahedra.flag = NULL;
+  (*mesh)->hexahedra.neigh = NULL;
+  (*mesh)->hexahedra.neighType = NULL;
+  (*mesh)->hexahedra.num = 0;
+  (*mesh)->hexahedra.size = 0;
+
+  // prisms
+  (*mesh)->prisms.node = NULL;
+  (*mesh)->prisms.colors = NULL;
+  (*mesh)->prisms.flag = NULL;
+  (*mesh)->prisms.neigh = NULL;
+  (*mesh)->prisms.neighType = NULL;
+  (*mesh)->prisms.num = 0;
+  (*mesh)->prisms.size = 0;
+
+  // pyramids
+  (*mesh)->pyramids.node = NULL;
+  (*mesh)->pyramids.colors = NULL;
+  (*mesh)->pyramids.flag = NULL;
+  (*mesh)->pyramids.neigh = NULL;
+  (*mesh)->pyramids.neighType = NULL;
+  (*mesh)->pyramids.num = 0;
+  (*mesh)->pyramids.size = 0;
+
   // triangles
   (*mesh)->triangles.node = NULL;
+  (*mesh)->triangles.neigh = NULL;
   (*mesh)->triangles.colors = NULL;
   (*mesh)->triangles.num = 0;
   (*mesh)->triangles.size = 0;
 
+  // quads
+  (*mesh)->quads.node = NULL;
+  (*mesh)->quads.colors = NULL;
+  (*mesh)->quads.num = 0;
+  (*mesh)->quads.size = 0;
+
   // lines
   (*mesh)->lines.node = NULL;
   (*mesh)->lines.colors = NULL;
   (*mesh)->lines.num = 0;
   (*mesh)->lines.size = 0;
 
-  // (*mesh)->numHexahedra = 0; 
   return HXT_STATUS_OK;
 }
 
@@ -50,19 +88,45 @@ HXTStatus hxtMeshDelete ( HXTMesh** mesh) {
 
   // tetrahedra
   HXT_CHECK( hxtAlignedFree(&(*mesh)->tetrahedra.colors) );
+  HXT_CHECK( hxtAlignedFree(&(*mesh)->tetrahedra.flag) );
   HXT_CHECK( hxtAlignedFree(&(*mesh)->tetrahedra.node) );
   HXT_CHECK( hxtAlignedFree(&(*mesh)->tetrahedra.neigh) );
+  HXT_CHECK( hxtAlignedFree(&(*mesh)->tetrahedra.neighType) );
   // HXT_CHECK( hxtAlignedFree(&(*mesh)->tetrahedra.subdet) );
+  
+  // hexahedra
+  HXT_CHECK( hxtAlignedFree(&(*mesh)->hexahedra.colors) );
+  HXT_CHECK( hxtAlignedFree(&(*mesh)->hexahedra.flag) );
+  HXT_CHECK( hxtAlignedFree(&(*mesh)->hexahedra.node) );
+  HXT_CHECK( hxtAlignedFree(&(*mesh)->hexahedra.neigh) );
+  HXT_CHECK( hxtAlignedFree(&(*mesh)->hexahedra.neighType) );
+
+  // prisms
+  HXT_CHECK( hxtAlignedFree(&(*mesh)->prisms.colors) );
+  HXT_CHECK( hxtAlignedFree(&(*mesh)->prisms.flag) );
+  HXT_CHECK( hxtAlignedFree(&(*mesh)->prisms.node) );
+  HXT_CHECK( hxtAlignedFree(&(*mesh)->prisms.neigh) );
+  HXT_CHECK( hxtAlignedFree(&(*mesh)->prisms.neighType) );
+
+  // pyramids
+  HXT_CHECK( hxtAlignedFree(&(*mesh)->pyramids.colors) );
+  HXT_CHECK( hxtAlignedFree(&(*mesh)->pyramids.flag) );
+  HXT_CHECK( hxtAlignedFree(&(*mesh)->pyramids.node) );
+  HXT_CHECK( hxtAlignedFree(&(*mesh)->pyramids.neigh) );
+  HXT_CHECK( hxtAlignedFree(&(*mesh)->pyramids.neighType) );
 
   // triangles
   HXT_CHECK( hxtAlignedFree(&(*mesh)->triangles.node) );
   HXT_CHECK( hxtAlignedFree(&(*mesh)->triangles.colors) );
 
+  // quads
+  HXT_CHECK( hxtAlignedFree(&(*mesh)->quads.node) );
+  HXT_CHECK( hxtAlignedFree(&(*mesh)->quads.colors) );
+
   // lines
   HXT_CHECK( hxtAlignedFree(&(*mesh)->lines.node) );
   HXT_CHECK( hxtAlignedFree(&(*mesh)->lines.colors) );
 
-  HXT_CHECK( hxtFree(&(*mesh)->typeStat) );
 
   HXT_CHECK( hxtFree(mesh) );
   return HXT_STATUS_OK;
@@ -75,18 +139,20 @@ HXTStatus ReadNodesFromGmsh(FILE *fp,  HXTMesh* m){
   char buf[BUFSIZ]={""};
   // scan for Nodes
   m->vertices.num = 0;
-  while( fgets(buf, BUFSIZ, fp )){
+  while( fgets(buf, BUFSIZ, fp )!=NULL){
     
     if(strstr(buf, "$Nodes")){
-      fgets(buf, BUFSIZ, fp );
+      if(fgets(buf, BUFSIZ, fp )==NULL)
+        return HXT_ERROR_MSG(HXT_STATUS_READ_ERROR, "Failed to read line");
 
       m->vertices.num = atoi(buf);
-      HXT_CHECK( hxtAlignedMalloc(&m->vertices.coord, 4*m->vertices.num*sizeof( double )) );
+      HXT_CHECK( hxtAlignedMalloc(&m->vertices.coord, sizeof( double )*4*m->vertices.num) );
       if (m->vertices.coord == NULL)return HXT_ERROR(HXT_STATUS_OUT_OF_MEMORY);
       m->vertices.size = m->vertices.num;
       for(n=0;n<m->vertices.num;++n){
-        fgets(buf, BUFSIZ, fp );
-        sscanf(buf, "%*d %lf %lf %lf", &m->vertices.coord[4*n+0], &m->vertices.coord[4*n+1], &m->vertices.coord[4*n+2]);
+        if(fgets(buf, BUFSIZ, fp )==NULL)
+          return HXT_ERROR_MSG(HXT_STATUS_READ_ERROR, "Failed to read line");
+        sscanf(buf, "%*d %lf %lf %lf", &m->vertices.coord[(size_t) 4*n+0], &m->vertices.coord[(size_t) 4*n+1], &m->vertices.coord[(size_t) 4*n+2]);
       }
       break;
     }
@@ -107,23 +173,41 @@ HXTStatus ReadElementsFromGmsh(FILE *fp, HXTMesh* m){
 
   m->lines.num = 0;
   m->triangles.num = 0;
+  m->quads.num = 0;
   m->tetrahedra.num = 0;
+  m->hexahedra.num = 0;
+  m->prisms.num = 0;
+  m->pyramids.num = 0;
 
   while(fgets(buf, BUFSIZ, fp )){    
     if(strstr(buf, "$Elements")){
-      fgets(buf, BUFSIZ, fp );      
+      if(fgets(buf, BUFSIZ, fp )==NULL)
+        return HXT_ERROR_MSG(HXT_STATUS_READ_ERROR, "Failed to read line");    
       int tmpK = atoi(buf);      
       for(k=0;k<tmpK;++k){
         int etype = 0;
-        fgets(buf, BUFSIZ, fp );
+        if(fgets(buf, BUFSIZ, fp )==NULL)
+          return HXT_ERROR_MSG(HXT_STATUS_READ_ERROR, "Failed to read line");
         sscanf(buf, "%*d %d", &etype);
         if(etype==TETID){ // tets
           ++(m->tetrahedra.num);
         }
+        else if(etype==HEXID){ // hexahedra
+          ++(m->hexahedra.num);
+        }
+        else if(etype==PRIID){ // prisms
+          ++(m->prisms.num);
+        }
+        else if(etype==PYRID){ // pyramids
+          ++(m->pyramids.num);
+        }
         else if(etype==TRIID){ // triangles
           ++(m->triangles.num);
         }
-        else if(etype==LINEID){ // triangles
+        else if(etype==QUADID){ // quads
+          ++(m->quads.num);
+        }
+        else if(etype==LINEID){ // lines
           ++(m->lines.num);
         }
         else if(etype==POINTID){ // points
@@ -145,30 +229,68 @@ HXTStatus ReadElementsFromGmsh(FILE *fp, HXTMesh* m){
     if (m->tetrahedra.colors == NULL)return HXT_ERROR(HXT_STATUS_OUT_OF_MEMORY);
     m->tetrahedra.size = m->tetrahedra.num;
   }
+  if (m->hexahedra.num){
+    HXT_CHECK( hxtAlignedMalloc(&m->hexahedra.node, (m->hexahedra.num)*8*sizeof(uint32_t)) );
+    if (m->hexahedra.node == NULL)return HXT_ERROR(HXT_STATUS_OUT_OF_MEMORY);
+    HXT_CHECK( hxtAlignedMalloc(&m->hexahedra.colors, (m->hexahedra.num)*sizeof(uint16_t)) );
+    if (m->hexahedra.colors == NULL)return HXT_ERROR(HXT_STATUS_OUT_OF_MEMORY);
+    m->hexahedra.size = m->hexahedra.num;
+  }
+  if (m->prisms.num){
+    HXT_CHECK( hxtAlignedMalloc(&m->prisms.node, (m->prisms.num)*6*sizeof(uint32_t)) );
+    if (m->prisms.node == NULL)return HXT_ERROR(HXT_STATUS_OUT_OF_MEMORY);
+    HXT_CHECK( hxtAlignedMalloc(&m->prisms.colors, (m->prisms.num)*sizeof(uint16_t)) );
+    if (m->prisms.colors == NULL)return HXT_ERROR(HXT_STATUS_OUT_OF_MEMORY);
+    m->prisms.size = m->prisms.num;
+  }
+  if (m->pyramids.num){
+    HXT_CHECK( hxtAlignedMalloc(&m->pyramids.node, (m->pyramids.num)*5*sizeof(uint32_t)) );
+    if (m->pyramids.node == NULL)return HXT_ERROR(HXT_STATUS_OUT_OF_MEMORY);
+    HXT_CHECK( hxtAlignedMalloc(&m->pyramids.colors, (m->pyramids.num)*sizeof(uint16_t)) );
+    if (m->pyramids.colors == NULL)return HXT_ERROR(HXT_STATUS_OUT_OF_MEMORY);
+    m->pyramids.size = m->pyramids.num;
+  }
   if (m->triangles.num){
     HXT_CHECK( hxtAlignedMalloc(&m->triangles.node, (m->triangles.num)*3*sizeof(uint32_t)) );
     if (m->triangles.node == NULL)return HXT_ERROR(HXT_STATUS_OUT_OF_MEMORY);
     HXT_CHECK( hxtAlignedMalloc(&m->triangles.colors, (m->triangles.num)*sizeof(uint16_t)) );
     if (m->triangles.colors == NULL)return HXT_ERROR(HXT_STATUS_OUT_OF_MEMORY);
+    m->triangles.size = m->triangles.num;
+  }
+  if (m->quads.num){
+    HXT_CHECK( hxtAlignedMalloc(&m->quads.node, (m->quads.num)*4*sizeof(uint32_t)) );
+    if (m->quads.node == NULL)return HXT_ERROR(HXT_STATUS_OUT_OF_MEMORY);
+    HXT_CHECK( hxtAlignedMalloc(&m->quads.colors, (m->quads.num)*sizeof(uint16_t)) );
+    if (m->quads.colors == NULL)return HXT_ERROR(HXT_STATUS_OUT_OF_MEMORY);
+    m->quads.size = m->quads.num;
   }
   if (m->lines.num){
     HXT_CHECK( hxtAlignedMalloc(&m->lines.node, (m->lines.num)*2*sizeof(uint32_t)) );
     if (m->lines.node == NULL)return HXT_ERROR(HXT_STATUS_OUT_OF_MEMORY);
     HXT_CHECK( hxtAlignedMalloc(&m->lines.colors, (m->lines.num)*sizeof(uint16_t)) );
     if (m->lines.colors == NULL)return HXT_ERROR(HXT_STATUS_OUT_OF_MEMORY);
+    m->lines.size = m->lines.num;
   }
         
   while( fgets(buf, BUFSIZ, fp )){
 
     if(strstr(buf, "$Elements")){
-      fgets(buf, BUFSIZ, fp );
+      if(fgets(buf, BUFSIZ, fp )==NULL)
+        return HXT_ERROR_MSG(HXT_STATUS_READ_ERROR, "Failed to read line");
       
       int tmpK = atoi(buf);
-      m->tetrahedra.num=m->triangles.num=m->lines.num=0;
+      m->tetrahedra.num=0;
+      m->hexahedra.num=0;
+      m->prisms.num=0;
+      m->pyramids.num=0;
+      m->triangles.num=0;
+      m->quads.num=0;
+      m->lines.num=0;
       
       for(k=0;k<tmpK;++k){
         int etype = 0, ntags;
-        fgets(buf, BUFSIZ, fp );
+        if(fgets(buf, BUFSIZ, fp )==NULL)
+          return HXT_ERROR_MSG(HXT_STATUS_READ_ERROR, "Failed to read line");
         sscanf(buf, "%*d %d %d", &etype, &ntags);
         if(etype==TETID){ // tets
           if(ntags==2){ // 
@@ -183,6 +305,52 @@ HXTStatus ReadElementsFromGmsh(FILE *fp, HXTMesh* m){
           }
           ++m->tetrahedra.num;
         }
+        else if(etype==HEXID){ // hexahedra
+          if(ntags==2){ // 
+            int a, b, c, d, e, f, g, h, color;
+            sscanf(buf, "%*d %*d %*d %*d %d  %d %d %d %d %d %d %d %d", 
+                   &color,&a, &b, &c, &d, &e, &f, &g, &h);
+            m->hexahedra.node[8*m->hexahedra.num+0] = a-1;
+            m->hexahedra.node[8*m->hexahedra.num+1] = b-1;
+            m->hexahedra.node[8*m->hexahedra.num+2] = c-1;
+            m->hexahedra.node[8*m->hexahedra.num+3] = d-1;
+            m->hexahedra.node[8*m->hexahedra.num+4] = e-1;
+            m->hexahedra.node[8*m->hexahedra.num+5] = f-1;
+            m->hexahedra.node[8*m->hexahedra.num+6] = g-1;
+            m->hexahedra.node[8*m->hexahedra.num+7] = h-1;
+            m->hexahedra.colors[m->hexahedra.num] = color;
+          }
+          ++m->hexahedra.num;
+        }
+        else if(etype==PRIID){ // prisms
+          if(ntags==2){ // 
+            int a, b, c, d, e, f, color;
+            sscanf(buf, "%*d %*d %*d %*d %d  %d %d %d %d %d %d", 
+                   &color,&a, &b, &c, &d, &e, &f);
+            m->prisms.node[6*m->prisms.num+0] = a-1;
+            m->prisms.node[6*m->prisms.num+1] = b-1;
+            m->prisms.node[6*m->prisms.num+2] = c-1;
+            m->prisms.node[6*m->prisms.num+3] = d-1;
+            m->prisms.node[6*m->prisms.num+4] = e-1;
+            m->prisms.node[6*m->prisms.num+5] = f-1;
+            m->prisms.colors[m->prisms.num] = color;
+          }
+          ++m->prisms.num;
+        }
+        else if(etype==PYRID){ // pyramids
+          if(ntags==2){ // 
+            int a, b, c, d, e, color;
+            sscanf(buf, "%*d %*d %*d %*d %d  %d %d %d %d %d", 
+                   &color,&a, &b, &c, &d, &e);
+            m->pyramids.node[5*m->pyramids.num+0] = a-1;
+            m->pyramids.node[5*m->pyramids.num+1] = b-1;
+            m->pyramids.node[5*m->pyramids.num+2] = c-1;
+            m->pyramids.node[5*m->pyramids.num+3] = d-1;
+            m->pyramids.node[5*m->pyramids.num+4] = e-1;
+            m->pyramids.colors[m->pyramids.num] = color;
+          }
+          ++m->pyramids.num;
+        }
         else if(etype==TRIID){ // triangles
           if(ntags==2){ // 
             int a, b, c, color;
@@ -195,6 +363,19 @@ HXTStatus ReadElementsFromGmsh(FILE *fp, HXTMesh* m){
           }
           ++m->triangles.num;
         }
+        else if(etype==QUADID){ // quads
+          if(ntags==2){ // 
+            int a, b, c, d, color;
+            sscanf(buf, "%*d %*d %*d %*d %d  %d %d %d %d", 
+                   &color,&a, &b, &c, &d);
+            m->quads.node[4*m->quads.num+0] = a-1;
+            m->quads.node[4*m->quads.num+1] = b-1;
+            m->quads.node[4*m->quads.num+2] = c-1;
+            m->quads.node[4*m->quads.num+3] = d-1;
+            m->quads.colors[m->quads.num] = color;
+          }
+          ++m->quads.num;
+        }
         else if(etype==POINTID){ // lines
           if(ntags==2){ // 
             int a;
@@ -219,24 +400,14 @@ HXTStatus ReadElementsFromGmsh(FILE *fp, HXTMesh* m){
     }
   }
 
-  // LINEID 1        typeStat[1] = lines.num
-  // TRIID  2   typeStat[2] = triangles.num
-  // TETID  4   typeStat[4] = tetrahedra.num
-  // QUADID 3   typeStat[3] = quadangles.num
-  HXT_CHECK( hxtMalloc (&m->typeStat, 5*sizeof(uint64_t)) );
-  if (m->typeStat == NULL)return HXT_ERROR(HXT_STATUS_OUT_OF_MEMORY);
-  m->typeStat[0] = 0;
-  m->typeStat[1] = m->lines.num;
-  m->typeStat[2] = m->triangles.num;
-  m->typeStat[4] = m->tetrahedra.num;
-  m->typeStat[3] = 0;
-
   return HXT_STATUS_OK;
 }
 
 HXTStatus  hxtMeshReadGmsh  ( HXTMesh* m , const char *filename) {
   FILE *f = fopen (filename, "r");
-  if (!f) return HXT_ERROR_MSG(HXT_STATUS_FILE_CANNOT_BE_OPENED, "mesh file '%s' cannot be opened",(filename==NULL)?"NULL":filename);
+  if (f==NULL)
+    return HXT_ERROR_MSG(HXT_STATUS_FILE_CANNOT_BE_OPENED,
+      "Cannot open mesh file \"%s\"",(filename==NULL)?"(null)":filename);
 
   HXT_CHECK(
     ReadNodesFromGmsh(f,m));
@@ -254,7 +425,8 @@ HXTStatus  hxtMeshReadGmsh  ( HXTMesh* m , const char *filename) {
 HXTStatus  hxtMeshWriteGmsh  ( HXTMesh* mesh , const char *filename) {
   FILE* file = fopen(filename,"w");
   if(file==NULL)
-    HXT_ERROR_MSG(HXT_STATUS_FAILED, "Cannot open file %s",filename);
+    return HXT_ERROR_MSG(HXT_STATUS_FILE_CANNOT_BE_OPENED,
+      "Cannot open mesh file \"%s\"",(filename==NULL)?"(null)":filename);
 
   /* format for gmsh */
   fprintf(file,"$MeshFormat\n"
@@ -266,7 +438,7 @@ HXTStatus  hxtMeshWriteGmsh  ( HXTMesh* mesh , const char *filename) {
   { /* print the nodes */
     uint32_t i;
     for (i=0; i<mesh->vertices.num; i++)
-      fprintf(file,"%u %.10E %.10E %.10E\n",i+1, mesh->vertices.coord[4*i+0], mesh->vertices.coord[4*i+1], mesh->vertices.coord[4*i+2]);
+      fprintf(file,"%u %.10E %.10E %.10E\n",i+1, mesh->vertices.coord[(size_t) 4*i+0], mesh->vertices.coord[(size_t) 4*i+1], mesh->vertices.coord[(size_t) 4*i+2]);
   }
 
   // count non-ghost vertex:
@@ -285,36 +457,97 @@ HXTStatus  hxtMeshWriteGmsh  ( HXTMesh* mesh , const char *filename) {
   
   fprintf(file,"$EndNodes\n"
           "$Elements\n"
-          "%lu\n",index+mesh->triangles.num+mesh->lines.num);
+          "%lu\n",
+          index
+          + mesh->lines.num
+          + mesh->triangles.num
+          + mesh->quads.num
+          + mesh->hexahedra.num
+          + mesh->prisms.num
+          + mesh->pyramids.num
+          );
 
   { /* print the elements */
     
     index = 0;
     for (i=0; i<mesh->lines.num; i++){
       uint16_t myColor = mesh->lines.colors ? mesh->lines.colors[i] : 0;
-      fprintf(file,"%llu %u 2 0 %u %u %u \n", ++index,LINEID,
+      fprintf(file,"%lu %u 2 0 %u %u %u\n", ++index,LINEID,
               myColor,
               mesh->lines.node[i*2]+1,
               mesh->lines.node[i*2 + 1]+1);
     }
     for (i=0; i<mesh->triangles.num; i++){
       uint16_t myColor = mesh->triangles.colors ? mesh->triangles.colors[i] : 0;
-      fprintf(file,"%llu %u 2 0 %u %u %u %u \n", ++index,TRIID,
+      fprintf(file,"%lu %u 2 0 %u %u %u %u\n", ++index,TRIID,
               myColor,
               mesh->triangles.node[i*3]+1,
               mesh->triangles.node[i*3 + 1]+1,
               mesh->triangles.node[i*3 + 2]+1);
     }
+    for (i=0; i<mesh->quads.num; i++){
+      uint16_t myColor = mesh->quads.colors ? mesh->quads.colors[i] : 0;
+      fprintf(file,"%lu %u 2 0 %u %u %u %u %u\n", ++index,QUADID,
+              myColor,
+              mesh->quads.node[i*4]+1,
+              mesh->quads.node[i*4 + 1]+1,
+              mesh->quads.node[i*4 + 2]+1,
+              mesh->quads.node[i*4 + 3]+1
+              );
+    }
     for (i=0; i<mesh->tetrahedra.num; i++){
       if(mesh->tetrahedra.node[i*4 + 3]!=UINT32_MAX){
         uint16_t myColor = mesh->tetrahedra.colors ? mesh->tetrahedra.colors[i] : 0;
-	if (myColor != UINT16_MAX)
-          fprintf(file,"%llu %u 2 0 %u %u %u %u %u\n", ++index,TETID,
-                  myColor,
-                  mesh->tetrahedra.node[i*4]+1,
-                  mesh->tetrahedra.node[i*4 + 1]+1,
-                  mesh->tetrahedra.node[i*4 + 2]+1,
-                  mesh->tetrahedra.node[i*4 + 3]+1);
+        if (myColor != UINT16_MAX)
+          fprintf(file,"%lu %u 2 0 %u %u %u %u %u\n", ++index,TETID,
+              myColor,
+              mesh->tetrahedra.node[i*4]+1,
+              mesh->tetrahedra.node[i*4 + 1]+1,
+              mesh->tetrahedra.node[i*4 + 2]+1,
+              mesh->tetrahedra.node[i*4 + 3]+1);
+      }
+    }
+    for (i=0; i<mesh->hexahedra.num; i++){
+      if(mesh->hexahedra.node[i*8 + 7]!=UINT32_MAX){
+        uint16_t myColor = mesh->hexahedra.colors ? mesh->hexahedra.colors[i] : 0;
+        if (myColor != UINT16_MAX)
+          fprintf(file,"%lu %u 2 0 %u %u %u %u %u %u %u %u %u\n", ++index,HEXID,
+              myColor,
+              mesh->hexahedra.node[i*8]+1,
+              mesh->hexahedra.node[i*8 + 1]+1,
+              mesh->hexahedra.node[i*8 + 2]+1,
+              mesh->hexahedra.node[i*8 + 3]+1,
+              mesh->hexahedra.node[i*8 + 4]+1,
+              mesh->hexahedra.node[i*8 + 5]+1,
+              mesh->hexahedra.node[i*8 + 6]+1,
+              mesh->hexahedra.node[i*8 + 7]+1);
+      }
+    }
+    for (i=0; i<mesh->prisms.num; i++){
+      if(mesh->prisms.node[i*6 + 5]!=UINT32_MAX){
+        uint16_t myColor = mesh->prisms.colors ? mesh->prisms.colors[i] : 0;
+        if (myColor != UINT16_MAX)
+          fprintf(file,"%lu %u 2 0 %u %u %u %u %u %u %u\n", ++index,PRIID,
+              myColor,
+              mesh->prisms.node[i*6]+1,
+              mesh->prisms.node[i*6 + 1]+1,
+              mesh->prisms.node[i*6 + 2]+1,
+              mesh->prisms.node[i*6 + 3]+1,
+              mesh->prisms.node[i*6 + 4]+1,
+              mesh->prisms.node[i*6 + 5]+1);
+      }
+    }
+    for (i=0; i<mesh->pyramids.num; i++){
+      if(mesh->pyramids.node[i*5 + 4]!=UINT32_MAX){
+        uint16_t myColor = mesh->pyramids.colors ? mesh->pyramids.colors[i] : 0;
+        if (myColor != UINT16_MAX)
+          fprintf(file,"%lu %u 2 0 %u %u %u %u %u %u\n", ++index,PYRID,
+              myColor,
+              mesh->pyramids.node[i*5]+1,
+              mesh->pyramids.node[i*5 + 1]+1,
+              mesh->pyramids.node[i*5 + 2]+1,
+              mesh->pyramids.node[i*5 + 3]+1,
+              mesh->pyramids.node[i*5 + 4]+1);
       }
     }
   }
diff --git a/contrib/hxt/hxt_mesh.h b/contrib/hxt/hxt_mesh.h
index 6dfbc674f35525a828186127ea82a1b104670053..1ae907f3c9173841adc5982d201a7f3948c59c43 100644
--- a/contrib/hxt/hxt_mesh.h
+++ b/contrib/hxt/hxt_mesh.h
@@ -4,13 +4,22 @@
 #include "hxt_tools.h" // to have SIMD_ALIGN and stdint.h
 
 
-
-
 #define HXT_GHOST_VERTEX UINT32_MAX
 #define HXT_DELETED_COLOR (UINT16_MAX-1)
 
 #define HXT_NO_ADJACENT UINT64_MAX
 
+/* Element types, same as Gmsh */
+typedef enum {
+  HXT_NO_ELT = 0,
+  HXT_LINE = 1,
+  HXT_TRI = 2,
+  HXT_QUAD = 3,
+  HXT_TET = 4,
+  HXT_HEX = 5,
+  HXT_PRI = 6,
+  HXT_PYR = 7
+} HXT_ELT_TYPE;
 
 struct hxtMeshStruct {
   HXTContext* ctx;
@@ -21,25 +30,68 @@ struct hxtMeshStruct {
     uint32_t size;
     double* coord; // 3 coordinates + 1 double per vertex!
   } vertices;
-  uint64_t* typeStat; // to delete
 
   // tetrahedra
   struct {
     uint32_t* node;  // aligned (size = tetrahedra.size*4*sizeof(uint32_t))
     uint64_t* neigh; // aligned (size = tetrahedra.size*4*sizeof(uint64_t))
+    char* neighType;
     uint16_t* colors;
+    uint16_t* flag;
     uint64_t num;    // number of tetrahedra
     uint64_t size;   // reserved number of tetrahedra (size of the vector)
   } tetrahedra;
+  
+  // hexahedra
+  struct {
+    uint32_t* node;  // aligned (size = hexahedra.size*8*sizeof(uint32_t))
+    uint64_t* neigh; // aligned (size = hexahedra.size*6*sizeof(uint64_t))
+    char* neighType;
+    uint16_t* colors;
+    uint16_t* flag;
+    uint64_t num;    // number of tetrahedra
+    uint64_t size;   // reserved number of hexahedra (size of the vector)
+  } hexahedra;
+
+  // prisms
+  struct {
+    uint32_t* node;  // aligned (size = prisms.size*6*sizeof(uint32_t))
+    uint64_t* neigh; // aligned (size = prisms.size*5*sizeof(uint64_t))
+    char* neighType;
+    uint16_t* colors;
+    uint16_t* flag;
+    uint64_t num;    // number of tetrahedra
+    uint64_t size;   // reserved number of prisms (size of the vector)
+  } prisms;
+  
+  // pyramids
+  struct {
+    uint32_t* node;  // aligned (size = pyramids.size*5*sizeof(uint32_t))
+    uint64_t* neigh; // aligned (size = pyramids.size*5*sizeof(uint64_t))
+    char* neighType;
+    uint16_t* colors;
+    uint16_t* flag;
+    uint64_t num;    // number of tetrahedra
+    uint64_t size;   // reserved number of pyramids (size of the vector)
+  } pyramids;
 
   // triangles // TODO: consider writing a array of structure...
   struct {
     uint32_t* node;
+    uint64_t* neigh; 
     uint16_t* colors;
     uint64_t num;
     uint64_t size;
   } triangles;
 
+  // quads
+  struct {
+    uint32_t* node;
+    uint16_t* colors;
+    uint64_t num;
+    uint64_t size;
+  } quads;
+
   // lines // TODO: consider writing a array of structure...
   struct {
     uint32_t* node;
diff --git a/contrib/hxt/hxt_mesh3d.c b/contrib/hxt/hxt_mesh3d.c
index ebb6184f91dfbb3d7146dd88c6099fd6bd55becf..2d8a8c0f336c8d3a342eb6b2f339ec98ee50d369 100644
--- a/contrib/hxt/hxt_mesh3d.c
+++ b/contrib/hxt/hxt_mesh3d.c
@@ -1,167 +1,228 @@
-#include <math.h>
-#include <time.h>
-#include "hxt_bbox.h"
-#include "hxt_tools.h"
-#include "hxt_mesh_size.h"
+// #include "hxt_mesh_size.h"
 #include "hxt_tetrahedra.h"
-#include "hxt_vertices.h"
+// #include "hxt_vertices.h"
 #include "hxt_mesh3d.h"
 #include "predicates.h"
+#include "hxt_tetFlag.h"
 
-#ifdef HXT_MICROSOFT
-#define _CRT_RAND_S 
-#include <stdlib.h> 
-double drand48() {
-  double a;
-  rand_s(&a);
-  return a;
-}
-#endif
+// #if defined(_MSC_VER)
+// #define _CRT_RAND_S 
+// #include <stdlib.h> 
+// double drand48() {
+//   double a;
+//   rand_s(&a);
+//   return a;
+// }
+// #endif
 
-static inline void sort3ints(uint32_t *i){
-  uint32_t no1 = i[0];
-  uint32_t no2 = i[1];
-  uint32_t no3 = i[2];
-
-  if (no1>no2) {   
-    i[1]=no1;    
-    i[0]=no2;   
-  } else {
-    i[1]=no2;  
-    i[0]=no1;  
-  } 
-  if (i[1]>no3) { 
-    i[2]=i[1];    
-    if(i[0]>no3){         
-      i[1]=i[0];                
-      i[0]=no3;
-    }else {
-      i[1]=no3;      
-    }         
-  }else i[2]=no3;
-  //printf("%u %u %u\n",i[0],i[1],i[2]);
-}
 
 
-static inline int facecmp(const void *p0, const void *p1)
-{ 
-  uint32_t *f0 = (uint32_t*)p0;
-  uint32_t *f1 = (uint32_t*)p1;
-  
-  if (f0[0] != f1[0]) return f0[0] - f1[0];
-  if (f0[1] != f1[1]) return f0[1] - f1[1];
-  return f0[2] - f1[2];
-}
+HXTStatus hxtComputeMeshSizeFromTrianglesAndLines(HXTMesh* mesh, HXTDelaunayOptions* delOptions) {
 
-HXTStatus hxtCreateFaceSearchStructure(HXTMesh* mesh, uint32_t **pfaces){
-  uint32_t *tfaces;
-  
-  HXT_CHECK(hxtMalloc(&tfaces,3*mesh->triangles.num*sizeof(uint32_t)));
-  memcpy (tfaces, mesh->triangles.node, 3*mesh->triangles.num*sizeof(uint32_t));    
+  HXT_CHECK(hxtAlignedMalloc(&delOptions->nodalSizes,mesh->vertices.num*sizeof(double))); 
   
-#pragma omp parallel for
-  for (uint32_t i = 0; i<mesh->triangles.num; i++)sort3ints(&tfaces[3*i]);
-  
-  // CELESTIN FIXME : SHOULD USE YOUR PARALLEL SORT
-  qsort(tfaces,mesh->triangles.num,3*sizeof(uint32_t),facecmp);
-  *pfaces = tfaces;
-  return HXT_STATUS_OK;  
-}
-
-HXTStatus hxtComputeMeshSizeFromMesh (HXTMesh* mesh, double **psizes){
+  #pragma omp parallel for
+  for (uint32_t i = 0; i<mesh->vertices.num; i++){
+    delOptions->nodalSizes[i] = DBL_MAX;
+  }
 
-  double *sizes;
-  HXT_CHECK(hxtMalloc(&sizes,mesh->vertices.num*sizeof(double))); 
-  
-#pragma omp parallel for
-  for (uint32_t i = 0; i<mesh->tetrahedra.num; i++){
-    for (uint32_t j = 0; j<4; j++){  
-      uint32_t n1 = mesh->tetrahedra.node[4*i+j];      
-      if (n1 != HXT_GHOST_VERTEX)
-        sizes[n1] = 1.e22;
+  // only do for triangles
+  // we do not take into account hereafter delOptions->nodalSizes = to DBL_MAX
+  // could be changed in another fashion
+  for (uint32_t i = 0; i<mesh->triangles.num; i++){
+    for (uint32_t j = 0; j<3; j++){  
+      for (uint32_t k = j+1; k<3; k++){  
+        uint32_t n1 = mesh->triangles.node[3*i+j];
+        uint32_t n2 = mesh->triangles.node[3*i+k];
+        if (n1 != HXT_GHOST_VERTEX && n2 != HXT_GHOST_VERTEX){
+          double *X1 = mesh->vertices.coord + (size_t) 4*n1;
+          double *X2 = mesh->vertices.coord + (size_t) 4*n2;
+          double l = sqrt ((X1[0]-X2[0])*(X1[0]-X2[0])+
+                           (X1[1]-X2[1])*(X1[1]-X2[1])+
+                           (X1[2]-X2[2])*(X1[2]-X2[2]));
+          if(l<delOptions->nodalSizes[n1]) delOptions->nodalSizes[n1] = l;
+          if(l<delOptions->nodalSizes[n2]) delOptions->nodalSizes[n2] = l;
+        }
+      }
     }
   }
 
-
-#pragma omp parallel for
-  for (uint32_t i = 0; i<mesh->tetrahedra.num; i++){
-    for (uint32_t j = 0; j<4; j++){  
-      uint32_t n1 = mesh->tetrahedra.node[4*i+(j+0)%4];
-      uint32_t n2 = mesh->tetrahedra.node[4*i+(j+1)%4];
+  for (uint32_t i = 0; i<mesh->lines.num; i++){
+      uint32_t n1 = mesh->lines.node[2*i+0];
+      uint32_t n2 = mesh->lines.node[2*i+1];
       if (n1 != HXT_GHOST_VERTEX && n2 != HXT_GHOST_VERTEX){
-        double *X1 = mesh->vertices.coord + 4*n1;
-        double *X2 = mesh->vertices.coord + 4*n2;
+        double *X1 = mesh->vertices.coord + (size_t) 4*n1;
+        double *X2 = mesh->vertices.coord + (size_t) 4*n2;
         double l = sqrt ((X1[0]-X2[0])*(X1[0]-X2[0])+
                          (X1[1]-X2[1])*(X1[1]-X2[1])+
                          (X1[2]-X2[2])*(X1[2]-X2[2]));
-        sizes [n1] = l < sizes [n1] ? l : sizes [n1];
-        sizes [n2] = l < sizes [n2] ? l : sizes [n2];
+        if(l<delOptions->nodalSizes[n1]) delOptions->nodalSizes[n1] = l;
+        if(l<delOptions->nodalSizes[n2]) delOptions->nodalSizes[n2] = l;
+    }
+  }
+  return HXT_STATUS_OK;    
+}
+
+HXTStatus hxtComputeMeshSizeFromMesh (HXTMesh* mesh, HXTDelaunayOptions* delOptions) {
+
+  HXT_CHECK(hxtAlignedMalloc(&delOptions->nodalSizes,mesh->vertices.num*sizeof(double))); 
+  
+  #pragma omp parallel for
+  for (uint32_t i = 0; i<mesh->vertices.num; i++){
+    delOptions->nodalSizes[i] = DBL_MAX;
+  }
+
+  // only do for triangles
+  // we do not take into account hereafter delOptions->nodalSizes = to DBL_MAX
+  // could be changed in another fashion
+  for (uint32_t i = 0; i<mesh->tetrahedra.num; i++){
+    for (uint32_t j = 0; j<4; j++){  
+      for (uint32_t k = j+1; k<4; k++){  
+        uint32_t n1 = mesh->tetrahedra.node[4*i+j];
+        uint32_t n2 = mesh->tetrahedra.node[4*i+k];
+        if (n1 != HXT_GHOST_VERTEX && n2 != HXT_GHOST_VERTEX){
+          double *X1 = mesh->vertices.coord + (size_t) 4*n1;
+          double *X2 = mesh->vertices.coord + (size_t) 4*n2;
+          double l = sqrt ((X1[0]-X2[0])*(X1[0]-X2[0])+
+               (X1[1]-X2[1])*(X1[1]-X2[1])+
+               (X1[2]-X2[2])*(X1[2]-X2[2]));
+          if(l<delOptions->nodalSizes[n1]) delOptions->nodalSizes[n1] = l;
+          if(l<delOptions->nodalSizes[n2]) delOptions->nodalSizes[n2] = l;
+        }
       }
     }
   }
-  *psizes = sizes;
   return HXT_STATUS_OK;    
 }
 
-// TODO: use a sort on the index only and then put everything into order...
-HXTStatus hxtEmptyMesh(HXTMesh* mesh){
+
+
+
+HXTStatus hxtEmptyMesh(HXTMesh* mesh, HXTDelaunayOptions* delOptions){
 // we assume that the input is a surface mesh
   if (mesh->tetrahedra.num)  
     return HXT_ERROR_MSG(HXT_STATUS_FAILED, "The input mesh should only contain triangles");
   if (mesh->triangles.num == 0)  
     return HXT_ERROR_MSG(HXT_STATUS_FAILED, "The input mesh should contain triangles");
 
-  hxtNodeInfo* nodeInfo;
+  double minDist2 = DBL_MAX;
+  #pragma omp parallel for reduction(min:minDist2)
+  for (uint64_t i=0; i<mesh->triangles.num; i++){
+    uint32_t* node = mesh->triangles.node + 3*i;
+    for (int j=0; j<3; j++) {
+      double* n1 = mesh->vertices.coord + (size_t) 4*node[j];
+      double* n2 = mesh->vertices.coord + (size_t) 4*node[(j+1)%3];
+
+      double dist2 = (n1[0]-n2[0])*(n1[0]-n2[0])
+                   + (n1[1]-n2[1])*(n1[1]-n2[1])
+                   + (n1[2]-n2[2])*(n1[2]-n2[2]);
+
+      if(dist2<minDist2)
+        minDist2 = dist2;
+    }
+  }
 
+  double minSize = sqrt(minDist2);
+
+  hxtNodeInfo* nodeInfo;
   HXT_CHECK( hxtAlignedMalloc(&nodeInfo, sizeof(hxtNodeInfo)*mesh->vertices.num) );
 
   #pragma omp parallel for simd aligned(nodeInfo:SIMD_ALIGN)
   for (uint32_t i=0; i<mesh->vertices.num; i++) {
     nodeInfo[i].node = i;
+    nodeInfo[i].status = HXT_STATUS_TRYAGAIN;
   }
 
-  HXT_CHECK( hxtDelaunaySteadyVertices(mesh, NULL, nodeInfo, mesh->vertices.num) );
+  delOptions->minSizeStart = 0.0;
+  delOptions->minSizeEnd = minSize;
+  HXT_CHECK( hxtDelaunaySteadyVertices(mesh, delOptions, nodeInfo, mesh->vertices.num) );
+  delOptions->minSizeStart = minSize;
+  delOptions->minSizeEnd = minSize;
+  delOptions->numVerticesInMesh = mesh->vertices.num;
+
+#ifdef DEBUG
+  #pragma omp parallel for simd aligned(nodeInfo:SIMD_ALIGN)
+  for (uint32_t i=0; i<mesh->vertices.num; i++) {
+    if(nodeInfo[i].status!=HXT_STATUS_TRUE){
+      HXT_WARNING("vertex %u of the empty mesh was not inserted\n", nodeInfo[i].node);
+    }
+  }
+#endif
 
   HXT_CHECK( hxtAlignedFree(&nodeInfo) );
   
   return HXT_STATUS_OK;
-  
 }
 
 
-HXTStatus hxtVerifyBoundary(HXTMesh* mesh, uint32_t *missing) {
 
-  uint32_t ft [4][3] = {{0,1,2},{0,1,3},{0,2,3},{1,2,3}};
-  
-  uint16_t *flags;
-  HXT_CHECK(hxtMalloc(&flags,mesh->triangles.num*sizeof(uint16_t))); 
-#pragma omp parallel for
-  for (uint32_t i = 0; i<mesh->triangles.num; i++)flags[i] = 0;
 
-  uint32_t *tfaces;
-  HXT_CHECK(hxtCreateFaceSearchStructure(mesh, &tfaces));   
-  
-#pragma omp parallel for
-  for (uint64_t i=0; i<mesh->tetrahedra.num; i++){
-    uint32_t *v = &mesh->tetrahedra.node[4*i];
-    for (uint32_t j=0; j<4;j++){
-      uint32_t facet[3] = {v[ft[j][0]],v[ft[j][1]],v[ft[j][2]]};
-      sort3ints (facet);
-      uint32_t* found = (uint32_t*)bsearch (facet, tfaces, mesh->triangles.num, 3*sizeof(uint32_t), facecmp);
-      if (found) {
-        uint32_t facetId = (found - tfaces)/3;
-        flags[facetId] = 1;
+
+/***************************************************
+ *      Coloring the mesh                          *
+ ***************************************************/
+HXTStatus hxtColorMesh(HXTMesh* mesh, uint16_t *nbColors) {
+  uint64_t *stack;
+  HXT_CHECK(hxtMalloc(&stack,mesh->tetrahedra.num*sizeof(uint64_t))); 
+  // now that tetrahedra are flaged, we can proceed to colorize the mesh
+  memset(mesh->tetrahedra.colors, 0, mesh->tetrahedra.size*sizeof(uint16_t));
+
+  uint16_t color = 1;
+  uint16_t colorOut = 0;
+
+  while (1){
+    uint64_t stackSize = 0;
+    uint64_t first = UINT64_MAX;
+
+    for (uint64_t i=0; i<mesh->tetrahedra.num; i++) {
+      if(mesh->tetrahedra.colors[i]==0){
+        first = i;
+        break;
       }
     }
+
+    if(first==UINT64_MAX)
+      break;
+
+    stack[stackSize++] = first;
+    mesh->tetrahedra.colors[first] = color;
+
+    for (uint64_t i=0; i<stackSize; i++) {
+      uint64_t t = stack[i];
+
+      if (mesh->tetrahedra.node[4*t+3] == HXT_GHOST_VERTEX)
+        colorOut = color;
+
+      for (unsigned j=0; j<4; j++) {
+        if(mesh->tetrahedra.neigh[4*t+j]!=HXT_NO_ADJACENT && isFacetConstrained(mesh, 4*t+j)==0){ // the facet is not a boundary facet
+          uint64_t neigh = mesh->tetrahedra.neigh[4*t+j]/4;
+          if(mesh->tetrahedra.colors[neigh]==0){
+            stack[stackSize++] = neigh;
+            mesh->tetrahedra.colors[neigh] = color;
+          }
+        }
+      }
+    }
+    color++;
+  }
+  *nbColors = color-1; // -1 because we began at one AND the colorout is counted...
+
+  HXT_CHECK( hxtFree(&stack) );
+
+  #pragma omp parallel for
+  for (int i=0;i<mesh->tetrahedra.num;i++){
+    if (mesh->tetrahedra.colors[i] == colorOut){
+      mesh->tetrahedra.colors[i] = UINT16_MAX;
+    }
+    else if(mesh->tetrahedra.colors[i] > colorOut){
+      mesh->tetrahedra.colors[i]--;
+    }
   }
 
-  *missing=0;
-  for (uint32_t i = 0; i<mesh->triangles.num; i++)*missing += 1 - flags[i];
-  HXT_CHECK(hxtFree(&tfaces)); 
-  HXT_CHECK(hxtFree(&flags)); 
   return HXT_STATUS_OK;
 }
 
+
 // refine 
 
 
@@ -243,143 +304,139 @@ double hxtTetCircumcenter(double a[3], double b[3], double c[3], double d[3],
 }
 
 
-/// Starts from an empty 3D mesh and color the different regions 
-/// assume that neighbors are computed
-HXTStatus hxtColorMesh(HXTMesh* mesh, uint32_t *nbColors) { // TODO: why is nbColors unused ??
-  // compute the face search structure
-  uint32_t *tfaces;
-  HXT_CHECK(hxtCreateFaceSearchStructure(mesh, &tfaces));   
-  
-  // TODO: check this out
-#pragma omp parallel for
-  for (uint64_t i = 0; i<mesh->tetrahedra.num; i++)
-    mesh->tetrahedra.colors[i] = 0 ;//mesh->tetrahedra.node[4*i+3]!=HXT_GHOST_VERTEX ? 0 : UINT16_MAX;
-
-  uint16_t color = 1;
-  uint16_t colorOut = 0;
+HXTStatus hxtRefineTetrahedraOneStep(HXTMesh* mesh, HXTDelaunayOptions* delOptions, HXTMeshSize* sizeField, int *nbAdd, int iter)
+{
+  double *newVertices;
+  uint32_t *numCreated;
+  int maxThreads = omp_get_max_threads();
+  HXT_CHECK( hxtAlignedMalloc(&newVertices, sizeof(double)*4*mesh->tetrahedra.num) );
+  HXT_CHECK( hxtMalloc(&numCreated, maxThreads*sizeof(uint32_t)) );
 
-  uint64_t *stack;
-  HXT_CHECK(hxtMalloc(&stack,mesh->tetrahedra.num*sizeof(uint64_t))); 
-  while (1){
-    uint64_t stackSize = 0;
-    uint64_t first;
+  
+  // TODO: creating multiple vertices per tetrahedron
+  uint32_t add = 0;
+  HXTStatus status = HXT_STATUS_OK;
+  #pragma omp parallel reduction(+:add)
+  {
+    int threadID = omp_get_thread_num();
+    uint32_t localAdd = 0;
 
-    for ( first = 0; first<mesh->tetrahedra.num; first++)if (mesh->tetrahedra.colors[first] == 0) break;    
     
-    if (first == mesh->tetrahedra.num) break;
-    stack[stackSize++] = first;
-    int count = 1;
-    while (stackSize){
-      stackSize --;
-      uint64_t t = stack[stackSize];
-      mesh->tetrahedra.colors[t] = color;
-      if (mesh->tetrahedra.node[4*t+3] == HXT_GHOST_VERTEX)
-        colorOut = color;
-      for (uint16_t i = 0; i< 4;i++){
-        uint64_t neigh = mesh->tetrahedra.neigh[4*t+i]/4;
-        if (!mesh->tetrahedra.colors[neigh]){
-          uint32_t* const Node = mesh->tetrahedra.node + 4*t;
-          uint32_t facet [3] = {Node[(i+1)%4], Node[(i+2)%4], Node[(i+3)%4]};
-          sort3ints(facet);
-          uint32_t* found = (uint32_t*)bsearch (facet, tfaces, mesh->triangles.num, 3*sizeof(uint32_t), facecmp);
-          if (!found){
-            stack[stackSize++] = neigh;
-            count ++;
+    #pragma omp for schedule(static)
+    for (uint64_t i=0; i<mesh->tetrahedra.num; i++)
+    {
+      newVertices[(size_t) 4*i+3] = -1.0;
+      if (mesh->tetrahedra.colors[i] != UINT16_MAX && isTetProcessed(mesh, i)==0){
+        double *a = mesh->vertices.coord + (size_t) 4*mesh->tetrahedra.node[4*i+0];
+        double *b = mesh->vertices.coord + (size_t) 4*mesh->tetrahedra.node[4*i+1];
+        double *c = mesh->vertices.coord + (size_t) 4*mesh->tetrahedra.node[4*i+2];
+        double *d = mesh->vertices.coord + (size_t) 4*mesh->tetrahedra.node[4*i+3];
+        double circumcenter [3];
+        double u,v,w;
+        hxtTetCircumcenter(a,b,c,d, circumcenter, &u, &v, &w);
+        double circumradius2 = (a[0]-circumcenter[0])*(a[0]-circumcenter[0])+
+                               (a[1]-circumcenter[1])*(a[1]-circumcenter[1])+
+                               (a[2]-circumcenter[2])*(a[2]-circumcenter[2]);
+        // all new edges will have a length equal to circumradius2
+        double meshSize;
+        //        HXTStatus status = hxtMeshSizeEvaluate ( sizeField, circumcenter, &meshSize);
+
+        double SIZES[4];
+        double AVG = 0;
+        int NN = 0;
+        for (int j=0;j<4;j++){
+          SIZES[j] = delOptions->nodalSizes[mesh->tetrahedra.node[4*i+j]];
+          if (SIZES[j] != DBL_MAX){
+            NN++;
+            AVG += SIZES[j];
+          }
+        }
+        if (NN != 4){
+          AVG /= NN;
+          for (int j=0;j<4;j++){
+            if (SIZES[j] == DBL_MAX){
+              // delOptions->nodalSizes[mesh->tetrahedra.node[4*i+j]] = AVG;
+              SIZES[j] = AVG;
+            }
           }
         }
+  
+        meshSize = SIZES[0] * (1-u-v-w) + SIZES[1] * u + SIZES[2] * v + SIZES[3] * w;
+  
+        if (u > 0 && v > 0 && w > 0 && 1.-u-v-w > 0 && meshSize * meshSize * .49 < circumradius2) {
+          //    printf("%llu %g\n",i,sqrt(circumradius2),meshSize);
+          newVertices[(size_t) 4*i  ] = circumcenter[0];
+          newVertices[(size_t) 4*i+1] = circumcenter[1];
+          newVertices[(size_t) 4*i+2] = circumcenter[2];
+          newVertices[(size_t) 4*i+3] = meshSize;
+          localAdd++;
+        }
+
+        markTetAsProcessed(mesh, i); // we do not need to refine that tetrahedra anymore
       }
     }
-    color++;
-  }
-  for (int i=0;i<mesh->tetrahedra.num;i++)if (mesh->tetrahedra.colors[i] == colorOut) mesh->tetrahedra.colors[i] = UINT16_MAX;
-  HXT_CHECK(hxtFree(&stack)); 
-  HXT_CHECK(hxtFree(&tfaces)); 
-  return HXT_STATUS_OK;
-}
-
 
-HXTStatus hxtRefineTetrahedraOneStep(HXTMesh* mesh, HXTMeshSize* sizeField, int *nbAdd, double **nodalSizes, int iter) {
+    numCreated[threadID] = localAdd;
+
+    #pragma omp barrier
+    #pragma omp single
+    {
+      int nthreads = omp_get_num_threads();
+      add = 0;
+      for (int i=0; i<nthreads; i++) {
+        uint32_t tsum = add + numCreated[i];
+        numCreated[i] = add;
+        add = tsum;
+      }
 
-  double *newSizes;
-  double *newVertices;
-  HXT_CHECK(hxtMalloc(&newVertices, 4*mesh->tetrahedra.num*sizeof(double))); 
-  HXT_CHECK(hxtMalloc(&newSizes,mesh->tetrahedra.num*sizeof(double))); 
-#pragma omp parallel for
-  for (uint64_t i=0; i<mesh->tetrahedra.num; i++)
-    newVertices[4*i+3] = 0.0;
-  
-  // TODO: not creating point stupidly
-  uint32_t add = 0;
-#pragma omp parallel for reduction (+:add)
-  for (uint64_t i=0; i<mesh->tetrahedra.num; i++){
-    uint16_t myColor = mesh->tetrahedra.colors[i];
-    if (myColor != UINT16_MAX){
-      double *a = mesh->vertices.coord + 4*mesh->tetrahedra.node[4*i+0];
-      double *b = mesh->vertices.coord + 4*mesh->tetrahedra.node[4*i+1];
-      double *c = mesh->vertices.coord + 4*mesh->tetrahedra.node[4*i+2];
-      double *d = mesh->vertices.coord + 4*mesh->tetrahedra.node[4*i+3];
-      double circumcenter [3];
-      double u,v,w;
-      hxtTetCircumcenter(a,b,c,d, circumcenter, &u, &v, &w);
-      double circumradius = sqrt((a[0]-circumcenter[0])*(a[0]-circumcenter[0])+
-                                 (a[1]-circumcenter[1])*(a[1]-circumcenter[1])+
-                                 (a[2]-circumcenter[2])*(a[2]-circumcenter[2]));
-      // all new edges will have a length equal to circumradius
-      double meshSize;
-      //        HXTStatus status = hxtMeshSizeEvaluate ( sizeField, circumcenter, &meshSize);
-      meshSize =
-        (*nodalSizes)[mesh->tetrahedra.node[4*i+0]] * (1-u-v-w) +
-        (*nodalSizes)[mesh->tetrahedra.node[4*i+1]] * u +
-        (*nodalSizes)[mesh->tetrahedra.node[4*i+2]] * v +
-        (*nodalSizes)[mesh->tetrahedra.node[4*i+3]] * w;
-      //        printf("%g %g %g %g\n",u,v,w,meshSize);
-      // if mesh size  is significantly smaller than circumradius
-      if (u > 0 && v > 0 && w > 0 && 1.-u-v-w > 0 && meshSize *.8 < circumradius) {
-        //            printf("%llu %g\n",i,circumradius);
-        newVertices[4*i  ] = circumcenter[0];
-        newVertices[4*i+1] = circumcenter[1];
-        newVertices[4*i+2] = circumcenter[2];
-        newVertices[4*i+3] = 1.0;
-        newSizes[i] = meshSize;
-        add++;
+      if(mesh->vertices.num + add>mesh->vertices.size){
+        status=hxtAlignedRealloc(&mesh->vertices.coord, sizeof(double)*4*(mesh->vertices.num + add));
+        if(status==HXT_STATUS_OK){
+          status=hxtAlignedRealloc(&delOptions->nodalSizes, (mesh->vertices.num + add)*sizeof(double));
+          mesh->vertices.size = mesh->vertices.num + add;
+        }
       }
     }
-  }  
 
-  if (mesh->vertices.num + add >= mesh->vertices.size){
-    HXT_CHECK(hxtRealloc(&mesh->vertices.coord,4*(mesh->vertices.num + add)*sizeof(double))); 
-    HXT_CHECK(hxtRealloc(nodalSizes,(mesh->vertices.num + add)*sizeof(double))); 
-    mesh->vertices.size = mesh->vertices.num + add;
+    localAdd = numCreated[threadID] + mesh->vertices.num;
+
+    if(status==HXT_STATUS_OK){
+      #pragma omp for schedule(static)
+      for (uint64_t i=0; i<mesh->tetrahedra.num; i++){
+        if ( newVertices [4*i+3]!=-1.0 ) {
+          mesh->vertices.coord[ (size_t) 4*localAdd   ] = newVertices [(size_t) 4*i  ];
+          mesh->vertices.coord[ (size_t) 4*localAdd+1 ] = newVertices [(size_t) 4*i+1];
+          mesh->vertices.coord[ (size_t) 4*localAdd+2 ] = newVertices [(size_t) 4*i+2];
+          delOptions->nodalSizes[localAdd] = newVertices [4*i+3];
+          localAdd++;
+        }
+      }
+    }
   }
 
-  add = 0;
-// #pragma omp parallel for
-  for (uint64_t i=0; i<mesh->tetrahedra.num; i++){
-    if ( newVertices [4*i+3]!=0.0 ) {
-      mesh->vertices.coord[ 4*mesh->vertices.num   ] = newVertices [4*i  ];
-      mesh->vertices.coord[ 4*mesh->vertices.num+1 ] = newVertices [4*i+1];
-      mesh->vertices.coord[ 4*mesh->vertices.num+2 ] = newVertices [4*i+2];
-      (*nodalSizes) [ mesh->vertices.num ] = newSizes[i];
-      mesh->vertices.num++;
-      add++;
-    }
+  if(status!=HXT_STATUS_OK){
+    HXT_TRACE(status);
+    return status;
   }
+
+  mesh->vertices.num += add;
+
+
+  HXT_CHECK( hxtFree(&numCreated) );
+  HXT_CHECK(hxtAlignedFree(&newVertices));
   
-  HXT_CHECK(hxtDelaunay(mesh, NULL));
+  HXT_CHECK(hxtDelaunay(mesh, delOptions));
 
-  HXT_CHECK(hxtFree(&newVertices));
-  HXT_CHECK(hxtFree(&newSizes));
-  *nbAdd = add;
+  *nbAdd = mesh->vertices.num - delOptions->numVerticesInMesh;
+  delOptions->numVerticesInMesh = mesh->vertices.num;
   return HXT_STATUS_OK;
 }
 
-HXTStatus hxtRefineTetrahedra(HXTMesh* mesh, HXTMeshSize* sizeField, double **nodalSizes) {
+HXTStatus hxtRefineTetrahedra(HXTMesh* mesh, HXTDelaunayOptions* delOptions, HXTMeshSize* sizeField) {
   int iter = 0;
-  while(iter++ < 20){
+  while(iter++ < 40){
     int nbAdd=0;
-    clock_t t1 = clock();
-    HXT_CHECK(hxtRefineTetrahedraOneStep(mesh, sizeField, &nbAdd, nodalSizes, iter));
-    HXT_INFO("ITERATION %3d -- %3f seconds\n",iter,(double)(clock()-t1)/CLOCKS_PER_SEC); 
+    HXT_CHECK(hxtRefineTetrahedraOneStep(mesh, delOptions, sizeField, &nbAdd, iter));
     //    uint32_t nb;
     //    HXT_CHECK(hxtColorMesh(mesh,&nb));
     if (nbAdd == 0) break;
diff --git a/contrib/hxt/hxt_mesh3d.h b/contrib/hxt/hxt_mesh3d.h
index a795d091cb6d9a3e76450a3a3c5646945f343c0f..8b67844d75ad8faef1ffc73717da4529d1f37824 100644
--- a/contrib/hxt/hxt_mesh3d.h
+++ b/contrib/hxt/hxt_mesh3d.h
@@ -1,21 +1,20 @@
 #ifndef _HXT_MESH_3D_
 #define _HXT_MESH_3D_
 
-#include <hxt_tools.h>
+#include "hxt_tetrahedra.h"
 
 /// Creates a structure that allows to look over triangular faces of the 2D mesh
 HXTStatus hxtCreateFaceSearchStructure(HXTMesh* mesh, uint32_t **pfaces);
 //// creates a mesh with all points of the surface mesh
-HXTStatus hxtEmptyMesh(HXTMesh* mesh);
+HXTStatus hxtEmptyMesh(HXTMesh* mesh, HXTDelaunayOptions* delOptions);
 /// Compute sizes at vertices of the mesh from existing edges of the tetrahera
-HXTStatus hxtComputeMeshSizeFromMesh (HXTMesh* mesh, double **sizes);
-/// Verify if all facets are present in a tetrahedrization, missing is the number of missing facets
-HXTStatus hxtVerifyBoundary(HXTMesh* mesh, uint32_t *missing);
+HXTStatus hxtComputeMeshSizeFromTrianglesAndLines (HXTMesh* mesh, HXTDelaunayOptions* delOptions);
+HXTStatus hxtComputeMeshSizeFromMesh (HXTMesh* mesh, HXTDelaunayOptions* delOptions);
+/// Gives a unique color to each enclosed volume
+HXTStatus hxtColorMesh(HXTMesh* mesh, uint16_t *nbColors);
 /// Recover the boundary
 HXTStatus hxtRecoverBoundary(HXTMesh* mesh);
-/// Starts from an empty 3D mesh and color the different regions 
-HXTStatus hxtColorMesh(HXTMesh* mesh, uint32_t *nbColors);
 /// Add points at tets circumcenter in order to fullfill a mesh size constraint 
-HXTStatus hxtRefineTetrahedra(HXTMesh* mesh, HXTMeshSize* meshsize, double **nodalSizes);
+HXTStatus hxtRefineTetrahedra(HXTMesh* mesh, HXTDelaunayOptions* delOptions, HXTMeshSize* meshsize);
 
 #endif
diff --git a/contrib/hxt/hxt_mesh3d_main.c b/contrib/hxt/hxt_mesh3d_main.c
new file mode 100644
index 0000000000000000000000000000000000000000..bbd5e7c6b36f7a79c4a12fa708f4c234280e1971
--- /dev/null
+++ b/contrib/hxt/hxt_mesh3d_main.c
@@ -0,0 +1,192 @@
+#include "hxt_mesh3d.h"
+#include "hxt_tetrahedra.h"
+#include "hxt_tetRepair.h"
+#include "hxt_tetUtils.h"
+#include "hxt_tetFlag.h"
+#include "hxt_tetOpti.h"
+
+HXTStatus hxtTetMesh3d(HXTMesh* mesh,
+                      int nthreads,
+                      int reproducible,
+                      int verbosity,
+                      int displayStat,
+                      int refine,  // refine if !=0
+                      int optimize,// optimize quality if !=0
+                      double qualityThreshold,
+                      HXTStatus (*bnd_recovery)(HXTMesh* mesh)) {
+  double t[8]={0};
+  t[0] = omp_get_wtime();
+
+  HXTBbox bbox;
+  hxtBboxInit(&bbox);
+  HXT_CHECK( hxtBboxAdd(&bbox, mesh->vertices.coord, mesh->vertices.num) );
+
+  HXTDelaunayOptions delOptions = {&bbox, NULL, 0.0, 0.0, 0, verbosity, reproducible, nthreads};
+  uint32_t numVerticesConstrained = mesh->vertices.num;
+  
+  HXT_INFO_COND(verbosity>0, "Creating an empty mesh with %u vertices", numVerticesConstrained);
+  HXT_CHECK( hxtEmptyMesh(mesh, &delOptions) );
+
+  t[1] = omp_get_wtime();
+
+  uint64_t nbMissingFacets, nbMissingEdges=0;
+  uint16_t nbColors;
+  HXT_CHECK( hxtConstrainTriangles(mesh, &nbMissingFacets) );
+  if(nbMissingFacets==0) // TODO: differentiating missing triangles and missing edges ??
+    HXT_CHECK( hxtConstrainEdgesNotInTriangles(mesh, &nbMissingEdges) );
+
+  t[2] = omp_get_wtime();
+
+  if (nbMissingFacets != 0 || nbMissingEdges!=0){
+    if(bnd_recovery==NULL)
+      return HXT_ERROR_MSG(HXT_STATUS_ERROR,
+        "there are missing features but no boundary recovery function is given");
+    if(nbMissingFacets)
+      HXT_INFO("Recovering %lu missing facet(s)", nbMissingFacets);
+    else if(nbMissingEdges)
+      HXT_INFO("Recovering %lu missing edge(s)", nbMissingEdges);
+    HXT_CHECK(bnd_recovery(mesh));
+
+    if(delOptions.numVerticesInMesh < mesh->vertices.num) {
+      HXT_INFO("Steiner(s) point(s) were inserted");
+      delOptions.numVerticesInMesh = mesh->vertices.num;
+    }
+
+    t[3] = omp_get_wtime();
+
+    memset(mesh->tetrahedra.flag, 0, mesh->tetrahedra.num*sizeof(uint16_t));
+    HXT_CHECK(hxtTetOrientNodes(mesh));
+    HXT_CHECK(hxtTetAdjacencies(mesh));
+    HXT_CHECK(hxtAddGhosts(mesh));
+    // HXT_CHECK( hxtTetVerify(mesh) );
+
+    HXT_CHECK( hxtConstrainTriangles(mesh, &nbMissingFacets) );
+    if(nbMissingFacets!=0)
+      return HXT_ERROR_MSG( HXT_STATUS_ERROR,
+        "%d boundary face%s still missing (after recovery step).",
+        nbMissingFacets, (nbMissingFacets>1)?"s are":" is" );
+
+    HXT_CHECK( hxtConstrainEdgesNotInTriangles(mesh, &nbMissingEdges) );
+    if(nbMissingEdges!=0)
+      return HXT_ERROR_MSG( HXT_STATUS_ERROR,
+        "%d constrained edge%s still missing (after recovery step).",
+        nbMissingEdges, (nbMissingEdges>1)?"s are":" is" );
+  }
+
+  HXT_CHECK(hxtColorMesh(mesh, &nbColors));
+
+#ifdef DEBUG
+  HXT_CHECK( hxtTetVerify(mesh) );
+
+  memset(mesh->tetrahedra.flag, 0, mesh->tetrahedra.num*sizeof(uint16_t));
+  HXT_CHECK( hxtConstrainTriangles(mesh, &nbMissingFacets) );
+  if(nbMissingFacets!=0)
+    return HXT_ERROR_MSG( HXT_STATUS_ERROR,
+      "%d boundary face%s still missing (after refine).",
+      nbMissingFacets, (nbMissingFacets>1)?"s are":" is" );
+
+  HXT_CHECK( hxtConstrainEdgesNotInTriangles(mesh, &nbMissingEdges) );
+  if(nbMissingEdges!=0)
+    return HXT_ERROR_MSG( HXT_STATUS_ERROR,
+      "%d constrained edge%s still missing (after refine).",
+      nbMissingEdges, (nbMissingEdges>1)?"s are":" is" );
+#endif
+
+  t[4] = omp_get_wtime();
+
+  if(refine){
+    // HXT_CHECK(hxtComputeMeshSizeFromMesh(mesh, &delOptions));
+    HXT_CHECK(hxtComputeMeshSizeFromTrianglesAndLines(mesh, &delOptions));
+
+    // // triangulate only one color
+    // if(color>=0) {
+    //   #pragma omp parallel for simd
+    //   for (uint64_t i=0; i<mesh->tetrahedra.num; i++) {
+    //     if(mesh->tetrahedra.colors[i]!=theColor){
+    //       markTetAsProcessed(mesh,i) = 1;
+    //     }
+    //   }
+    // }
+
+    
+    HXTMeshSize *meshSize = NULL;
+    // HXT_CHECK(hxtMeshSizeCreate (context,&meshSize));
+    // HXT_CHECK(hxtMeshSizeCompute (meshSize, bbox.min, bbox.max, mySize, NULL));
+    //    printf("time from empty mesh to first insertion: %f second\n", omp_get_wtime() - time);
+    HXT_CHECK(hxtRefineTetrahedra(mesh, &delOptions, meshSize));
+    // HXT_CHECK(hxtMeshSizeDelete (&meshSize));
+    HXT_CHECK(hxtAlignedFree(&delOptions.nodalSizes));
+    // #endif
+  }
+
+  t[5] = omp_get_wtime();
+
+#ifdef DEBUG
+  HXT_CHECK( hxtTetVerify(mesh) );
+
+  memset(mesh->tetrahedra.flag, 0, mesh->tetrahedra.num*sizeof(uint16_t));
+  HXT_CHECK( hxtConstrainTriangles(mesh, &nbMissingFacets) );
+  if(nbMissingFacets!=0)
+    return HXT_ERROR_MSG( HXT_STATUS_ERROR,
+      "%d boundary face%s still missing (after refine).",
+      nbMissingFacets, (nbMissingFacets>1)?"s are":" is" );
+
+  HXT_CHECK( hxtConstrainEdgesNotInTriangles(mesh, &nbMissingEdges) );
+  if(nbMissingEdges!=0)
+    return HXT_ERROR_MSG( HXT_STATUS_ERROR,
+      "%d constrained edge%s still missing (after refine).",
+      nbMissingEdges, (nbMissingEdges>1)?"s are":" is" );
+#endif
+
+  if(optimize)
+    HXT_CHECK( hxtOptimizeTetrahedra(mesh, &bbox, delOptions.minSizeEnd, qualityThreshold, numVerticesConstrained) );
+
+  t[6] = omp_get_wtime();
+
+
+#ifdef DEBUG
+  HXT_CHECK( hxtTetVerify(mesh) );
+
+  memset(mesh->tetrahedra.flag, 0, mesh->tetrahedra.num*sizeof(uint16_t));
+  HXT_CHECK( hxtConstrainTriangles(mesh, &nbMissingFacets) );
+  if(nbMissingFacets!=0)
+    return HXT_ERROR_MSG( HXT_STATUS_ERROR,
+      "%d boundary face%s still missing (after refine).",
+      nbMissingFacets, (nbMissingFacets>1)?"s are":" is" );
+
+  HXT_CHECK( hxtConstrainEdgesNotInTriangles(mesh, &nbMissingEdges) );
+  if(nbMissingEdges!=0)
+    return HXT_ERROR_MSG( HXT_STATUS_ERROR,
+      "%d constrained edge%s still missing (after refine).",
+      nbMissingEdges, (nbMissingEdges>1)?"s are":" is" );
+#endif
+
+  
+  if(displayStat){
+    HXT_INFO("\n\t\tFinal tet. mesh contains %lu tetrahedra"
+             "\n\t\tand                      %u vertices", mesh->tetrahedra.num, mesh->vertices.num);
+
+    HXT_INFO("tEmptyMesh  \t = \t %8.3f", t[1]-t[0]);
+    HXT_INFO("tVerifyBnd  \t = \t %8.3f", t[2]-t[1]);
+    if(t[3]){
+      HXT_INFO("tBndRecovery\t = \t %8.3f", t[3]-t[2]);
+      HXT_INFO("tConvertMesh\t = \t %8.3f", t[4]-t[3]);
+      if(refine)
+        HXT_INFO("tRefine     \t = \t %8.3f", t[5]-t[4]);
+    }
+    else{
+      HXT_INFO("tBndRecovery\t = \t    0.000 (boundary not altered)");
+      HXT_INFO("tConvertMesh\t = \t    0.000 (nothing to convert)");
+      if(refine)
+        HXT_INFO("tRefine     \t = \t %8.3f", t[5]-t[2]);
+    }
+
+    if(!optimize)
+      HXT_INFO("tOptimize   \t = \t    0.000 (mesh optimization disabled)");
+    else
+      HXT_INFO("tOptimize   \t = \t %8.3f", t[6]-t[5]);
+  }
+
+  return HXT_STATUS_OK;
+}
+
diff --git a/contrib/hxt/hxt_mesh3d_main.h b/contrib/hxt/hxt_mesh3d_main.h
new file mode 100644
index 0000000000000000000000000000000000000000..af2b9ec27213f0fd795f1801d0c0090f61792dba
--- /dev/null
+++ b/contrib/hxt/hxt_mesh3d_main.h
@@ -0,0 +1,16 @@
+#ifndef _HXT_MESH_3D_MAIN_
+#define _HXT_MESH_3D_MAIN_
+
+#include "hxt_mesh.h"
+
+HXTStatus hxtTetMesh3d(HXTMesh* mesh,
+                      int nthreads,
+                      int reproducible,
+                      int verbosity,
+                      int displayStat,
+                      int refine,
+                      int optimize,
+                      double qualityThreshold,
+                      HXTStatus (*bnd_recovery)(HXTMesh* mesh));
+
+#endif
\ No newline at end of file
diff --git a/contrib/hxt/hxt_message.c b/contrib/hxt/hxt_message.c
index a22398430a85617557a68cfb1a1f47ab9c06ee40..bedd547d239570c239fb61cd29dff3d8a34b4d10 100644
--- a/contrib/hxt/hxt_message.c
+++ b/contrib/hxt/hxt_message.c
@@ -32,13 +32,25 @@ const char*  hxtGetStatusString(HXTStatus status){
     case HXT_STATUS_POINTER_ERROR:
       return "wrong pointer given";
       break;
+    case HXT_STATUS_READ_ERROR:
+      return "read error";
+      break;
+    case HXT_STATUS_WRITE_ERROR:
+      return "write error";
+      break;
+    case HXT_STATUS_RANGE_ERROR:
+      return "number out of range";
+      break;
+    case HXT_STATUS_FORMAT_ERROR:
+      return "wrong format";
+      break;
     default:
       if(status<=HXT_STATUS_INTERNAL)
         return "internal error was not catched. This should not happen";
       else if(status<0)
         return "unknow error";
       else
-        return "positive return value";
+        return "positive return value (no error)";
       break;
   }
 }
diff --git a/contrib/hxt/hxt_message.h b/contrib/hxt/hxt_message.h
index 9bba6219dc21b2e63f8f007625a12b35d2d28941..50b0a6d412e0508e4b329ffa7cd315b57bd1e26d 100644
--- a/contrib/hxt/hxt_message.h
+++ b/contrib/hxt/hxt_message.h
@@ -8,10 +8,6 @@
 #define STR(x) #x
 #define STRINGIFY(x) STR(x)
 
-#ifdef _MSC_VER
-#define __func__ __FUNCTION__
-#endif
-
 #define HXT_INFO(...)                 hxtMessageInfo(__func__, __FILE__, STRINGIFY(__LINE__), ## __VA_ARGS__ )
 #define HXT_INFO_COND(cond, ...)      ((cond)?HXT_INFO(__VA_ARGS__):HXT_STATUS_OK)
 #define HXT_WARNING(...)              hxtMessageWarning(__func__, __FILE__, STRINGIFY(__LINE__), ## __VA_ARGS__ )
@@ -38,11 +34,12 @@
 #define HXT_CHECK(status)             HXT_CHECK_MSG(status,NULL)
 
 /* use to check some expression inside of function, throw error if exp is not true */
-#ifdef DEBUG
+#ifndef NDEBUG
   #define HXT_ASSERT_MSG(exp, ...)                                          \
     do {                                                                    \
       if (!(exp)) {                                                         \
-        return HXT_ERROR_MSG(HXT_STATUS_ASSERTION_FAILED, ## __VA_ARGS__ ); \
+        HXT_ERROR_MSG(HXT_STATUS_ASSERTION_FAILED, ## __VA_ARGS__ );        \
+        abort();                                                            \
     }                                                                       \
   } while(0)
   #define HXT_ASSERT(exp)             HXT_ASSERT_MSG(exp, "assertion (" #exp ") Failed")
diff --git a/contrib/hxt/hxt_non_linear_solver.c b/contrib/hxt/hxt_non_linear_solver.c
index 656127dca5a420fd3dcba4ead4eb739adb50f039..cfaac46f2870bbc661c6dc1876018fa0dd957235 100644
--- a/contrib/hxt/hxt_non_linear_solver.c
+++ b/contrib/hxt/hxt_non_linear_solver.c
@@ -3,8 +3,8 @@
 
 HXTStatus hxtNewtonRaphson(HXTLinearSystem *nrSys, double *solution, int size, int maxiter, double tol, HXTNonLinearSolverCallbackF *fcb, HXTNonLinearSolverCallbackDF *dfcb, void *data) {
   double *delta, *rhs;
-  HXT_CHECK(hxtMalloc(&delta,size*sizeof(double)));
-  HXT_CHECK(hxtMalloc(&rhs,size*sizeof(double))); 
+  HXT_CHECK(hxtAlignedMalloc(&delta,size*sizeof(double)));
+  HXT_CHECK(hxtAlignedMalloc(&rhs,size*sizeof(double))); 
   for(int n=0; n<maxiter; n++){
     HXT_CHECK(hxtLinearSystemZeroMatrix(nrSys));
     HXT_CHECK(fcb(data,solution,nrSys,rhs));
@@ -20,8 +20,8 @@ HXTStatus hxtNewtonRaphson(HXTLinearSystem *nrSys, double *solution, int size, i
     if(nrNorm<tol)
       break;
   }
-  HXT_CHECK(hxtFree(&delta));
-  HXT_CHECK(hxtFree(&rhs));
+  HXT_CHECK(hxtAlignedFree(&delta));
+  HXT_CHECK(hxtAlignedFree(&rhs));
   return HXT_STATUS_OK;
 }
 
diff --git a/contrib/hxt/hxt_opt.c b/contrib/hxt/hxt_opt.c
new file mode 100644
index 0000000000000000000000000000000000000000..b0f27051610a3cf567f53db42af514d780ce787e
--- /dev/null
+++ b/contrib/hxt/hxt_opt.c
@@ -0,0 +1,812 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <float.h>
+#include <errno.h>
+#include <limits.h>
+#include "hxt_opt.h"
+
+
+const HXTOptionArgumentConstraints HXT_POSITIVE_RANGE = {
+	0, DBL_MAX,
+	0, LLONG_MAX,
+	0, ULLONG_MAX,
+	NULL, NULL, NULL
+};
+const HXTOptionArgumentConstraints HXT_ALPHA_LOWERCASE_RANGE = {
+	-DBL_MAX, DBL_MAX,
+	LLONG_MIN, LLONG_MAX,
+	0, ULLONG_MAX,
+	NULL, NULL, HXT_ALPHA_LOWERCASE_CHARACTERS
+};
+const HXTOptionArgumentConstraints HXT_ALPHA_UPPERCASE_RANGE = {
+	-DBL_MAX, DBL_MAX,
+	LLONG_MIN, LLONG_MAX,
+	0, ULLONG_MAX,
+	NULL, NULL, HXT_ALPHA_UPPERCASE_CHARACTERS
+};
+const HXTOptionArgumentConstraints HXT_ALPHA_RANGE = {
+	-DBL_MAX, DBL_MAX,
+	LLONG_MIN, LLONG_MAX,
+	0, ULLONG_MAX,
+	NULL, NULL, HXT_ALPHA_CHARACTERS
+};
+const HXTOptionArgumentConstraints HXT_NUMERIC_RANGE = {
+	-DBL_MAX, DBL_MAX,
+	LLONG_MIN, LLONG_MAX,
+	0, ULLONG_MAX,
+	NULL, NULL, HXT_NUMERIC_CHARACTERS
+};
+const HXTOptionArgumentConstraints HXT_ALPHANUMERIC_LOWERCASE_RANGE = {
+	-DBL_MAX, DBL_MAX,
+	LLONG_MIN, LLONG_MAX,
+	0, ULLONG_MAX,
+	NULL, NULL, HXT_ALPHANUMERIC_LOWERCASE_CHARACTERS
+};
+const HXTOptionArgumentConstraints HXT_ALPHANUMERIC_UPPERCASE_RANGE = {
+	-DBL_MAX, DBL_MAX,
+	LLONG_MIN, LLONG_MAX,
+	0, ULLONG_MAX,
+	NULL, NULL, HXT_ALPHANUMERIC_UPPERCASE_CHARACTERS
+};
+const HXTOptionArgumentConstraints HXT_ALPHANUMERIC_RANGE = {
+	-DBL_MAX, DBL_MAX,
+	LLONG_MIN, LLONG_MAX,
+	0, ULLONG_MAX,
+	NULL, NULL, HXT_ALPHANUMERIC_CHARACTERS
+};
+const HXTOptionArgumentConstraints HXT_PRINTABLE_VISIBLE_RANGE = {// between space and ~
+	-DBL_MAX, DBL_MAX,
+	LLONG_MIN, LLONG_MAX,
+	0, ULLONG_MAX,
+	NULL, NULL, HXT_PRINTABLE_VISIBLE_CHARACTERS
+};
+const HXTOptionArgumentConstraints HXT_PRINTABLE_RANGE = {
+	-DBL_MAX, DBL_MAX,
+	LLONG_MIN, LLONG_MAX,
+	0, ULLONG_MAX,
+	NULL, NULL, HXT_PRINTABLE_CHARACTERS
+};
+const HXTOptionArgumentConstraints HXT_0_1_RANGE = {
+	0., 1.,
+	0, 1,
+	0, 1,
+	NULL, NULL, NULL
+};
+const HXTOptionArgumentConstraints HXT_0_2_RANGE = {
+	0., 2.,
+	0, 2,
+	0, 2,
+	NULL, NULL, NULL
+};
+const HXTOptionArgumentConstraints HXT_0_3_RANGE = {
+	0., 3.,
+	0, 3,
+	0, 3,
+	NULL, NULL, NULL
+};
+const HXTOptionArgumentConstraints HXT_0_4_RANGE = {
+	0., 4.,
+	0, 4,
+	0, 4,
+	NULL, NULL, NULL
+};
+const HXTOptionArgumentConstraints HXT_0_5_RANGE = {
+	0., 5.,
+	0, 5,
+	0, 5,
+	NULL, NULL, NULL
+};
+const HXTOptionArgumentConstraints HXT_0_10_RANGE = {
+	0., 10.,
+	0, 10,
+	0, 10,
+	NULL, NULL, NULL
+};
+const HXTOptionArgumentConstraints HXT_0_20_RANGE = {
+	0., 20.,
+	0, 20,
+	0, 20,
+	NULL, NULL, NULL
+};
+const HXTOptionArgumentConstraints HXT_0_50_RANGE = {
+	0., 50.,
+	0, 50,
+	0, 50,
+	NULL, NULL, NULL
+};
+const HXTOptionArgumentConstraints HXT_0_100_RANGE = {
+	0., 100.,
+	0, 100,
+	0, 100,
+	NULL, NULL, NULL
+};
+const HXTOptionArgumentConstraints HXT_0_1000_RANGE = {
+	0., 1000.,
+	0, 1000,
+	0, 1000,
+	NULL, NULL, NULL
+};
+
+
+typedef struct HXTOptionStruct {
+  char shortName;
+  const char* longName;
+  const char* description;
+  const HXTOptionArgumentConstraints* constraints;
+  HXTOptionArgumentType valueType;
+  void* valuePtr;
+} HXTOption;
+
+static HXTOption helpOption = {'h', "help", "Display this help message", NULL, HXT_FLAG, NULL};
+static HXTOption* optionList = NULL;
+static int optionListLength = 0;
+static int optionListSize = 0;
+
+
+#define MAX(a,b) (((a)>(b))?(a):(b))
+
+
+static HXTStatus optionListReserve(int n)
+{
+	if(optionList==NULL) {
+		optionListSize = MAX(16,n);
+		HXT_CHECK( hxtMalloc(&optionList, optionListSize*sizeof(HXTOption)) );
+		optionList[0] = helpOption;
+		optionListLength = 1;
+	}
+	else if(optionListLength + n > optionListSize) {
+		optionListSize = MAX(2*optionListSize, optionListLength + n);
+		HXT_CHECK( hxtRealloc(&optionList, optionListSize) );
+	}
+	return HXT_STATUS_OK;
+}
+
+
+HXTStatus hxtAddOption(char shortName,
+                       const char* longName,
+                       const char* description,
+                       HXTOptionArgumentType valueType,
+                       const HXTOptionArgumentConstraints* constraints,
+                       void* valuePtr)
+{
+	HXT_CHECK( optionListReserve(1) );
+
+#ifndef NDEBUG
+	if(valuePtr==NULL && (shortName!='\0' || longName!=NULL || description!=NULL))
+		return HXT_ERROR_MSG(HXT_STATUS_ERROR, "Adding a non-empty option with a NULL value pointer makes no sense");
+	for (int i=0; i<optionListLength; i++) {
+		if(shortName!='\0' && shortName==optionList[i].shortName)
+			return HXT_ERROR_MSG(HXT_STATUS_ERROR, "-%c is already the short name of another option", shortName);
+	}
+	if(longName!=NULL) {
+		if(longName[0]=='\0')
+			return HXT_ERROR_MSG(HXT_STATUS_ERROR, "Cannot use empty string as a long option name. Use NULL pointer if you do not want a long option name.");
+		if(longName[0]=='-')
+			return HXT_ERROR_MSG(HXT_STATUS_ERROR, "long option \"%s\" cannot begin with a '-'", longName);
+		for (int i=0; longName[i]!='\0'; i++) {
+			if(longName[i]<=' ')
+				return HXT_ERROR_MSG(HXT_STATUS_ERROR, "long option \"%s\" should only contain printable characters", longName);
+		}
+		for (int i=0; i<optionListLength; i++) {
+			if(optionList[i].longName!=NULL && strcmp(longName, optionList[i].longName)==0)
+				return HXT_ERROR_MSG(HXT_STATUS_ERROR, "--%s is already the long name of another option", longName);
+		}
+	}
+#endif
+
+	if(shortName=='\0' && (longName==NULL || longName[0]=='\0') &&
+	   (valueType==HXT_FLAG || valueType==HXT_NO_FLAG) ) {
+		return HXT_ERROR_MSG(HXT_STATUS_ERROR, "A flag must have an option name. Therefore, it can not be a trailing option");
+	}
+
+	optionList[optionListLength].shortName = shortName;
+	optionList[optionListLength].longName = longName;
+	optionList[optionListLength].description = description;
+	optionList[optionListLength].valueType = valueType;
+	optionList[optionListLength].constraints = constraints;
+	optionList[optionListLength].valuePtr = valuePtr;
+	optionListLength++;
+
+	return HXT_STATUS_OK;
+}
+
+
+/*********************************************************************
+*  search a long option inside the list                              *
+*********************************************************************/
+static int searchLongOption(const char* string)
+{
+	for (int i=0; i<optionListLength; i++) {
+		if(optionList[i].longName!=NULL &&
+		   strcmp(optionList[i].longName, string)==0) {
+			return i;
+		}
+	}
+
+	return -1;
+}
+
+
+/*********************************************************************
+*  search a short option inside the list                             *
+*********************************************************************/
+static int searchShortOption(char c)
+{
+	for (int i=0; i<optionListLength; i++) {
+		if(c==optionList[i].shortName) {
+			return i;
+		}
+	}
+	return -1;
+}
+
+
+int fileExists(const char *fname)
+{
+    FILE *file;
+    if ((file = fopen(fname, "r")))
+    {
+        fclose(file);
+        return 1;
+    }
+    return 0;
+}
+
+
+static const char* getArgTypeName(HXTOptionArgumentType t){
+	switch(t) {
+	case HXT_FLAG:
+	case HXT_NO_FLAG:
+		return "(none)";
+    case HXT_DOUBLE:
+    	return "a double";
+    case HXT_FLOAT:
+    	return "a float";
+    case HXT_INT:
+    	return "an integer ";
+    case HXT_I64:
+    	return "a 64-bit integer";
+    case HXT_I32:
+    	return "a 32-bit integer";
+    case HXT_I16:
+    	return "a 16-bit integer";
+    case HXT_UNSIGNED:
+    	return "an unsigned integer ";
+    case HXT_U64:
+    	return "a 64-bit unsigned integer";
+    case HXT_U32:
+    	return "a 32-bit unsigned integer";
+    case HXT_U16:
+    	return "a 16-bit unsigned integer";
+    default:
+		return "a string";
+	}
+}
+
+
+/*********************************************************************
+*  scan the argument and verify its validity                         *
+*********************************************************************/
+static HXTStatus doOption(HXTOption* opt,
+	                      const char* arg,
+	                      const char* optName)
+{
+	if(opt->valueType==HXT_FLAG) {
+		*(int*) opt->valuePtr = 1;
+		return HXT_STATUS_OK;
+	}
+	if(opt->valueType==HXT_NO_FLAG) {
+		*(int*) opt->valuePtr = 0;
+		return HXT_STATUS_OK;
+	}
+
+	char* endptr = NULL;
+	double r = 0;
+	unsigned long long u = 0;
+	long long int i = 0;
+	const char* s = NULL;
+
+	int isDouble = 0, isInteger = 0, isUnsigned = 0;
+
+	if(opt->valueType==HXT_DOUBLE || opt->valueType==HXT_FLOAT) {
+		isDouble = 1;
+		r = strtod(arg, &endptr);
+	}
+	else if(opt->valueType==HXT_INT ||
+	        opt->valueType==HXT_I64 ||
+	        opt->valueType==HXT_I32 ||
+	        opt->valueType==HXT_I16) {
+		isInteger = 1;
+		i = strtoll(arg, &endptr, 10);
+	}
+	else if(opt->valueType==HXT_UNSIGNED ||
+	        opt->valueType==HXT_U64 ||
+	        opt->valueType==HXT_U32 ||
+	        opt->valueType==HXT_U16) {
+		isUnsigned = 1;
+		u = strtoull(arg, &endptr, 10);
+	}
+	else {
+		s = arg;
+	}
+
+	// verify the validity
+	if(errno == ERANGE){
+range_error:
+		return HXT_ERROR_MSG(HXT_STATUS_RANGE_ERROR,
+		       "cannot convert argument \"%s\" of option \"%s\" to %s (range overflow)",
+		       arg, optName, getArgTypeName(opt->valueType));
+	}
+	else if (arg == endptr)
+	{
+		return HXT_ERROR_MSG(HXT_STATUS_FORMAT_ERROR,
+		       "cannot convert argument \"%s\" of option \"%s\" to %s (no digit)",
+		       arg, optName, getArgTypeName(opt->valueType));
+	}
+	else if(endptr!=NULL && *endptr!='\0') {
+		return HXT_ERROR_MSG(HXT_STATUS_FORMAT_ERROR,
+		       "cannot convert argument \"%s\" of option \"%s\" to %s (trailing unvalid characters)",
+		       arg, optName, getArgTypeName(opt->valueType));
+	}
+
+	// verify the conversion is possible
+	switch(opt->valueType) {
+	// floating point values:
+		case HXT_DOUBLE:
+			*(double*) opt->valuePtr = r;
+			break;
+	    case HXT_FLOAT:
+		    if((float)r!=r) goto range_error;
+		    *(float*) opt->valuePtr = r;
+		    break;
+	// integer values
+	    case HXT_INT:
+		    if((int)i!=i) goto range_error;
+		    *(int*) opt->valuePtr = i;
+		    break;
+	    case HXT_I64:
+		    if((int64_t)i!=i) goto range_error;
+		    *(int64_t*) opt->valuePtr = i;
+		    break;
+	    case HXT_I32:
+		    if((int32_t)i!=i) goto range_error;
+		    *(int32_t*) opt->valuePtr = i;
+		    break;
+	    case HXT_I16:
+		    if((int16_t)i!=i) goto range_error;
+		    *(int16_t*) opt->valuePtr = i;
+		    break;
+	// unsigned integer values
+	    case HXT_UNSIGNED:
+		    if((unsigned)u!=u) goto range_error;
+		    *(unsigned*) opt->valuePtr = u;
+		    break;
+	    case HXT_U64:
+		    if((uint64_t)u!=u) goto range_error;
+		    *(uint64_t*) opt->valuePtr = u;
+		    break;
+	    case HXT_U32:
+		    if((uint32_t)u!=u) goto range_error;
+		    *(uint32_t*) opt->valuePtr = u;
+		    break;
+	    case HXT_U16:
+		    if((uint16_t)u!=u) goto range_error;
+		    *(uint16_t*) opt->valuePtr = u;
+		    break;
+   // string values
+		case HXT_EXISTING_FILENAME:
+			if(!fileExists(s))
+				return HXT_ERROR_MSG(HXT_STATUS_FILE_CANNOT_BE_OPENED,
+				                     "file \"%s\" does not exist", s);
+			*(const char**) opt->valuePtr = s;
+			break;
+		case HXT_NEW_FILENAME:
+			if(fileExists(s))
+				return HXT_ERROR_MSG(HXT_STATUS_FILE_CANNOT_BE_OPENED,
+				                     "file \"%s\" already exists", s);
+			*(const char**) opt->valuePtr = s;
+			break;
+		case HXT_ASK_TO_ERASE_FILENAME:
+			if(fileExists(s)) {
+				char memory[64];
+				HXT_INFO("file \"%s\" already exists\n do you want to overwrite it ? y/N", s);
+				char* string = fgets(memory, 64, stdin);
+				if(string==NULL || (string[0]!='y' && string[0]!='Y') )
+					return HXT_ERROR_MSG(HXT_STATUS_FILE_CANNOT_BE_OPENED, "aborting");
+			}
+			/* fall through */
+
+		default:
+			*(const char**) opt->valuePtr = s;
+	    	break;
+	}
+
+	if(opt->constraints==NULL)
+		return HXT_STATUS_OK;
+
+
+	if(isDouble){
+		if(r < opt->constraints->doubleMin || r > opt->constraints->doubleMax)
+			return HXT_ERROR_MSG(HXT_STATUS_RANGE_ERROR,
+			                     "cannot convert argument \"%s\" of option \"%s\" "
+			                     "to %s between %g and %g (range overflow)",
+			                     arg, optName, getArgTypeName(opt->valueType),
+								 opt->constraints->doubleMin, opt->constraints->doubleMax);
+	}
+	else if(isInteger){
+		if(i < opt->constraints->intMin || i > opt->constraints->intMax)
+			return HXT_ERROR_MSG(HXT_STATUS_RANGE_ERROR,
+			                     "cannot convert argument \"%s\" of option \"%s\" "
+			                     "to %s between %lld and %lld (range overflow)",
+			                     arg, optName, getArgTypeName(opt->valueType),
+								 opt->constraints->intMin, opt->constraints->intMax);
+	}
+	else if(isUnsigned){
+		if(u < opt->constraints->unsignedMin || u > opt->constraints->unsignedMax)
+			return HXT_ERROR_MSG(HXT_STATUS_RANGE_ERROR,
+			                     "cannot convert argument \"%s\" of option \"%s\" "
+			                     "to %s between %llu and %llu (range overflow)",
+			                     arg, optName, getArgTypeName(opt->valueType),
+								 opt->constraints->unsignedMin, opt->constraints->unsignedMax);
+	}
+	else {
+		if(opt->constraints->stringPrefix!=NULL) {
+			const char* prefix = opt->constraints->stringPrefix;
+			size_t lenpre = strlen(prefix), lens = strlen(s);
+			if(lens < lenpre || strncmp(prefix, s, lenpre) != 0)
+				return HXT_ERROR_MSG(HXT_STATUS_ERROR, "argument \"%s\" of option \"%s\" is not prefixed by \"%s\"", prefix);
+		}
+		if(opt->constraints->stringSuffix!=NULL) {
+			const char* suffix = opt->constraints->stringSuffix;
+			size_t lensuf = strlen(suffix), lens = strlen(s);
+			if(lens < lensuf || strcmp(suffix, s + lens - lensuf) != 0)
+				return HXT_ERROR_MSG(HXT_STATUS_ERROR, "argument \"%s\" of option \"%s\" is not suffixed by \"%s\"", suffix);
+		}
+		if(opt->constraints->stringCharAllowed!=NULL) {
+			const char* allowed = opt->constraints->stringCharAllowed;
+			for (int j=0; s[j]!='\0'; j++) {
+				int found = 0;
+				int special = 0;
+				if(opt->constraints==&HXT_ALPHA_LOWERCASE_RANGE
+				|| opt->constraints==&HXT_ALPHA_RANGE
+				|| opt->constraints==&HXT_ALPHANUMERIC_LOWERCASE_RANGE
+				|| opt->constraints==&HXT_ALPHANUMERIC_RANGE) {
+					special = 1;
+					if(s[j]>='a' && s[j]<='z') found = 1;
+				}
+				if(opt->constraints==&HXT_ALPHA_UPPERCASE_RANGE
+				|| opt->constraints==&HXT_ALPHA_RANGE
+				|| opt->constraints==&HXT_ALPHANUMERIC_UPPERCASE_RANGE
+				|| opt->constraints==&HXT_ALPHANUMERIC_RANGE) {
+					special = 1;
+					if(s[j]>='A' && s[j]<='Z') found = 1;
+				}
+				if(opt->constraints==&HXT_NUMERIC_RANGE
+				|| opt->constraints==&HXT_ALPHANUMERIC_LOWERCASE_RANGE
+				|| opt->constraints==&HXT_ALPHANUMERIC_UPPERCASE_RANGE
+				|| opt->constraints==&HXT_ALPHANUMERIC_RANGE) {
+					special = 1;
+					if(s[j]>='0' && s[j]<='9') found = 1;
+				}
+				if(opt->constraints==&HXT_PRINTABLE_VISIBLE_RANGE
+				|| opt->constraints==&HXT_PRINTABLE_RANGE) {
+					special = 1;
+					if(s[j]>' ' && s[j]<='~') found = 1;
+				}
+				if(opt->constraints==&HXT_PRINTABLE_RANGE) {
+					special = 1;
+					if(s[j]==' ' || s[j]=='\t' || s[j]=='\n') found = 1;
+				}
+				if(special!=1) {
+					for (int k=0; allowed[k]!='\0'; k++) {
+						if(s[j]==allowed[k]){
+							found = 1;
+							break;
+						}
+					}
+				}
+				
+				if(found==0)
+					return HXT_ERROR_MSG(HXT_STATUS_FORMAT_ERROR, "argument \"%s\" of option \"%s\" contain the unvalid character %c", s[j]);
+			}
+		}
+	}
+
+	return HXT_STATUS_OK;
+}
+
+
+static int getNextTrailingOption(int n) {
+	for (int i=n+1; i<optionListLength; i++) {
+		if(optionList[i].shortName=='\0' && optionList[i].longName==NULL) {
+			return i;
+		}
+	}
+	return -1;
+}
+
+
+HXTStatus hxtParseOptions(const int argc, const char* argv[])
+{
+	int dashdash = 0;
+	int trailing = getNextTrailingOption(0);
+	for (int i=1; i<argc; i++) {
+		const char *arg = NULL;
+		HXTOption* opt = NULL;
+
+		if(dashdash || argv[i][0]!='-' || (argv[i][0]=='-' && argv[i][1]=='\0')){
+			if(trailing==-1 || optionList[trailing].valuePtr==NULL)
+				return HXT_ERROR_MSG(HXT_STATUS_FORMAT_ERROR,
+				                     "Additional argument \"%s\" does not correspond to any option",
+				                     argv[i]);
+			else
+				HXT_CHECK( doOption(&optionList[trailing], argv[i], optionList[trailing].description) );
+
+			trailing = getNextTrailingOption(trailing);
+		}
+		else if(argv[i][1]=='-') { /* long opt */
+			if(argv[i][2]=='\0') /* -- terminate argument parsing,
+			everything from now on is a trailing option */ {
+				dashdash = 1;
+				continue;
+			}
+
+			char* equalSign = strchr(argv[i]+2,'=');
+			
+			if(equalSign!=NULL){
+				*equalSign = '\0';
+				arg = equalSign + 1;
+			}
+
+			int num = searchLongOption(argv[i]+2);
+			if(num<0){
+				return HXT_ERROR_MSG(HXT_STATUS_FORMAT_ERROR,
+				       "option \"%s\" not found", argv[i]);
+			}
+			opt = &optionList[num];
+
+			if(equalSign!=NULL){
+				*equalSign = '=';
+				if(opt->valueType==HXT_FLAG || opt->valueType==HXT_NO_FLAG){
+					return HXT_ERROR_MSG(HXT_STATUS_FORMAT_ERROR,
+					       "option \"%s\" takes no argument", argv[i]);
+				}
+			}
+			else{
+				if(num==0) { // it's the help option
+					return HXT_STATUS_INTERNAL;
+				}
+				if(opt->valueType!=HXT_FLAG && opt->valueType!=HXT_NO_FLAG) {
+					if(argc<=i+1) {
+						return HXT_ERROR_MSG(HXT_STATUS_FORMAT_ERROR,
+					       "options \"%s\" requires an argument", argv[i]);
+					}
+					else {
+						arg = argv[i+1];
+						i++;
+					}
+				}
+			}
+
+			HXT_CHECK( doOption(opt, arg, argv[i]) );
+		}
+		else{ /* short option */
+			int cond = argv[i][1];
+			for(int j=1; cond; j++){
+				int num = searchShortOption(argv[i][j]);
+				if(num<0){
+					return HXT_ERROR_MSG(HXT_STATUS_FORMAT_ERROR,
+					       "option \'%c\' in \"%s\" not found", argv[i][j], argv[i]);
+				}
+
+				char optName[] = "- ";
+				optName[1] = argv[i][j];
+				cond = argv[i][j+1];
+
+				if(num==0){
+					return HXT_STATUS_INTERNAL;
+				}
+
+				opt = &optionList[num];
+
+				if(opt->valueType!=HXT_FLAG && opt->valueType!=HXT_NO_FLAG){
+					if(cond){
+						arg = argv[i] + j+1;
+						cond = 0; // stop the loop
+					}
+					else if(argc<=i+1) {
+						return HXT_ERROR_MSG(HXT_STATUS_FORMAT_ERROR,
+						       "options \'%c\' in \"%s\" requires an argument", argv[i][j], argv[i]);
+					}
+					else {
+						arg = argv[i+1];
+						i++;
+					}
+				}
+
+				HXT_CHECK( doOption(opt, arg, optName) );
+			}
+		}
+	}
+
+	if(trailing!=-1) {
+		if(getNextTrailingOption(trailing)!=-1)
+			return HXT_ERROR_MSG(HXT_STATUS_FORMAT_ERROR, "No \"%s\" given", optionList[trailing].description);
+	}
+
+	HXT_CHECK( hxtFree(&optionList) );
+
+	return HXT_STATUS_OK;
+}
+
+
+#define MY_SPRINTF(...) *offset+=snprintf(text+ *offset, 16384- *offset, ## __VA_ARGS__ )
+
+static const char* getHelpValueName(HXTOption* opt) {
+	switch(opt->valueType) {
+    case HXT_DOUBLE:
+    case HXT_FLOAT:
+    return "VAL";
+    case HXT_INT:
+    case HXT_I64:
+    case HXT_I32:
+    case HXT_I16:
+    case HXT_UNSIGNED:
+    case HXT_U64:
+    case HXT_U32:
+    case HXT_U16:
+    return "NUM";
+    case HXT_STRING:
+    return "TEXT";
+    case HXT_EXISTING_FILENAME:
+    case HXT_NEW_FILENAME:
+    case HXT_ASK_TO_ERASE_FILENAME:
+    return "FILENAME";
+    default:
+    return "(none)";
+	}
+}
+
+
+static void printOptionLine(HXTOption* opt, char text[16384], int* offset)
+{
+	const int char_max = 30;
+	int oldOffset = *offset;
+	MY_SPRINTF("  ");
+
+	if(opt->shortName!='\0'){
+		if(opt->longName!=NULL){
+			MY_SPRINTF("-%c, ", opt->shortName);
+		}
+		else{
+			MY_SPRINTF("-%c", opt->shortName);
+		}
+	}
+
+	if(opt->longName!=NULL){
+		if(opt->valueType==HXT_FLAG || opt->valueType==HXT_NO_FLAG)
+			MY_SPRINTF("--%s", opt->longName);
+		else
+			MY_SPRINTF("--%s=%s", opt->longName, getHelpValueName(opt));
+
+		
+	}
+
+	int char_count = *offset - oldOffset;
+	if(char_count<char_max)
+		MY_SPRINTF("%*c", char_max - char_count, ' ');
+	else
+		MY_SPRINTF("\n%*c", char_max, ' ');
+
+	// description
+	{
+		int i,k;
+		for (i=0,k=0; opt->description[i+1]!='\0'; i++, k++) {
+			if(opt->description[i]=='\n'){
+				if(opt->description[i+1]=='\n' || opt->description[i+1]=='\0')
+					MY_SPRINTF("%.*s\n", k, opt->description+i-k);
+				else
+					MY_SPRINTF("%.*s\n%*c", k, opt->description+i-k, char_max+2, ' ');
+				k=-1;
+			}
+		}
+		MY_SPRINTF("%s", opt->description+i-k);
+	}
+
+	// default argument
+	if(opt->valueType!=HXT_FLAG && opt->valueType!=HXT_NO_FLAG)
+	{
+		double r = 0;
+		unsigned long long u = 0;
+		long long int i = 0;
+		MY_SPRINTF("\n%*cdefault: %s=", char_max+1, ' ', getHelpValueName(opt));
+		switch(opt->valueType) {
+	    case HXT_DOUBLE:
+	    MY_SPRINTF("%g", *(double*) opt->valuePtr);
+	    break;
+	    case HXT_FLOAT:
+	    r = *(float*) opt->valuePtr;
+	    MY_SPRINTF("%g", r);
+	    break;
+	    case HXT_INT:
+		MY_SPRINTF("%d", *(int*) opt->valuePtr);
+	    break;
+	    case HXT_I64:
+	    i = *(int64_t*) opt->valuePtr;
+	    MY_SPRINTF("%lld", i);
+	    break;
+	    case HXT_I32:
+	    i = *(int32_t*) opt->valuePtr;
+	    MY_SPRINTF("%lld", i);
+	    break;
+	    case HXT_I16:
+	    i = *(int16_t*) opt->valuePtr;
+	    MY_SPRINTF("%lld", i);
+	    break;
+	    case HXT_UNSIGNED:
+	    MY_SPRINTF("%u", *(unsigned*) opt->valuePtr);
+	    break;
+	    case HXT_U64:
+	    u = *(uint64_t*) opt->valuePtr;
+	    MY_SPRINTF("%llu", u);
+	    break;
+	    case HXT_U32:
+	    u = *(uint32_t*) opt->valuePtr;
+	    MY_SPRINTF("%llu", u);
+	    break;
+	    case HXT_U16:
+	    u = *(uint16_t*) opt->valuePtr;
+	    MY_SPRINTF("%llu", u);
+	    break;
+	    case HXT_STRING:
+	    case HXT_EXISTING_FILENAME:
+	    case HXT_NEW_FILENAME:
+	    case HXT_ASK_TO_ERASE_FILENAME:
+	    if(opt->valuePtr!=NULL)
+	    	MY_SPRINTF("%s", *(const char**) opt->valuePtr);
+	    break;
+	    default:
+	    break;
+		}
+	}
+
+	MY_SPRINTF("\n");
+}
+
+
+HXTStatus hxtGetOptionHelp(char text[16384],
+                           const char* programName,
+                           const char* programDescription,
+                           const char* additionalInfo)
+{
+	int offsetval = 0;
+	int* offset = &offsetval;
+	if(programName!=NULL) {
+		MY_SPRINTF("%s [OPTION] ...", programName);
+		int n = getNextTrailingOption(0);
+		while(n!=-1 && optionList[n].valuePtr!=NULL) {
+			int n2 = getNextTrailingOption(n);
+			if(optionList[n].shortName=='\0' && optionList[n].longName==NULL && optionList[n].description!=NULL) {
+				if(n2==-1)
+					MY_SPRINTF(" [%s]", optionList[n].description);
+				else
+					MY_SPRINTF(" %s", optionList[n].description);
+			}
+			n=n2;
+		}
+	}
+
+	if(programDescription!=NULL)
+		MY_SPRINTF("\n\n%s", programDescription);
+	MY_SPRINTF("\n\n");
+
+	for (int i=0; i<optionListLength; i++) {
+		if(optionList[i].shortName!='\0' || optionList[i].longName!=NULL)
+			printOptionLine(&optionList[i], text, offset);
+	}
+
+	if(additionalInfo)
+		MY_SPRINTF("%s\n", additionalInfo);
+	return HXT_STATUS_OK;
+}
\ No newline at end of file
diff --git a/contrib/hxt/hxt_opt.h b/contrib/hxt/hxt_opt.h
new file mode 100644
index 0000000000000000000000000000000000000000..c2ad5b8b5c49e15d8d049a1815b5392fc523796a
--- /dev/null
+++ b/contrib/hxt/hxt_opt.h
@@ -0,0 +1,170 @@
+#ifndef __HXT_OPT_H__
+#define __HXT_OPT_H__
+
+#include "hxt_tools.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/****************************************************************************************
+ *  argument type: the type of the argument that must be given to the option
+ * or HXT[_NO]_FLAG to indicate that the option doesn't have an argument
+ ***************************************************************************************/
+typedef enum {
+// flags
+  // no argument must be given, the value must be an integer set to 0 (it is set to 1 when option is called)
+    HXT_FLAG,
+  // no argument must be given, the value must be an integer set to 1 (it is set to 0 when option is called)
+    HXT_NO_FLAG,
+
+// floating point values:
+    HXT_DOUBLE,
+    HXT_FLOAT,
+
+// integer values
+    HXT_INT,
+    HXT_I64,
+    HXT_I32,
+    HXT_I16,
+
+// unsigned integer values
+    HXT_UNSIGNED,
+    HXT_U64,
+    HXT_U32,
+    HXT_U16,
+
+// strings values
+    HXT_STRING,
+    HXT_EXISTING_FILENAME,
+    HXT_NEW_FILENAME,
+    HXT_ASK_TO_ERASE_FILENAME
+} HXTOptionArgumentType;
+
+/*************************************************************************************
+ *  Constraints on the accepted range of the value given in argument                 *
+ ************************************************************************************/
+typedef struct {
+  // acceptable range for floating-point values
+  double doubleMin;
+  double doubleMax;
+
+  // acceptable range for integer values
+  long long int intMin;
+  long long int intMax;
+
+  // acceptable range for unsigned integer values
+  unsigned long long unsignedMin;
+  unsigned long long unsignedMax;
+
+  // required prefix and suffix to the string (NULL pointer are omitted)
+  const char* stringPrefix;
+  const char* stringSuffix;
+
+  // character allowed, required or forbidden (NULL pointer are omitted)
+  const char* stringCharAllowed;
+} HXTOptionArgumentConstraints;
+
+#define HXT_ALPHA_LOWERCASE_CHARACTERS        "abcdefghijklmnopqrstuvwxyz"
+#define HXT_ALPHA_UPPERCASE_CHARACTERS        "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+#define HXT_ALPHA_CHARACTERS                  "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
+#define HXT_NUMERIC_CHARACTERS                "0123456789"
+#define HXT_ALPHANUMERIC_LOWERCASE_CHARACTERS "abcdefghijklmnopqrstuvwxyz0123456789"
+#define HXT_ALPHANUMERIC_UPPERCASE_CHARACTERS "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
+#define HXT_ALPHANUMERIC_CHARACTERS  \
+    "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
+#define HXT_PRINTABLE_VISIBLE_CHARACTERS  \
+    "!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"
+#define HXT_PRINTABLE_CHARACTERS \
+    "\t\n !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"
+
+// built-in hxtOptionArg
+extern const HXTOptionArgumentConstraints HXT_POSITIVE_RANGE;
+
+extern const HXTOptionArgumentConstraints HXT_ALPHA_LOWERCASE_RANGE;
+extern const HXTOptionArgumentConstraints HXT_ALPHA_UPPERCASE_RANGE;
+extern const HXTOptionArgumentConstraints HXT_ALPHA_RANGE;
+extern const HXTOptionArgumentConstraints HXT_NUMERIC_RANGE;
+extern const HXTOptionArgumentConstraints HXT_ALPHANUMERIC_LOWERCASE_RANGE;
+extern const HXTOptionArgumentConstraints HXT_ALPHANUMERIC_UPPERCASE_RANGE;
+extern const HXTOptionArgumentConstraints HXT_ALPHANUMERIC_RANGE;
+extern const HXTOptionArgumentConstraints HXT_PRINTABLE_VISIBLE_RANGE;
+extern const HXTOptionArgumentConstraints HXT_PRINTABLE_RANGE;
+
+extern const HXTOptionArgumentConstraints HXT_0_1_RANGE;
+extern const HXTOptionArgumentConstraints HXT_0_2_RANGE;
+extern const HXTOptionArgumentConstraints HXT_0_3_RANGE;
+extern const HXTOptionArgumentConstraints HXT_0_4_RANGE;
+extern const HXTOptionArgumentConstraints HXT_0_5_RANGE;
+extern const HXTOptionArgumentConstraints HXT_0_10_RANGE;
+extern const HXTOptionArgumentConstraints HXT_0_20_RANGE;
+extern const HXTOptionArgumentConstraints HXT_0_50_RANGE;
+extern const HXTOptionArgumentConstraints HXT_0_100_RANGE;
+extern const HXTOptionArgumentConstraints HXT_0_1000_RANGE;
+
+
+HXTStatus hxtAddOption(char short_name,
+                       const char* longName,
+                       const char* description,
+                       HXTOptionArgumentType valueType,
+                       const HXTOptionArgumentConstraints* constraints,
+                       void* valuePtr);
+
+/* Trailing options are options without an option name.
+ * the order in which they are added is important !
+ * by default, the last trailing option is optional
+ * you can add an empty trailing option (a trailing option with valuePtr==NULL) at the end to make the last trailing option required
+ * example:
+ *
+ *    const char* input = NULL;
+ *    HXT_CHECK(hxtAddTrailingOption("INPUT_MESH", HXT_EXISTING_FILENAME, NULL, &input));
+ *
+ *    // without the next line, the user would be allowed not giving an input mesh file in the command line
+ *
+ *    HXT_CHECK(hxtAddTrailingOption(NULL, 0, NULL, NULL));
+ *
+ *    // this empty trailing option option ensure that an input mesh file is required
+*/
+static inline HXTStatus hxtAddTrailingOption(
+                      const char* argumentName, // name to use in the usage line ./program [Options] trailingArg1 trailingArg2 ...
+                      HXTOptionArgumentType valueType,
+                      const HXTOptionArgumentConstraints* constraints,
+                      void* valuePtr)
+{
+  HXT_CHECK( hxtAddOption('\0', NULL, argumentName, valueType, constraints, valuePtr) );
+  return HXT_STATUS_OK;
+}
+
+// use the HXT_PARSE_COMMAND_LINE macro instead
+HXTStatus hxtGetOptionHelp(char message[16384],
+                           const char* programName,
+                           const char* programDescription,
+                           const char* additionalInfo);
+
+// use the HXT_PARSE_COMMAND_LINE macro instead
+HXTStatus hxtParseOptions(const int argc, const char* argv[]);
+
+// This macro should be placed in the main, after all options are added !
+#define HXT_PARSE_COMMAND_LINE(argc, argv, programName, programDescription, additionalInfo)   \
+do{                                                                                           \
+  char help[16384];                                                                           \
+  HXTStatus status = hxtGetOptionHelp(help, programName, programDescription, additionalInfo); \
+  if(status==HXT_STATUS_OK)                                                                   \
+    status = hxtParseOptions(argc, argv);                                                     \
+  if(status!=HXT_STATUS_OK && status!=HXT_STATUS_INTERNAL) {                                  \
+    HXT_TRACE(status);                                                                        \
+    puts("\n  ==> Help on Error\n\n                              ---\n");                     \
+  }                                                                                           \
+  if(status!=HXT_STATUS_OK) {                                                                 \
+    printf("%s", help);                                                                       \
+    return status!=HXT_STATUS_INTERNAL?status:0;                                              \
+  }                                                                                           \
+} while(0);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/contrib/hxt/hxt_option.c b/contrib/hxt/hxt_option.c
index d750738c4d7125432a049ba562cd41b4a7b59afb..500ce1d87aa5cf4d4a3092094f7c434d8b44480d 100644
--- a/contrib/hxt/hxt_option.c
+++ b/contrib/hxt/hxt_option.c
@@ -1,244 +1,503 @@
-#include "hxt_option.h"
-#include "hxt_message.h"
-#include "hxt_tools.h"
+#include <stdlib.h>
+#include <stdio.h>
 #include <string.h>
+#include <errno.h>
+#include "hxt_option.h"
+
+static HXTOpt help_option     = {"h", "help", "Display this help message"};
 
-typedef struct {
-  void *data;
-  hxtOptionType type;
-  char *description;
-  char *name;
-  HXTOptionCallback *cb;
-  void *cbdata;
-} hxtOption;
-
-struct HXTOptionListStruct {
-  size_t noptions;
-  hxtOption *options;
-  size_t narguments;
-  hxtOption *arguments;
-  int lastArgumentIsRepeated;
-  char *description;
+/*********************************************************************
+*  program structure                                                 *
+*********************************************************************/
+struct HXTOptProgramStruct {
+	const char* usage_line;
+	const char* start_description;
+	const char* end_description;
+	HXTOpt** opts;
+	size_t opt_length;
+	size_t opt_capacity;
 };
 
-HXTStatus hxtOptionListCreate(HXTOptionList **list, const char *description)
+
+/*********************************************************************
+*  Create a program                                                  *
+*********************************************************************/
+HXTStatus hxtOptProgCreate(HXTOptProgram** program_ptr,
+			   const char* usage_line,
+			   const char* start_description,
+			   const char* end_description)
 {
-  HXT_CHECK(hxtMalloc(list, sizeof(HXTOptionList)));
-  (*list)->noptions = 0;
-  (*list)->options = NULL;
-  (*list)->narguments = 0;
-  (*list)->arguments = NULL;
-  (*list)->description = strdup(description);
-  (*list)->lastArgumentIsRepeated = 0;
-  return HXT_STATUS_OK;
+	HXT_ASSERT(program_ptr!=NULL);
+
+	HXTOptProgram* program;
+	HXT_CHECK( hxtMalloc(&program, sizeof(HXTOptProgram)) );
+	*program_ptr = program;
+
+	program->usage_line = usage_line;
+	program->start_description = start_description;
+	program->end_description = end_description;
+	program->opts = NULL;
+	program->opt_length = 0;
+	program->opt_capacity = 0;
+
+	hxtOptProgAddOption(program, &help_option);
+
+	return HXT_STATUS_OK;
 }
 
-HXTStatus hxtOptionListDelete(HXTOptionList **list)
+
+/*********************************************************************
+*  add an option to a program                                        *
+*********************************************************************/
+HXTStatus hxtOptProgAddOption(HXTOptProgram* program, HXTOpt* opt)
 {
-  for (size_t i = 0; i < (*list)->noptions; ++i) {
-    free((*list)->options[i].name);
-    free((*list)->options[i].description);
-  }
-  for (size_t i = 0; i < (*list)->narguments; ++i) {
-    free((*list)->arguments[i].name);
-    free((*list)->arguments[i].description);
-  }
-  free((*list)->description);
-  if((*list)->options)
-    HXT_CHECK(hxtFree(&(*list)->options));
-  if((*list)->arguments)
-    HXT_CHECK(hxtFree(&(*list)->arguments));
-  HXT_CHECK(hxtFree(list));
-  return HXT_STATUS_OK;
+	HXT_ASSERT( program!=NULL );
+	HXT_ASSERT( opt!=NULL );
+
+	if(program->opt_length >= program->opt_capacity){
+		size_t newSize = program->opt_length?program->opt_length*2:16;
+		HXT_CHECK( hxtRealloc(&program->opts,
+					          sizeof(HXTOpt*) * newSize ) );
+		program->opt_capacity = newSize;
+	}
+
+	program->opts[program->opt_length++] = opt;
+
+	return HXT_STATUS_OK;
+}
+
+
+/*********************************************************************
+*  delete a program                                                  *
+*********************************************************************/
+HXTStatus hxtOptProgDelete(HXTOptProgram* program){
+	HXT_ASSERT( program!=NULL );
+	HXT_CHECK( hxtFree(&program->opts) );
+	HXT_CHECK( hxtFree(&program) );
+
+	return HXT_STATUS_OK;
 }
 
-HXTStatus hxtOptionListAdd(HXTOptionList *list, const char *name, const char *description, hxtOptionType otype, HXTOptionFlag oflag, void *data)
+
+/*********************************************************************
+*  search a long option inside the lists of a program                *
+*********************************************************************/
+static int searchLongOption(HXTOptProgram* program,
+			                const char* string)
 {
-  return hxtOptionListAddCallback(list, name, description, otype, oflag, data, NULL, NULL);
+	for (int i=0; i<program->opt_length; i++) {
+		if(program->opts[i]->longs!=NULL &&
+		   strcmp(program->opts[i]->longs, string)==0) {
+			return i;
+		}
+	}
+
+	return -1;
 }
 
-HXTStatus hxtOptionListAddCallback(HXTOptionList *list, const char *name, const char *description, hxtOptionType otype, HXTOptionFlag oflag, void *data, HXTOptionCallback *cb, void *cbdata)
+
+/*********************************************************************
+*  search a short option inside the lists of a program               *
+*********************************************************************/
+static int searchShortOption(HXTOptProgram* program,
+                             char c)
 {
-  hxtOption *opt;
-  if (oflag & HXT_OPTION_ARGUMENT) {
-    if(list->lastArgumentIsRepeated)
-      return HXT_ERROR_MSG(HXT_STATUS_FAILED, "hxt options : cannot add an argument after a repeated command line argument (trying to add '%s' after '%s').", name, list->arguments[list->narguments-1].name);
-    list->narguments++;
-    HXT_CHECK(hxtRealloc(&list->arguments, list->narguments*sizeof(hxtOption)));
-    opt = &list->arguments[list->narguments-1];
-    if (oflag & HXT_OPTION_REPEATED_ARGUMENT) {
-      if (!cb)
-        return HXT_ERROR_MSG(HXT_STATUS_FAILED, "hxt options : a repeated argument without callback does not make sense");
-      list->lastArgumentIsRepeated = 1;
-    }
-  }
-  else {
-    list->noptions++;
-    HXT_CHECK(hxtRealloc(&list->options, list->noptions*sizeof(hxtOption)));
-    opt = &list->options[list->noptions-1];
-  }
-  opt->name = strdup(name);
-  opt->description = strdup(description);
-  opt->type = otype;
-  opt->data = data;
-  opt->cb = cb;
-  opt->cbdata = cbdata;
-  return HXT_STATUS_OK;
+	for (int i=0; i<program->opt_length; i++) {
+		if(program->opts[i]->shorts!=NULL &&
+		   strchr(program->opts[i]->shorts, c)!=NULL) {
+			return i;
+		}
+	}
+
+	return 0;
 }
 
-static HXTStatus hxtOptionParse(hxtOption *option, const char *val) {
-  switch(option->type) {
-    case HXT_OPTION_INT :
-      if(sscanf(val, "%d", (int*)option->data) == 1)
-        return HXT_STATUS_OK;
-      break;
-    case HXT_OPTION_DOUBLE :
-      if(sscanf(val, "%le", (double*)option->data) == 1)
-        return HXT_STATUS_OK;
-      break;
-    case HXT_OPTION_STRING :
-      {
-        char *sdata = *(char**)option->data;
-        if (sdata)
-          hxtFree(&sdata);
-        HXT_CHECK(hxtMalloc(&sdata, sizeof(char)*(strlen(val)+1)));
-        strcpy(sdata, val);
-        *(char**)option->data = sdata;
-        return HXT_STATUS_OK;
-      }
-      break;
-    default :
-      return HXT_ERROR_MSG(HXT_STATUS_FAILED, "unknown option type for option %s", option->name);
-  };
-  return HXT_ERROR_MSG(HXT_STATUS_FAILED, "cannot parse option \"%s\" with value \"s\"", option->name, val);
+static inline const char* getArgTypeName(ARG_TYPE t){
+	switch(t) {
+		case ARG_INT64 :
+			return "a 64-bit integer";
+		case ARG_INT32 :
+			return "a 32-bit integer";
+		case ARG_UINT32 :
+			return "a 32-bit unsigned integer";
+		case ARG_DOUBLE :
+			return "a float";
+		case ARG_FLOAT :
+			return "a double";
+		case ARG_POSITIVE_FLOAT :
+			return "a positive float";
+		case ARG_POSITIVE_DOUBLE :
+			return "a positive double";
+		case ARG_STRING :
+			return "a string";
+		default:
+			if(t>0)
+				return "an integer";
+			else
+				return "a string";
+	}
 }
 
-static HXTStatus hxtOptionRunCallback(hxtOption *option) {
-  if (!option->cb)
-    return HXT_STATUS_OK;
-  return option->cb(option->data, option->cbdata);
+
+/*********************************************************************
+*  scan parameter and execute callback function for user options     *
+*********************************************************************/
+static HXTStatus doOption(HXTOpt* opt,
+                          const char* arg,
+                          void* state,
+                          char* optName)
+{
+	char* endptr = NULL;
+
+	int64_t integer = INT64_MIN;
+	double real = NAN;
+	const char* string = "\a";
+
+	
+	if(opt->argRequirement&1 ||
+	  (opt->argRequirement==ARG_OPTIONAL && arg!=NULL)) {
+		if(opt->argType>-4)
+			integer = strtol(arg, &endptr, 0);
+		else if(opt->argType>-8)
+			real = strtod(arg, &endptr);
+		else
+			string = arg;
+
+
+		if(errno == ERANGE){
+			return HXT_ERROR_MSG(HXT_STATUS_RANGE_ERROR,
+			       "cannot convert argument \"%s\" of option \"%s\" to %s (range overflow)",
+			       arg, optName, getArgTypeName(opt->argType));
+		}
+		else if (arg == endptr)
+		{
+			return HXT_ERROR_MSG(HXT_STATUS_FORMAT_ERROR,
+			       "cannot convert argument \"%s\" of option \"%s\" to %s (no digit)",
+			       arg, optName, getArgTypeName(opt->argType));
+		}
+		else if(endptr!=NULL && *endptr!='\0') {
+			return HXT_ERROR_MSG(HXT_STATUS_FORMAT_ERROR,
+			    "cannot convert argument \"%s\" of option \"%s\" to %s (trailing unvalid characters)",
+			    arg, optName, getArgTypeName(opt->argType));
+		}
+
+		switch(opt->argType){
+			case ARG_POSITIVE_INT64 :
+				if(integer<0)
+					return HXT_ERROR_MSG(HXT_STATUS_RANGE_ERROR,
+					       "cannot convert argument \"%s\" of option \"%s\" to %s (value was negative)",
+					       arg, optName, getArgTypeName(opt->argType));
+			break;
+			case ARG_INT32:
+			{
+				int32_t i32 = integer;
+				if(i32!=integer)
+					return HXT_ERROR_MSG(HXT_STATUS_RANGE_ERROR,
+					       "cannot convert argument \"%s\" of option \"%s\" to %s ((int32_t)%ld!=%ld)",
+					       arg, optName, getArgTypeName(opt->argType), integer, integer);
+			}
+			break;
+			case ARG_UINT32:
+			{
+				uint32_t u32 = integer;
+				if(u32!=integer)
+					return HXT_ERROR_MSG(HXT_STATUS_RANGE_ERROR,
+					       "cannot convert argument \"%s\" of option \"%s\" to %s ((uint32_t)%ld!=%ld)",
+					       arg, optName, getArgTypeName(opt->argType), integer, integer);
+			}
+			break;
+			case ARG_POSITIVE_DOUBLE:
+				if(real<0.0)
+					return HXT_ERROR_MSG(HXT_STATUS_RANGE_ERROR,
+					       "cannot convert argument \"%s\" of option \"%s\" to %s (value was negative)",
+					       arg, optName, getArgTypeName(opt->argType));
+			break;
+			case ARG_POSITIVE_FLOAT:
+				if(real<0.0)
+					return HXT_ERROR_MSG(HXT_STATUS_RANGE_ERROR,
+					       "cannot convert argument \"%s\" of option \"%s\" to %s (value was negative)",
+					       arg, optName, getArgTypeName(opt->argType));
+			case ARG_FLOAT:
+			{
+				float f32 = real;
+				if(f32!=real)
+					return HXT_ERROR_MSG(HXT_STATUS_RANGE_ERROR,
+					       "cannot convert argument \"%s\" of option \"%s\" to %s ((float)%f!=%f)",
+					       arg, optName, getArgTypeName(opt->argType), real, real);
+			}
+			break;
+			default:
+				if(opt->argType>0 && ( integer > opt->argType || integer<0 ) )
+					return HXT_ERROR_MSG(HXT_STATUS_RANGE_ERROR,
+					       "cannot convert argument \"%s\" of option \"%s\" to an integer between 0 and %d",
+					       arg, optName, opt->argType);
+			break;
+
+		}
+	}
+
+	if(opt->argType>-4)
+		opt->integer = integer;
+	else if(opt->argType>-8)
+		opt->real = real;
+	else
+		opt->string = string;
+
+	if(opt->cb!=NULL){
+		HXTStatus err = opt->cb(opt, state);
+		if(err<0){
+		return HXT_ERROR_MSG(HXT_STATUS_ERROR,
+		       "argument \"%s\" of option \"%s\" triggered an error in callback",
+		       arg, optName);
+		}
+	}
+
+	return HXT_STATUS_OK;
 }
 
-static HXTStatus hxtOptionPrint(hxtOption *option) {
-  switch(option->type) {
-    case HXT_OPTION_INT :
-      printf("%i", *(int*)option->data);
-      return HXT_STATUS_OK;
-    case HXT_OPTION_DOUBLE :
-      printf("%g", *(double*)option->data);
-      return HXT_STATUS_OK;
-    case HXT_OPTION_STRING :
-      printf("\"%s\"", *(char**)option->data);
-      return HXT_STATUS_OK;
-    case HXT_OPTION_FLAG :
-      printf("%s", (*(int*)option->data)?"on":"off");
-      return HXT_STATUS_OK;
-    default :
-      return HXT_ERROR_MSG(HXT_STATUS_FAILED, "unknown option type for option %s", option->name);
-  };
+
+HXTStatus hxtOptCalledWithoutArg(HXTOpt* opt){
+	if(opt->argType>-4) // integer type
+		return (opt->integer==INT64_MIN);
+	else if(opt->argType>-8)
+		return (opt->real==NAN);
+	else
+		return (opt->string[0]=='\a');
 }
 
-static HXTStatus hxtOptionListParseArgvCheck(HXTOptionList *options, int argc, char **argv, const char *description)
+
+/*********************************************************************
+*  Function parsing argv                                             *
+*********************************************************************/
+HXTStatus hxtOptProgParse(HXTOptProgram* program,
+			  const int argc,
+			  char* argv[],
+			  void* state,
+			  int* optind)
 {
-  int p=1;
-  int iarg=0;
-  while (p < argc) {
-    if (strlen(argv[p]) > 2 && strncmp(argv[p],"--",2) == 0) {
-      size_t i;
-      const char *optname;
-      if (strlen(argv[p]) > 5 && strncmp("--no-",argv[p],5) == 0) {
-        optname = argv[p]+5;
-        for (i = 0; i < options->noptions; ++i) {
-          hxtOption *opt = &options->options[i];
-          if(!strcmp(optname, opt->name) && opt->type == HXT_OPTION_FLAG) {
-            *(int*)opt->data = 0;
-            HXT_CHECK(hxtOptionRunCallback(opt));
-            break;
-          }
-        }
-      }
-      else {
-        optname = argv[p]+2;
-        for (i = 0; i < options->noptions; ++i) {
-          hxtOption *opt = &options->options[i];
-          if(!strcmp(optname, opt->name)) {
-            if (opt->type == HXT_OPTION_FLAG)
-              *(int*)opt->data = 1;
-            else{
-              HXT_CHECK(hxtOptionParse(opt, argv[p+1]));
-              HXT_CHECK(hxtOptionRunCallback(opt));
-              p++;
-            }
-            break;
-          }
-        }
-      }
-      if (i == options->noptions)
-        return HXT_ERROR_MSG(HXT_STATUS_FAILED, "unknown command line option \"%s\"", optname);
-    }
-    else {
-      if ((size_t)iarg == options->narguments && options->lastArgumentIsRepeated)
-        iarg--;
-      if ((size_t)iarg >= options->narguments)
-        return HXT_ERROR_MSG(HXT_STATUS_FAILED, "too many command line arguments (\"%s\")", argv[p]);
-      hxtOption *opt = &options->arguments[iarg];
-      HXT_CHECK(hxtOptionParse(opt, argv[p]));
-      HXT_CHECK(hxtOptionRunCallback(opt));
-        iarg++;
-    }
-    p++;
-  }
-  if ((size_t)iarg != options->narguments)
-    return HXT_ERROR_MSG(HXT_STATUS_FAILED, "missing command line arguments (\"%s\")", options->arguments[iarg].name);
-  return HXT_STATUS_OK;
+	HXT_ASSERT(program!=NULL);
+	HXT_ASSERT(optind!=NULL);
+
+	int numarg = argc;
+	*optind = 1;
+	for (int i=1; i<numarg; i++) {
+		char *arg = NULL;
+		HXTOpt* opt = NULL;
+
+		if(argv[i][0]!='-' || (argv[i][0]=='-' && argv[i][1]=='\0')){
+			/* not opt, shift everything before this string */
+			arg = argv[i];
+			for (int j=i; j<argc-1; j++) {
+				argv[j] = argv[j+1];
+			}
+			argv[argc-1] = arg;
+			numarg--;
+			i--;
+		}
+		else if(argv[i][1]=='-') { /* long opt */
+			if(argv[i][2]=='\0') /* -- terminate argument parsing */
+				return HXT_STATUS_OK;
+
+			char* equalSign = strchr(argv[i]+2,'=');
+			
+			if(equalSign!=NULL){
+				*equalSign = '\0';
+				arg = equalSign + 1;
+			}
+
+			int num = searchLongOption(program, argv[i]+2);
+			if(num<0){
+				return HXT_ERROR_MSG(HXT_STATUS_FORMAT_ERROR,
+				       "option \"%s\" not found", argv[i]);
+			}
+			opt = program->opts[num];
+
+			if(equalSign!=NULL){
+				*equalSign = '=';
+				if(opt->argRequirement==ARG_NONE){
+					return HXT_ERROR_MSG(HXT_STATUS_FORMAT_ERROR,
+					       "option \"%s\" takes no argument", argv[i]);
+				}
+			}
+			else{
+				if(num==0) { // it's the help option
+					*optind = -1;
+					return HXT_STATUS_OK;
+				}
+				if(opt->argRequirement&1 && numarg<=i+1){
+					return HXT_ERROR_MSG(HXT_STATUS_FORMAT_ERROR,
+					       "options \"%s\" requires an argument", argv[i]);
+				}
+				else if(numarg>i+1 && (opt->argRequirement&1 || argv[i+1][0]!='-')) {
+					arg = argv[i+1];
+					i++;
+				}
+			}
+
+			HXT_CHECK( doOption(opt, arg, state, argv[i]) );
+		}
+		else{ /* short option */
+			int cond = argv[i][1];
+			for(int j=1; cond; j++){
+				int num = searchShortOption(program, argv[i][j]);
+				if(num<0){
+					return HXT_ERROR_MSG(HXT_STATUS_FORMAT_ERROR,
+					       "option \'%c\' in \"%s\" not found", argv[i][j], argv[i]);
+				}
+
+				char optName[] = "- ";
+				optName[1] = argv[i][j];
+				cond = argv[i][j+1];
+
+				if(num==0){
+					*optind = -1;
+					return HXT_STATUS_OK;
+				}
+
+				opt = program->opts[num];
+
+				if(opt->argRequirement!=ARG_NONE){
+					if(cond){
+						arg = argv[i] + j+1;
+						cond = 0; // stop the loop
+					}
+					else if(opt->argRequirement&1 && numarg<=i+1){
+						return HXT_ERROR_MSG(HXT_STATUS_FORMAT_ERROR,
+						       "options \'%c\' in \"%s\" requires an argument", argv[i][j], argv[i]);
+					}
+					else if(numarg>i+1 && (opt->argRequirement==ARG_REQUIRED || argv[i+1][0]!='-')) {
+						arg = argv[i+1];
+						i++;
+					}
+				}
+
+				HXT_CHECK( doOption(opt, arg, state, optName) );
+			}
+		}
+		*optind = i+1;
+	}
+
+	for (int i=0; i<program->opt_length; i++) {
+		HXTOpt* opt = program->opts[i];
+		if(opt->argRequirement==ARG_NON_NULL_REQUIRED &&
+			 ((opt->argType>-4 && opt->integer==0) ||
+			  (opt->argType>-8 && opt->real==0.0)  ||
+			  (opt->argType<=-8 && opt->string==NULL)
+			 )
+			) {
+			if(opt->longs!=NULL)
+				return HXT_ERROR_MSG(HXT_STATUS_ERROR,
+				       "mandatory option \"--%s\" must have a non-null argument",
+				       opt->longs);
+			else
+				return HXT_ERROR_MSG(HXT_STATUS_ERROR,
+				       "mandatory option \'-%c\' must have a non-null argument",
+				       opt->shorts[0]);
+		}
+	}
+
+	return HXT_STATUS_OK;
 }
 
-HXTStatus hxtOptionListParseArgv(HXTOptionList *options, int argc, char **argv, const char *description)
+
+#define MY_SPRINTF(...) *offset+=snprintf(text+ *offset, 16384- *offset, ## __VA_ARGS__ )
+
+static void printOptionLine(HXTOpt* opt, char text[16384], int* offset)
 {
-  HXTStatus err = hxtOptionListParseArgvCheck(options, argc, argv, description);
-  if (err){
-    printf("\n");
-    HXT_CHECK(hxtOptionListPrintUsage(options, argv[0]));
-    printf("\n");
-    return HXT_STATUS_FAILED;
-  }
-  return HXT_STATUS_OK;
+	int char_max = 30;
+	int oldOffset = *offset;
+	MY_SPRINTF("  ");
+
+	int long_exist = opt->longs!=NULL && opt->longs[0]!='\0';
+
+	// short params
+	if(opt->shorts!=NULL && opt->shorts[0]!='\0'){
+		int len = strlen(opt->shorts);
+
+		for (int i=0; i<len-1; i++){
+			MY_SPRINTF("-%c, ", opt->shorts[i]);
+		}
+		
+		if(long_exist){
+			MY_SPRINTF("-%c, ", opt->shorts[len-1]);
+		}
+		else{
+			MY_SPRINTF("-%c", opt->shorts[len-1]);
+		}
+	}
+
+	if(long_exist){
+		if(opt->argRequirement==ARG_NONE)
+			MY_SPRINTF("--%s", opt->longs);
+		else if(opt->argRequirement==ARG_OPTIONAL)
+			MY_SPRINTF("--%s[=%s]", opt->longs, opt->argName);
+		else
+			MY_SPRINTF("--%s=%s", opt->longs, opt->argName);
+
+		
+	}
+
+	int char_count = *offset - oldOffset;
+	if(char_count<char_max)
+		MY_SPRINTF("%*c", char_max - char_count, ' ');
+	else
+		MY_SPRINTF("\n%*c", char_max, ' ');
+
+	// description
+	{
+		int i,k;
+		for (i=0,k=0; opt->description[i+1]!='\0'; i++, k++) {
+			if(opt->description[i]=='\n'){
+				if(opt->description[i+1]=='\n' || opt->description[i+1]=='\0')
+					MY_SPRINTF("%.*s\n", k, opt->description+i-k);
+				else
+					MY_SPRINTF("%.*s\n%*c", k, opt->description+i-k, char_max+2, ' ');
+				k=-1;
+			}
+		}
+		MY_SPRINTF("%s", opt->description+i-k);
+	}
+
+	// default argument
+	if(opt->argRequirement!=ARG_NONE &&
+		(opt->argType!=ARG_STRING || opt->string!=NULL))
+	{
+		MY_SPRINTF("\n%*cdefault: %s=", char_max+1, ' ', opt->argName);
+		if(opt->argType>-4) // integer type
+			MY_SPRINTF("%ld",opt->integer);
+		else if(opt->argType>-8)
+			MY_SPRINTF("%g",opt->real);
+		else
+			MY_SPRINTF("%s",opt->string);
+	}
+
+	MY_SPRINTF("\n");
 }
 
-HXTStatus hxtOptionListPrintUsage(const HXTOptionList *list, const char *exename)
+
+
+HXTStatus hxtOptProgGetHelp(HXTOptProgram* program, char text[16384])
 {
-  printf("Usage : %s", exename);
-  if (list->noptions != 0)
-    printf(" [options]");
-  for (size_t i = 0; i < list->narguments; ++i) {
-    printf(" %s", list->arguments[i].name);
-  }
-  if (list->lastArgumentIsRepeated)
-    printf(" [%s2 ...]", list->arguments[list->narguments-1].name);
-  printf("\n");
-  printf("%s\n", list->description);
-  char opstr[256];
-  const char* hxtOptionTypeName[] = {"double", "int", "string"};
-  if (list->narguments != 0) {
-    printf("\nmandatory arguments :\n");
-    for(size_t i = 0; i < list->narguments; ++i) {
-      hxtOption *opt = &list->arguments[i];
-      sprintf(opstr, "%s(%s)", opt->name, hxtOptionTypeName[opt->type]);
-      printf("    %-20s %s\n", opstr, opt->description);
-    }
-  }
-  if (list->noptions != 0) {
-    printf("\noptions :\n");
-    for(size_t i = 0; i < list->noptions; ++i) {
-      hxtOption *opt = &list->options[i];
-      if (opt->type == HXT_OPTION_FLAG)
-        sprintf(opstr, "[no-]%s", opt->name);
-      else
-        sprintf(opstr, "%s %s", opt->name, hxtOptionTypeName[opt->type]);
-      printf("  --%-20s %s [",opstr,  opt->description);
-      HXT_CHECK(hxtOptionPrint(opt));
-      printf("]\n");
-    }
-  }
-  return HXT_STATUS_OK;
-}
+	int offsetval = 0;
+	int* offset = &offsetval;
+	if(program->usage_line)
+		MY_SPRINTF("%s\n\n", program->usage_line);
+
+	if(program->start_description)
+		MY_SPRINTF("%s\n\n", program->start_description);
+
+	int oldOffset = *offset;
+	MY_SPRINTF("Application Options:\n");
+	int char_printed = *offset - oldOffset;
+
+	MY_SPRINTF("%.*s\n", char_printed>64?63:char_printed-1,
+   "---------------------------------------------------------------");
+
+	for (int i=0; i<program->opt_length; i++) {
+		printOptionLine(program->opts[i], text, offset);
+	}
+
+	if(program->end_description)
+		MY_SPRINTF("%s\n", program->end_description);
+	return HXT_STATUS_OK;
+}
\ No newline at end of file
diff --git a/contrib/hxt/hxt_option.h b/contrib/hxt/hxt_option.h
index d373c130b79b911e0c3a25d02e70214321a1ae19..29dab9f9728c0c2d95e9d3ef525b34afe98e47ff 100644
--- a/contrib/hxt/hxt_option.h
+++ b/contrib/hxt/hxt_option.h
@@ -1,64 +1,181 @@
-#ifndef HXT_OPTION_H
-#define HXT_OPTION_H
+#ifndef __HXT_OPT_H__
+#define __HXT_OPT_H__
 
-#include "hxt_api.h"
-#include "hxt_message.h"
+#include "hxt_tools.h"
 
+#ifdef __cplusplus
+extern "C" {
+#endif
 
-/* 
- * usage example:
- *
- 
-#include "hxt_option.h"
+typedef struct HXTOptListStruct HXTOptList;
+typedef struct HXTOptProgramStruct HXTOptProgram;
 
-HXTStatus opencb(void *val, void *data) {
-  //open_file(*(char**)val);
-  return HXT_STATUS_OK;
-}
+/************** new option parsing method ******************
+
+  ## the program is detailed into a HXTOptProgram.
+  HXTOptProgram* program;
+  HXT_CHECK( hxtOptProgCreate(&program, 
+                "Usage: mon_programme [options]",                      ## a usage line for Help
+                "mon_programme is a programme that doesn't do much",   ## a description of the program
+                "Example:\n    mon_programme --coucou=David"           ## Examples, contact information etc.
+           ) );
+
+  ## then, we will add each possible options to this program
+  ## each options is detailed into a HXTOpt:
+  HXTOpt myOpt = {
+         "cC",     ## short option names (each character is a short name => '-c' and '-C' refer to this option)
+         "coucou", ## long option name   (a unique long name             => '--coucou' refers to this option)
+
+         "Prints coucou c to the screen\n" ## a description of this option
+         "Not usefull at all but funny",   ## can span multiple lines (no comma on the previous line)
+
+         coucouCallBack, ## a callback function called everytime the program is called
+                         ## the prototype of 'coucouCallBack' must be:
+                         ##     HXTStatus coucouCallBack(HXTOpt* opt, void* state);
+                         ## the option given will be 'myOpt'.
+                         ## the state is a pointer that we give to the parsing function
+
+         "YOUR_NAME",    ## the name of the argument to show in Help
+                         ## here Help will show: 
+                         ## -c, -C, --coucou=YOUR_NAME   Prints coucou YOUR_NAME to the screen
+
+         ARG_REQUIRED,   ## one of ARG_NONE, ARG_REQUIRED, ARG_OPTIONAL and ARG_NON_NULL_REQUIRED
+                         ## specify if an argument is required (see ARG_REQUIREMENT section)
+
+         ARG_STRING,     ## the type of the argument as described below (ARG_TYPE section)
+         
+         {.string="Joe"} ## a default value for the argument (uneeded here as the argument is required...)
+     };
+
+  ##then we add the option to the program:
+  HXT_CHECK( hxtOptProgAddOption(program, myOpt) );
+
+  ## Once all options are added, we can parse the argument.
+  ## The function will automatically return the status and print Help on errors
+
+  void* state = NULL;  ## this pointer will be given to each callback functions
+  int optind=0;        ## this integer will contain the number of string processed in argv
+  HXT_OPT_MAIN_PARSE(program, argc, argv, NULL, &optind );
+
+  ## you can now use the argument provided by the user safely
+  printf(" Coucou %s !!!", myOpt.string);
+
+  ## and delete the program structure
+  HXT_CHECK( hxtOptProgDelete(program) );
 
-int main(int argc, char **argv) {
-
-  char *opts = NULL;
-  char *opti = NULL;
-  double optd = 3.;
-  int optb = 0;
-
-  HXTOptionList *list;
-  HXT_CHECK(hxtOptionListCreate(&list, "Solve the laplace equation."));
-  HXT_CHECK(hxtOptionListAddCallback(list, "filename", "file to open", HXT_OPTION_STRING, HXT_OPTION_REPEATED_ARGUMENT, &opti, opencb, NULL));
-  HXT_CHECK(hxtOptionListAdd(list, "optd", "description of double option", HXT_OPTION_DOUBLE, 0, &optd));
-  HXT_CHECK(hxtOptionListAdd(list, "opts", "description of string option", HXT_OPTION_STRING, 0, &opts));
-  HXT_CHECK(hxtOptionListAdd(list, "optb", "description of flag option", HXT_OPTION_FLAG, 0, &optb));
-  HXT_CHECK(hxtOptionListParseArgv(list, argc, argv, "laplace"));
-  hxtOptionListPrintUsage(list, argv[0]);
-  HXT_CHECK(hxtOptionListDelete(&list));
-  ...
-  }
 */
 
-#ifdef __cplusplus
-extern "C" {
-#endif
 
-typedef struct HXTOptionListStruct HXTOptionList;
+/****************************************************************************************
+ *  ARG_REQUIREMENT : specify if an argument is required to follow the option
+ ***************************************************************************************/
+typedef enum {
+  ARG_NONE=0,     /* no argument, the union value is incremented at each call */
+  ARG_REQUIRED=1, /* argument required, the union value is the value of the argument */
+  ARG_OPTIONAL=2, /* optional argument, the union value is:
+                      - the value of the argument if there is an argument
+                      - INT64_MIN for integers type
+                      - NAN for floating point type if there was no argument
+                      - "\a" for string types
+                      */
+  ARG_NON_NULL_REQUIRED=3  /* argument is required and the option should be non-null
+                              at end of parsing */
+} ARG_REQUIREMENT;
+
 
-typedef enum {HXT_OPTION_DEFAULT=0, HXT_OPTION_ARGUMENT=1, HXT_OPTION_REPEATED_ARGUMENT=2} HXTOptionFlag;
-typedef enum {HXT_OPTION_DOUBLE, HXT_OPTION_INT, HXT_OPTION_STRING, HXT_OPTION_FLAG} hxtOptionType;
+/****************************************************************************************
+ *  ARG_TYPE: the accepted values of the possible argument
+ * (use whatever you want if argRequirement==ARG_NONE)
+ ***************************************************************************************/
+typedef enum {
+  // a positive value N means an integer between 0 and N included
+  ARG_POSITIVE_INT32=INT32_MAX,
 
-typedef HXTStatus HXTOptionCallback(void *valuep, void *data);
+  /* Integer value types (stored in opt.integer) */
+  ARG_INT64=0,    // an integer, whatever fits in a int64_t
+  ARG_POSITIVE_INT64=-1,
+  ARG_INT32=-2, // the value is checked to fit in a int32_t
+  ARG_UINT32=-3,// the value is checked to fit in a uint32_t
 
-HXTStatus hxtOptionListCreate(HXTOptionList **list, const char *description);
-HXTStatus hxtOptionListDelete(HXTOptionList **list);
+  /* Floating value types (stored in opt.real)   */
+  ARG_DOUBLE=-4,// a double value, whatever fits in a double
+  ARG_FLOAT=-5, // the value is checked to fit in a float
+  ARG_POSITIVE_FLOAT=-6, // the value is checked to fit in a float and be positive
+  ARG_POSITIVE_DOUBLE=-7,// the value is checked to fit in a double and be positive
 
-HXTStatus hxtOptionListAdd(HXTOptionList *list, const char *name, const char *description, hxtOptionType otype, HXTOptionFlag oflag, void *data);
-HXTStatus hxtOptionListAddCallback(HXTOptionList *list, const char *name, const char *description, hxtOptionType otype, HXTOptionFlag oflag, void *data, HXTOptionCallback *cb, void *cbdata);
+   /* String value type (stored in opt.string)    */
+  ARG_STRING=-8
+} ARG_TYPE;
 
-HXTStatus hxtOptionListParseArgv(HXTOptionList *options, int argc, char **argv, const char *description);
-HXTStatus hxtOptionListPrintUsage(const HXTOptionList *options, const char *exename);
+
+
+typedef struct HXTOptStruct{
+  const char* const shorts;
+  const char* const longs;
+  const char* const description;
+  HXTStatus (*const cb)(struct HXTOptStruct* arg, void* state);
+  const char* const argName;
+  const ARG_REQUIREMENT argRequirement;
+  const int argType;
+  
+  int64_t     integer;
+  double      real;
+  const char* string;
+}HXTOpt;
+
+
+
+
+
+
+HXTStatus hxtOptProgCreate(HXTOptProgram** program_ptr,
+                           const char* usage_line,
+                           const char* start_description,
+                           const char* end_description);
+
+
+HXTStatus hxtOptProgAddOption(HXTOptProgram* program,
+                              HXTOpt* opt);
+
+
+HXTStatus hxtOptProgParse(HXTOptProgram* program,
+                          const int argc, char* argv[],
+                          void* state,
+                          int* optind);
+
+
+#define HXT_OPT_MAIN_PARSE(program, argc, argv, state, optind_ptr)               \
+  do {                                                                           \
+    char help[16384];                                                            \
+    hxtOptProgGetHelp(program, help);                                            \
+    HXTStatus _status = hxtOptProgParse(program, argc, argv, NULL, optind_ptr ); \
+    if(_status!=HXT_STATUS_OK){                                                  \
+      HXT_TRACE(_status);                                                        \
+      puts("\n  ==> Help on Error\n\n                              ---\n\n");    \
+      *optind_ptr=-1;                                                            \
+    }                                                                            \
+    if(*optind_ptr==-1) {                                                        \
+      printf("%s", help);                                                        \
+      HXT_CHECK( hxtOptProgDelete(program) );                                    \
+      return _status;                                                            \
+    }                                                                            \
+  }while(0)
+
+HXTStatus hxtOptCalledWithoutArg(HXTOpt* opt);
+
+HXTStatus hxtOptProgGetHelp(HXTOptProgram* program, char text[16384]);
+
+HXTStatus hxtOptProgDelete(HXTOptProgram* program);
+
+
+static inline HXTStatus hxtOptProgAddOptionArray(HXTOptProgram* program,
+                                                 HXTOpt* opt, int n) {
+  for (int i=0; i<n; i++) { HXT_CHECK( hxtOptProgAddOption(program, &opt[i]) ); }
+  return HXT_STATUS_OK;
+}
 
 #ifdef __cplusplus
 }
 #endif
 
-
 #endif
diff --git a/contrib/hxt/hxt_parametrization.c b/contrib/hxt/hxt_parametrization.c
index fe2864e1cdfabfe43c7b4859fa2cffcb8fc0c6b7..23f1486e74d7b6d7a879898a8d486ce6fa4f059c 100644
--- a/contrib/hxt/hxt_parametrization.c
+++ b/contrib/hxt/hxt_parametrization.c
@@ -167,7 +167,7 @@ static HXTStatus hxtLongestEdgeBisection(HXTEdges *edges,int nrefinements)
             break;
           }
         }
-        do{	  
+        do{       
           HXT_CHECK(hxtLongestEdge(e,ti,&ei,ej));
           tj = e->edg2tri[2*ei+0]==ti ? e->edg2tri[2*ei+1] : e->edg2tri[2*ei+0];
           HXT_CHECK(hxtLongestEdge(e,tj,&ej,ei));
@@ -275,7 +275,7 @@ static HXTStatus hxtLongestEdgeBisection(HXTEdges *edges,int nrefinements)
               t_[1] = ttj;    
 
           }
-        }//end for	
+        }//end for      
         e->numEdges +=3;
         if(e->numEdges > maxEdg){
           maxEdg *= 2;
@@ -285,7 +285,7 @@ static HXTStatus hxtLongestEdgeBisection(HXTEdges *edges,int nrefinements)
         }
         e->node[2*ei+1] = m->vertices.num-1;
         e->edg2tri[2*ei+0] = ti;
-        e->edg2tri[2*ei+1] = ttj;	
+        e->edg2tri[2*ei+1] = ttj;       
         e->node[2*e0+0] = m->vertices.num-1;
         e->node[2*e0+1] = nj;
         e->edg2tri[2*e0+0] = tti;
@@ -346,12 +346,12 @@ static HXTStatus hxtPartitioning(HXTEdges *edges,int *part, int nPartitions,HXTV
     uint64_t ce = 0;
     for(uint64_t ie=0; ie<mesh->triangles.num; ie++){      
       if(part[ie]==p){
-	for(int jv=0; jv<3; jv++){
-	  uint32_t current = mesh->triangles.node[3*ie+jv];
-	  if(flagVertices[current] == (uint32_t)-1)
-	    flagVertices[current] = cv++;	  
-	}
-	ce++;
+        for(int jv=0; jv<3; jv++){
+          uint32_t current = mesh->triangles.node[3*ie+jv];
+          if(flagVertices[current] == (uint32_t)-1)
+            flagVertices[current] = cv++;         
+        }
+        ce++;
       }
     }    
     /*
@@ -365,12 +365,12 @@ static HXTStatus hxtPartitioning(HXTEdges *edges,int *part, int nPartitions,HXTV
       uint32_t current = flagVertices[iv];
       if(current==(uint32_t)-1);
       else{
-	msh->vertices.coord[4*current+0] = mesh->vertices.coord[4*iv+0];
-	msh->vertices.coord[4*current+1] = mesh->vertices.coord[4*iv+1];
-	msh->vertices.coord[4*current+2] = mesh->vertices.coord[4*iv+2];
-	msh->vertices.coord[4*current+3] = mesh->vertices.coord[4*iv+3];	
+        msh->vertices.coord[4*current+0] = mesh->vertices.coord[4*iv+0];
+        msh->vertices.coord[4*current+1] = mesh->vertices.coord[4*iv+1];
+        msh->vertices.coord[4*current+2] = mesh->vertices.coord[4*iv+2];
+        msh->vertices.coord[4*current+3] = mesh->vertices.coord[4*iv+3];        
       }
-    }	
+    }   
 
     msh->triangles.num = ce;
     //<
@@ -382,25 +382,18 @@ static HXTStatus hxtPartitioning(HXTEdges *edges,int *part, int nPartitions,HXTV
     ce = 0;
     for(uint64_t ie=0; ie<mesh->triangles.num; ie++){
       if(part[ie]==p){
-	global[ce] = edges->global[ie];
-	msh->triangles.colors[ce] = mesh->triangles.colors[ie];
-	for(int jv=0; jv<3; jv++){
-	  uint32_t current = flagVertices[mesh->triangles.node[3*ie+jv]];
-	  msh->triangles.node[3*ce+jv] = current;
-	}
-	ce++;
+        global[ce] = edges->global[ie];
+        msh->triangles.colors[ce] = mesh->triangles.colors[ie];
+        for(int jv=0; jv<3; jv++){
+          uint32_t current = flagVertices[mesh->triangles.node[3*ie+jv]];
+          msh->triangles.node[3*ce+jv] = current;
+        }
+        ce++;
       }
     }
-    /*
-    char name[16];
-    sprintf(name,"havealook%d.msh",p);
-    hxtMeshWriteGmsh(msh,name);
-    */
     HXTEdges* edg = NULL;
     HXT_CHECK(hxtEdgesCreate(msh,&edg));
-    //<    
     edg->global = global;
-    //>
     HXT_CHECK(hxtVectorPushBack(partitions,edg));
   }
 
@@ -439,63 +432,59 @@ static HXTStatus hxtCheckConnectivity(HXTEdges *e,HXTVector *partitions)
       uint64_t current = queue[iq];
       uint32_t *ed = &e->tri2edg[3*current];
       for(int j=0; j<3; j++){
-	if (e->edg2tri[2*ed[j]+1] == (uint64_t) -1)
-	  continue;
-	uint64_t next = e->edg2tri[2*ed[j]] == current ? e->edg2tri[2*ed[j]+1] : e->edg2tri[2*ed[j]];
-	if (flag[next] == -1){
-	  queue[last]= next;
-	  last++;
-	  idx[iq+1]++;
-	  flag[next] = (int) count;
-	}	
+        if (e->edg2tri[2*ed[j]+1] == (uint64_t) -1)
+          continue;
+        uint64_t next = e->edg2tri[2*ed[j]] == current ? e->edg2tri[2*ed[j]+1] : e->edg2tri[2*ed[j]];
+        if (flag[next] == -1){
+          queue[last]= next;
+          last++;
+          idx[iq+1]++;
+          flag[next] = (int) count;
+        }       
       }      
     }
     count++;
     goOn = 0;
     for(uint64_t i=0; i<m->triangles.num; i++)
       if (flag[i]== -1){
-	goOn=1;
-	iq=last;
-	queue[last] = i;
-	last++;
-	flag[i] = count;	
-	break;
+        goOn=1;
+        iq=last;
+        queue[last] = i;
+        last++;
+        flag[i] = count;        
+        break;
       }
   }
 
-  //if(partitions!=NULL)
-    printf("HXT_INFO \t There is/are %d connected part(s).\n",count);
+  printf("HXT_INFO \t There is/are %d connected part(s).\n",count);
 
   for(uint64_t i=0; i<m->triangles.num; i++){
     uint32_t *refVert = &m->triangles.node[3*queue[i]];
-    //printf("REFERENCE triangle %lu (%u %u %u)\n",queue[i],refVert[0],refVert[1],refVert[2]);
     for(uint64_t j=idx[i]+flag[queue[i]]; j<idx[i+1]+flag[queue[i]]; j++){
       
       int cond = 0; 
       uint32_t *checkVert = &m->triangles.node[3*queue[j]];
-      //printf("\t check tri %lu (%u %u %u)\n",queue[j],checkVert[0],checkVert[1],checkVert[2]);
       for(int ii=0; ii<3; ii++){
-	for(int jj=0; jj<3; jj++){
-	  //printf("%u->%u vs %u<-%u\n",refVert[ii],refVert[(ii+1)%3],checkVert[(jj+1)%3],checkVert[jj]);
-	  if(refVert[ii]==checkVert[(jj+1)%3] && refVert[(ii+1)%3]==checkVert[jj]){
-	    cond = 1;
-	    break;
-	  }
-	}
-	if(cond==1)
-	  break;
+        for(int jj=0; jj<3; jj++){
+          if(refVert[ii]==checkVert[(jj+1)%3] && refVert[(ii+1)%3]==checkVert[jj]){
+            cond = 1;
+            break;
+          }
+        }
+        if(cond==1)
+          break;
       }//end for ii
-      if(cond==0){	
-	printf("Unconsistent orientation \t %lu (%u %u %u) ; (%u %u)-(%u %u)-(%u %u) [%d] ~ %lu (%u %u %u) ; (%u %u)-(%u %u)-(%u %u) [%d].\n",queue[i],refVert[0],refVert[1],refVert[2],e->node[2*e->tri2edg[3*queue[i]+0]+0],e->node[2*e->tri2edg[3*queue[i]+0]+1],e->node[2*e->tri2edg[3*queue[i]+1]+0],e->node[2*e->tri2edg[3*queue[i]+1]+1],e->node[2*e->tri2edg[3*queue[i]+2]+0],e->node[2*e->tri2edg[3*queue[i]+2]+1],flag[queue[i]],queue[j],checkVert[0],checkVert[1],checkVert[2],e->node[2*e->tri2edg[3*queue[j]+0]+0],e->node[2*e->tri2edg[3*queue[j]+0]+1],e->node[2*e->tri2edg[3*queue[j]+1]+0],e->node[2*e->tri2edg[3*queue[j]+1]+1],e->node[2*e->tri2edg[3*queue[j]+2]+0],e->node[2*e->tri2edg[3*queue[j]+2]+1],flag[queue[j]]);
-	uint64_t temp = m->triangles.node[3*queue[j]+0];
-	m->triangles.node[3*queue[j]+0] = m->triangles.node[3*queue[j]+1];
-	m->triangles.node[3*queue[j]+1] = temp;
-	uint32_t te = e->tri2edg[3*queue[j]+1];
-	e->tri2edg[3*queue[j]+1] = e->tri2edg[3*queue[j]+2];
-	e->tri2edg[3*queue[j]+2] = te;
-
-	printf("Consistent (?) orientation \t %lu (%u %u %u) ; (%u %u)-(%u %u)-(%u %u) [%d] ~ %lu (%u %u %u) ; (%u %u)-(%u %u)-(%u %u) [%d].\n",queue[i],refVert[0],refVert[1],refVert[2],e->node[2*e->tri2edg[3*queue[i]+0]+0],e->node[2*e->tri2edg[3*queue[i]+0]+1],e->node[2*e->tri2edg[3*queue[i]+1]+0],e->node[2*e->tri2edg[3*queue[i]+1]+1],e->node[2*e->tri2edg[3*queue[i]+2]+0],e->node[2*e->tri2edg[3*queue[i]+2]+1],flag[queue[i]],queue[j],checkVert[0],checkVert[1],checkVert[2],e->node[2*e->tri2edg[3*queue[j]+0]+0],e->node[2*e->tri2edg[3*queue[j]+0]+1],e->node[2*e->tri2edg[3*queue[j]+1]+0],e->node[2*e->tri2edg[3*queue[j]+1]+1],e->node[2*e->tri2edg[3*queue[j]+2]+0],e->node[2*e->tri2edg[3*queue[j]+2]+1],flag[queue[j]]);
-	
+      if(cond==0){      
+        printf("Unconsistent orientation \t %lu (%u %u %u) ; (%u %u)-(%u %u)-(%u %u) [%d] ~ %lu (%u %u %u) ; (%u %u)-(%u %u)-(%u %u) [%d].\n",queue[i],refVert[0],refVert[1],refVert[2],e->node[2*e->tri2edg[3*queue[i]+0]+0],e->node[2*e->tri2edg[3*queue[i]+0]+1],e->node[2*e->tri2edg[3*queue[i]+1]+0],e->node[2*e->tri2edg[3*queue[i]+1]+1],e->node[2*e->tri2edg[3*queue[i]+2]+0],e->node[2*e->tri2edg[3*queue[i]+2]+1],flag[queue[i]],queue[j],checkVert[0],checkVert[1],checkVert[2],e->node[2*e->tri2edg[3*queue[j]+0]+0],e->node[2*e->tri2edg[3*queue[j]+0]+1],e->node[2*e->tri2edg[3*queue[j]+1]+0],e->node[2*e->tri2edg[3*queue[j]+1]+1],e->node[2*e->tri2edg[3*queue[j]+2]+0],e->node[2*e->tri2edg[3*queue[j]+2]+1],flag[queue[j]]);
+        uint64_t temp = m->triangles.node[3*queue[j]+0];
+        m->triangles.node[3*queue[j]+0] = m->triangles.node[3*queue[j]+1];
+        m->triangles.node[3*queue[j]+1] = temp;
+        uint32_t te = e->tri2edg[3*queue[j]+1];
+        e->tri2edg[3*queue[j]+1] = e->tri2edg[3*queue[j]+2];
+        e->tri2edg[3*queue[j]+2] = te;
+
+        printf("Consistent (?) orientation \t %lu (%u %u %u) ; (%u %u)-(%u %u)-(%u %u) [%d] ~ %lu (%u %u %u) ; (%u %u)-(%u %u)-(%u %u) [%d].\n",queue[i],refVert[0],refVert[1],refVert[2],e->node[2*e->tri2edg[3*queue[i]+0]+0],e->node[2*e->tri2edg[3*queue[i]+0]+1],e->node[2*e->tri2edg[3*queue[i]+1]+0],e->node[2*e->tri2edg[3*queue[i]+1]+1],e->node[2*e->tri2edg[3*queue[i]+2]+0],e->node[2*e->tri2edg[3*queue[i]+2]+1],flag[queue[i]],queue[j],checkVert[0],checkVert[1],checkVert[2],e->node[2*e->tri2edg[3*queue[j]+0]+0],e->node[2*e->tri2edg[3*queue[j]+0]+1],e->node[2*e->tri2edg[3*queue[j]+1]+0],e->node[2*e->tri2edg[3*queue[j]+1]+1],e->node[2*e->tri2edg[3*queue[j]+2]+0],e->node[2*e->tri2edg[3*queue[j]+2]+1],flag[queue[j]]);
+        
       }//end if cond==0
     }//end for j 
   }//end for i
@@ -537,11 +526,11 @@ static HXTStatus hxtSplitEdges(HXTEdges *edges,int nPartitions,HXTVector *partit
     for(int j=0; j<3; j++){      
       uint64_t *local = &edg2tri[2*current[j]];
       if(local[1]==(uint64_t)-1)
-	continue;
-      else{		
-	nbh[idx[i]+temp] = i == (int) local[0] ? (int) local[1] : (int) local[0];
-	weights[idx[i]+temp] = (int) (hxtEdgesLength(edges,current[j])+1);
-	temp++;
+        continue;
+      else{             
+        nbh[idx[i]+temp] = i == (int) local[0] ? (int) local[1] : (int) local[0];
+        weights[idx[i]+temp] = (int) (hxtEdgesLength(edges,current[j])+1);
+        temp++;
       }
     }
     
@@ -663,10 +652,7 @@ HXTStatus hxtParametrizationCreate(HXTMesh *mesh, int nrefinements, HXTParametri
     HXTMeanValues *param;
     HXTEdges *current = (HXTEdges *)(toparam->ptr[i]);
     HXT_CHECK(hxtMeanValuesCreate(current,&param));
-    //printf("creation (%d/%d): ok\n",i,toparam->last);    
     HXT_CHECK(hxtMeanValuesCompute(param));        
-    //printf("%d \t ntottri = %ld\n",i,param0->edges->edg2mesh->triangles.num);
-    //printf("computation: ok\n");
     int ar;
     HXT_CHECK(hxtMeanValueAspectRatio(param,&ar));
     
@@ -678,20 +664,10 @@ HXTStatus hxtParametrizationCreate(HXTMesh *mesh, int nrefinements, HXTParametri
   hxtVectorFree(&toparam);
   
   param0->n = atlas->last+1;
-  //printf("n=%d\n",param0->n);
   HXT_CHECK(hxtMalloc(&param0->maps,param0->n*sizeof(HXTMeanValues*)));
-  for(int i=0; i<param0->n; i++){    
+  for(int i=0; i<param0->n; i++)    
     param0->maps[i] = (HXTMeanValues *) atlas->ptr[i];
-    /*
-    char str0[64];
-    sprintf(str0, "param_%d.msh",i);
-    HXT_CHECK(hxtMeanValuesWrite(param0->maps[i],str0));
-    char str1[64];
-    sprintf(str1, "paramMesh_%d.msh",i);
-    HXT_CHECK(hxtMeanValuesWriteParamMesh(param0->maps[i],str1));
-    printf("writing (%d/%d): ok\n",i,param0->n-1);
-    */
-  }
+  
   HXT_CHECK(hxtVectorFree(&atlas));
   return HXT_STATUS_OK;
 }
@@ -749,8 +725,8 @@ HXTStatus hxtParametrizationCompute(HXTParametrization *parametrization, int **_
     
     for(int ie=0; ie<ne; ie++){
       for(int kk=0; kk<3; kk++){
-	int gvn = (int) parametrization->edges->edg2mesh->triangles.node[3*global[ie]+kk];
-	nodes[nNodes[c]+gn[3*ie+kk]] = gvn;
+        int gvn = (int) parametrization->edges->edg2mesh->triangles.node[3*global[ie]+kk];
+        nodes[nNodes[c]+gn[3*ie+kk]] = gvn;
       }
     }
 
diff --git a/contrib/hxt/hxt_parametrization_old.c b/contrib/hxt/hxt_parametrization_old.c
deleted file mode 100644
index 40eaf64714783d226b735e09c0f4cf9e180c8aad..0000000000000000000000000000000000000000
--- a/contrib/hxt/hxt_parametrization_old.c
+++ /dev/null
@@ -1,412 +0,0 @@
-#include "hxt_parametrization.h"
-#include "hxt_mean_values.h"
-#include "hxt_edge.h"
-#include "metis.h"
-
-
-/*
-
-// # O U T P U T
-
-int* colors; //gives colors of elements: colors[i] = color of i-th element
-int* nNodes; //gives  lower index of int* nodes for a color: nNodes[i] = first node index of i-th color
-int* nodes; //gives the global index of initial 'mesh': nodes[i] = if i=nNodes[c]+k, gives the global index of k-th node of c-th color 
-double* uv; //give the uv param coordinates of a local node: uv[2*i+p] = u/v component of the global k-node of the c-th color
-
- */
-
-
-
-
-struct HXTVectorStruct{
-  void **ptr;
-  size_t size;
-  int last;
-  int length;
-};
-
-
-static HXTStatus hxtVectorInit(HXTVector **vecptr){
-
-
-  HXT_CHECK(hxtMalloc(vecptr,sizeof(HXTVector)));
-  
-  HXTVector *new = *vecptr;
-  
-  new->ptr = NULL;
-  new->last = -1;
-  new->length = 0;
-
-  return HXT_STATUS_OK;
-}
-
-
-static HXTStatus hxtVectorFree(HXTVector **vecptr){
-
-  HXTVector *vector = *vecptr;
-  
-  HXT_CHECK(hxtFree(&vector->ptr));
-
-  HXT_CHECK(hxtFree(vecptr));
-
-  return HXT_STATUS_OK;
-
-}
-
-static HXTStatus hxtVectorPushBack(HXTVector *vector, void *object){
-
-  int i = vector->last;
-  int length = vector->length;
-
-  if(i+1 >= length){
-    if(length==0)
-      length=1;
-    HXT_CHECK(hxtRealloc(&vector->ptr,2*(length)*sizeof(void *)));  
-    vector->length = 2*length;
-  }
-  vector->ptr[i+1] = object;
-  vector->last = i+1;
-
-  return HXT_STATUS_OK;
-}
-
-
-/*---------------------------------------*/
-
-
-
-struct HXTParametrizationStruct{
-  HXTEdges *edges;
-  int n;
-  HXTMeanValues **maps;
-};
-
-static HXTStatus hxtSplitEdges(HXTEdges *edges,int nPartitions,HXTVector *partitions)
-{
-
-  HXTMesh *mesh = edges->edg2mesh;
-  
-  uint32_t *tri2edg = edges->tri2edg;
-  uint64_t *edg2tri = edges->edg2tri; 
-  
-  HXTBoundaries *boundaries;
-  HXT_CHECK(hxtEdgesSetBoundaries(edges, &boundaries));
-  int nbedg;
-  HXT_CHECK(hxtBoundariesGetNumberOfBorderEdges(boundaries,&nbedg));
-
-  int nVertex = (int) mesh->triangles.num;
-  int nEdge = (int) (edges->numEdges) - nbedg;
-  
-  int *idx=NULL;
-  HXT_CHECK(hxtMalloc(&idx,(nVertex+1)*sizeof(int)));
-  int *nbh=NULL;
-  HXT_CHECK(hxtMalloc(&nbh, 2*nEdge*sizeof(int)));
-
-  idx[0] = 0;
-  for(int i=0; i<nVertex; i++){// triangle by triangle
-    uint32_t *current = &tri2edg[3*i];
-    int temp = 0;
-    for(int j=0; j<3; j++){      
-      uint64_t *local = &edg2tri[2*current[j]];
-      if(local[1]==-1){}//printf("ele %d (s1)\n",i+1);}
-      else{		
-	nbh[idx[i]+temp] = i == (int) local[0] ? (int) local[1] : (int) local[0];
-	temp++;
-      }
-    }
-    
-    idx[i+1] = idx[i] + temp;
-  }
-
-  
-  //  int options[5];
-  //  options[0] = 1;
-  //  options[1] = 1;
-  //  options[2] = 1;
-  //  options[3] = 1;
-  //  options[4] = 0;
-  int wgtflag = 0;
-  int numflag = 0;
-  
-  // only for k-way  
-  idx_t options[METIS_NOPTIONS];
-  METIS_SetDefaultOptions(options);
-  options[METIS_OPTION_NCUTS] = nPartitions;
-  options[METIS_OPTION_MINCONN] = 1;
-  options[METIS_OPTION_CONTIG] = 1;  
-  
-  int edgeCut;
-  int *part;
-  HXT_CHECK(hxtMalloc(&part,nVertex*sizeof(int)));
-  //int zero = 0;
-  int one=1;
-  //METIS_PartGraphRecursive(&nVertex,&one,idx,nbh,NULL,NULL,NULL,&nPartitions,NULL,NULL,NULL,&edgeCut,part);
-  //  METIS_PartGraphKway(&nVertex,idx,nbh,NULL,NULL,&wgtflag, &numflag,&nPartitions,options,&edgeCut,part);
-  //  METIS_PartGraphKway(&nVertex,&one,idx,nbh,NULL,NULL,weights,&nPartitions,NULL,NULL,options,&edgeCut,part);
-  METIS_PartGraphKway(&nVertex,&one,idx,nbh,NULL,NULL,NULL,&nPartitions,NULL,NULL,options,&edgeCut,part);
-  //  METIS_PartGraphKway(&nVertex,&one,idx,nbh,NULL,NULL,NULL,&nPartitions,NULL,NULL,options,&edgeCut,part);
-
-  HXT_CHECK(hxtFree(&nbh));
-  HXT_CHECK(hxtFree(&idx));
-  
-  
-  uint32_t *flagVertices;
-  HXT_CHECK(hxtMalloc(&flagVertices,mesh->vertices.num*sizeof(uint32_t)));
-  for(int p = 0; p<nPartitions; p++){
-    HXTMesh *msh=NULL;
-    HXT_CHECK(hxtMeshCreate(mesh->ctx,&msh));
-
-    
-    for(uint32_t iv=0; iv<mesh->vertices.num; iv++)
-      flagVertices[iv] = -1;
-    
-    uint32_t cv = 0;
-    uint64_t ce = 0;
-    for(uint64_t ie=0; ie<mesh->triangles.num; ie++){      
-      if(part[ie]==p){
-	for(int jv=0; jv<3; jv++){
-	  uint32_t current = mesh->triangles.node[3*ie+jv];
-	  if(flagVertices[current] == -1)
-	    flagVertices[current] = cv++;	  
-	}
-	ce++;
-      }
-    }
-    /*
-    printf("------------stat \t part %d---------------\n",p);
-    printf("\t number of vertices:%u\n",cv);
-    printf("\t number of elements:%lu\n",ce);
-    */
-    msh->vertices.num = cv;
-    HXT_CHECK(hxtAlignedMalloc(&msh->vertices.coord,4*cv*sizeof(double)));
-    for(uint32_t iv=0; iv<mesh->vertices.num; iv++){
-      uint32_t current = flagVertices[iv];
-      if(current==-1);
-      else{
-	msh->vertices.coord[4*current+0] = mesh->vertices.coord[4*iv+0];
-	msh->vertices.coord[4*current+1] = mesh->vertices.coord[4*iv+1];
-	msh->vertices.coord[4*current+2] = mesh->vertices.coord[4*iv+2];
-	msh->vertices.coord[4*current+3] = mesh->vertices.coord[4*iv+3];	
-      }
-    }	
-
-    msh->triangles.num = ce;
-    //<
-    uint64_t *global;    
-    HXT_CHECK(hxtMalloc(&global,ce*sizeof(uint64_t)));
-    //>
-    HXT_CHECK( hxtAlignedMalloc(&msh->triangles.node,ce*3*sizeof(uint32_t)) );
-    HXT_CHECK( hxtAlignedMalloc(&msh->triangles.colors,ce*sizeof(uint16_t)) );
-    ce = 0;
-    for(uint64_t ie=0; ie<mesh->triangles.num; ie++){
-      if(part[ie]==p){
-	global[ce] = edges->global[ie];
-	msh->triangles.colors[ce] = mesh->triangles.colors[ie];
-	for(int jv=0; jv<3; jv++){
-	  uint32_t current = flagVertices[mesh->triangles.node[3*ie+jv]];
-	  msh->triangles.node[3*ce+jv] = current;
-	}
-	ce++;
-      }
-    }
-    HXTEdges* edg = NULL;
-    HXT_CHECK(hxtEdgesCreate(msh,&edg));
-    //<    
-    edg->global = global;
-    //>
-    HXT_CHECK(hxtVectorPushBack(partitions,edg));
-  }
-  return HXT_STATUS_OK;
-}
-
-static HXTStatus hxtCuttingProcess(HXTEdges *edges, int ar, int bool, HXTVector *toparam)
-{
-
-  HXTBoundaries *boundaries;
-  HXT_CHECK(hxtEdgesSetBoundaries(edges, &boundaries));
-  int nll;
-  HXT_CHECK(hxtBoundariesGetNumberOfLineLoops(boundaries,&nll));
-  int g = (edges->numEdges - edges->edg2mesh->vertices.num - edges->edg2mesh->triangles.num + 2 - nll)/2;
-  
-  HXTVector *toSplit;
-  HXT_CHECK(hxtVectorInit(&toSplit));  
-  if(ar==0 || g!=0 || nll == 0)
-    HXT_CHECK(hxtVectorPushBack(toSplit,edges));
-  else
-    HXT_CHECK(hxtVectorPushBack(toparam,edges));
-  for(int i=0; i<toSplit->last+1; i++){
-    HXTVector *split=NULL;
-    HXT_CHECK(hxtVectorInit(&split));
-    HXTEdges *beingSplitted = (HXTEdges*) toSplit->ptr[i];
-    HXT_CHECK(hxtSplitEdges(beingSplitted,2,split));
-    if(bool==1){
-      HXT_CHECK(hxtMeshDelete(&beingSplitted->edg2mesh));
-      HXT_CHECK(hxtEdgesDelete(&beingSplitted));
-    }
-    else
-      bool=1;
-    for(int j=0; j<split->last+1; j++){
-      HXTEdges* piece = (HXTEdges*) split->ptr[j];
-      HXT_CHECK(hxtEdgesSetBoundaries(piece, &boundaries));
-      HXT_CHECK(hxtBoundariesGetNumberOfLineLoops(boundaries,&nll));
-      g = (piece->numEdges - piece->edg2mesh->vertices.num  - piece->edg2mesh->triangles.num + 2 - nll)/2;
-      if(g!=0 || nll==0){
-	HXT_CHECK(hxtVectorPushBack(toSplit,piece));
-      }
-      else
-	HXT_CHECK(hxtVectorPushBack(toparam,piece));
-    }
-    HXT_CHECK(hxtVectorFree(&split));
-  }
-  HXT_CHECK(hxtVectorFree(&toSplit));
-
-  // free boundaries
-  
-  return HXT_STATUS_OK;
-}
-
-HXTStatus hxtParametrizationCreate(HXTMesh *mesh, HXTParametrization **parametrization)
-{
-  HXTParametrization* param;
-  HXT_CHECK(hxtMalloc(&param,sizeof(HXTParametrization)));
-  *parametrization = param;
-
-  HXTMesh *initialMesh=NULL;
-  HXT_CHECK(hxtMeshCreate(mesh->ctx,&initialMesh));
-  *initialMesh = *mesh;
-  HXT_CHECK(hxtEdgesCreate(initialMesh, &param->edges));
-  
-  HXTEdges *edges=NULL;
-  HXT_CHECK(hxtEdgesCreate(initialMesh, &edges));
-  uint64_t *global;
-  HXT_CHECK(hxtMalloc(&global,edges->edg2mesh->triangles.num*sizeof(uint64_t)));
-  for(uint64_t i=0; i<edges->edg2mesh->triangles.num; i++)
-    global[i]= i;
-  edges->global = global;
-  param->edges->global = global;
-  HXTVector *toparam;
-  HXT_CHECK(hxtVectorInit(&toparam));
-  HXT_CHECK(hxtCuttingProcess(edges,1,0,toparam));
-  
-  HXTVector *atlas;
-  HXT_CHECK(hxtVectorInit(&atlas));
-  for(int i=0; i<toparam->last+1; i++){
-    HXTMeanValues *param;
-    HXTEdges *current = (HXTEdges *)(toparam->ptr[i]);
-    HXT_CHECK(hxtMeanValuesCreate(current,&param));
-    HXT_CHECK(hxtMeanValuesCompute(param));
-    int ar;
-    HXT_CHECK(hxtMeanValueAspectRatio(param,&ar));
-    
-    if (ar==0){
-      //printf("wrong aspect ratio!\n");
-      HXT_CHECK(hxtCuttingProcess(current,ar,1,toparam));
-      //printf("splitting process: ok\n");      
-    }
-    else
-      HXT_CHECK(hxtVectorPushBack(atlas,param));
-  }
-  hxtVectorFree(&toparam);
-
-
-  
-  param->n = atlas->last+1;
-  //printf("n=%d\n",param->n);
-  HXT_CHECK(hxtMalloc(&param->maps,param->n*sizeof(HXTMeanValues*)));
-  for(int i=0; i<param->n; i++){
-    param->maps[i] = (HXTMeanValues *) atlas->ptr[i];
-    //    char str0[64];
-    //    sprintf(str0, "param_%d.msh",i);
-    //    HXT_CHECK(hxtMeanValuesWrite(param->maps[i],str0));
-    //    char str1[64];
-    //    sprintf(str1, "paramMesh_%d.msh",i);
-    //    HXT_CHECK(hxtMeanValuesWriteParamMesh(param->maps[i],str1));
-    //printf("writing (%d/%d): ok\n",i,param->n-1);
-  }
-  HXT_CHECK(hxtVectorFree(&atlas));
-  return HXT_STATUS_OK;
-}
-
-
-
-HXTStatus hxtParametrizationDelete(HXTParametrization **parametrization)
-{
-
-  return HXT_STATUS_OK;
-}
-
-HXTStatus hxtParametrizationCompute(HXTParametrization *parametrization, int **_colors_, int **_nNodes_, int **_nodes_, double **_uv_, int *nc)
-{
-
-  int *colors=NULL, *nNodes=NULL, *nodes=NULL;
-  double *uv=NULL;
-  
-  int Ncolors = parametrization->n;
-  *nc=Ncolors;
-  int totalNtriangles = (int) parametrization->edges->edg2mesh->triangles.num;
-  //int totalNvertices = parametrization->edges->edg2mesh->vertices.num;
-  
-  HXT_CHECK(hxtMalloc(&colors,totalNtriangles*sizeof(int)));
-  HXT_CHECK(hxtMalloc(&nNodes,(Ncolors+1)*sizeof(int)));
-  nNodes[0] = 0;
-  for(int c=0; c<Ncolors; c++){
-
-    uint64_t *global=NULL;
-    int nv, ne;    
-    HXT_CHECK(hxtMeanValuesGetData(parametrization->maps[c],&global, NULL, NULL, &nv,&ne));
-    nNodes[c+1] = nNodes[c]+nv;
-
-    for(int ie=0; ie<ne; ie++)
-      colors[global[ie]] = c;
-    
-    HXT_CHECK(hxtFree(&global));
-    
-  }
-
-  
-  HXT_CHECK(hxtMalloc(&uv,2*nNodes[Ncolors]*sizeof(double)));//
-  HXT_CHECK(hxtMalloc(&nodes,nNodes[Ncolors]*sizeof(int)));
-  for(int it=0; it<nNodes[Ncolors]; it++)
-    nodes[it] = -1;
-  
-  for(int c=0; c<Ncolors; c++){
-
-    uint64_t *global=NULL;
-    uint32_t *gn=NULL;
-    double *uvc=NULL;
-    int nv, ne;    
-    HXT_CHECK(hxtMeanValuesGetData(parametrization->maps[c],&global, &gn, &uvc, &nv,&ne));
-    for(int iv=0; iv<2*nv; iv++)
-      uv[2*nNodes[c]+iv] = uvc[iv];
-    
-    for(int ie=0; ie<ne; ie++){
-      for(int kk=0; kk<3; kk++){
-	//printf("c=%d, ie=%d, global[ie]=%lu, kk=%d \t %lu\n",c,ie,global[ie],kk,3*parametrization->edges->edg2mesh->triangles.num);
-	int gvn = (int) parametrization->edges->edg2mesh->triangles.node[3*global[ie]+kk];
-	nodes[nNodes[c]+gn[3*ie+kk]] = gvn;
-      }
-    }
-
-    //printf("maybe not\n");
-    HXT_CHECK(hxtFree(&global));
-    //printf("-----------0\n");
-    HXT_CHECK(hxtFree(&gn));
-    //printf("-----------1\n");
-    HXT_CHECK(hxtFree(&uvc));
-    //printf("-----------2\n");
-  }
-
-  
-  *_colors_=colors;
-  *_nNodes_=nNodes;
-  *_nodes_=nodes;
-  *_uv_=uv;
-
-  return HXT_STATUS_OK;
-}
-
-HXTStatus hxtParametrizationWrite(HXTParametrization *parametrization, const char *filename)
-{  
-
-  return HXT_STATUS_OK;
-}
diff --git a/contrib/hxt/hxt_parametrization_old.h b/contrib/hxt/hxt_parametrization_old.h
deleted file mode 100644
index ad29d05c509da8f3741581017e37abb6cc90e555..0000000000000000000000000000000000000000
--- a/contrib/hxt/hxt_parametrization_old.h
+++ /dev/null
@@ -1,18 +0,0 @@
-#ifndef HEXTREME_PARAMETRIZATION_H
-#define HEXTREME_PARAMETRIZATION_H
-
-#include "hxt_tools.h"
-#include "hxt_mesh.h"
-#include "hxt_edge.h"
-
-typedef struct HXTVectorStruct HXTVector;
-typedef struct HXTParametrizationStruct HXTParametrization;
-
-
-HXTStatus hxtParametrizationCreate(HXTMesh *mesh, HXTParametrization **parametrization);
-HXTStatus hxtParametrizationDelete(HXTParametrization **parametrization);
-HXTStatus hxtParametrizationCompute(HXTParametrization *parametrization, int **colors, int **nNodes, int **nodes, double **uv, int*nc);
-HXTStatus hxtParametrizationWrite(HXTParametrization *parametrization, const char *filename);
-
-
-#endif
diff --git a/contrib/hxt/hxt_sort.c b/contrib/hxt/hxt_sort.c
new file mode 100644
index 0000000000000000000000000000000000000000..74ba36c4bda40d2f8448b4c125571245dd6d6310
--- /dev/null
+++ b/contrib/hxt/hxt_sort.c
@@ -0,0 +1,65 @@
+#include "hxt_sort.h"
+
+/***************************************
+ *     for 1 value                     *
+ ***************************************/
+static inline uint64_t group1_get(uint64_t* val, const void* userData){
+  return *val;
+}
+
+HXTStatus group1_sort(uint64_t* val, const uint64_t n, const uint64_t max){
+  PARALLEL_HYBRID64(uint64_t, val, n, max, group1_get, NULL);
+  return HXT_STATUS_OK;
+}
+
+/***************************************
+ *     for 2 values                    *
+ ***************************************/
+static inline uint64_t group2_get_v0(HXTGroup2* pair, const void* userData){
+  return pair->v[0];
+}
+
+HXTStatus group2_sort_v0(HXTGroup2* pair, const uint64_t n, const uint64_t max){
+  PARALLEL_HYBRID64(HXTGroup2, pair, n, max, group2_get_v0, NULL);
+  return HXT_STATUS_OK;
+}
+
+static inline uint64_t group2_get_v1(HXTGroup2* pair, const void* userData){
+  return pair->v[1];
+}
+
+HXTStatus group2_sort_v1(HXTGroup2* pair, const uint64_t n, const uint64_t max){
+  PARALLEL_HYBRID64(HXTGroup2, pair, n, max, group2_get_v1, NULL);
+  return HXT_STATUS_OK;
+}
+
+
+/***************************************
+ *     for 3 values                    *
+ ***************************************/
+static inline uint64_t group3_get_v0(HXTGroup3* triplet, const void* userData){
+  return triplet->v[0];
+}
+
+HXTStatus group3_sort_v0(HXTGroup3* triplet, const uint64_t n, const uint64_t max){
+  PARALLEL_HYBRID64(HXTGroup3, triplet, n, max, group3_get_v0, NULL);
+  return HXT_STATUS_OK;
+}
+
+static inline uint64_t group3_get_v1(HXTGroup3* triplet, const void* userData){
+  return triplet->v[1];
+}
+
+HXTStatus group3_sort_v1(HXTGroup3* triplet, const uint64_t n, const uint64_t max){
+  PARALLEL_HYBRID64(HXTGroup3, triplet, n, max, group3_get_v1, NULL);
+  return HXT_STATUS_OK;
+}
+
+static inline uint64_t group3_get_v2(HXTGroup3* triplet, const void* userData){
+  return triplet->v[2];
+}
+
+HXTStatus group3_sort_v2(HXTGroup3* triplet, const uint64_t n, const uint64_t max){
+  PARALLEL_HYBRID64(HXTGroup3, triplet, n, max, group3_get_v2, NULL);
+  return HXT_STATUS_OK;
+}
\ No newline at end of file
diff --git a/contrib/hxt/hxt_sort.h b/contrib/hxt/hxt_sort.h
index a30fe6195b3805b212b93297a6038244872ab9f0..ae409d33474cbf9729e52d5be5291a114228b7db 100644
--- a/contrib/hxt/hxt_sort.h
+++ b/contrib/hxt/hxt_sort.h
@@ -22,6 +22,24 @@ Author: Célestin Marot (celestin.marot@uclouvain.be)                        */
 
 #include "hxt_tools.h"
 
+// sorting function already defined
+typedef struct{
+  uint64_t v[2];
+}HXTGroup2;
+
+typedef struct{
+  uint64_t v[3];
+}HXTGroup3;
+
+HXTStatus group1_sort(uint64_t* val, const uint64_t n, const uint64_t max);
+
+HXTStatus group2_sort_v0(HXTGroup2* pair, const uint64_t n, const uint64_t max);
+HXTStatus group2_sort_v1(HXTGroup2* pair, const uint64_t n, const uint64_t max);
+
+HXTStatus group3_sort_v0(HXTGroup3* triplet, const uint64_t n, const uint64_t max);
+HXTStatus group3_sort_v1(HXTGroup3* triplet, const uint64_t n, const uint64_t max);
+HXTStatus group3_sort_v2(HXTGroup3* triplet, const uint64_t n, const uint64_t max);
+
 
 /* convert from other types to uint32_t CONSERVING ORDER FOR ALL VALUES ! */
 
diff --git a/contrib/hxt/hxt_tetAdjacencies.c b/contrib/hxt/hxt_tetAdjacencies.c
deleted file mode 100644
index fcf8dd3181b4e29b63587af30de8dd6721e7b457..0000000000000000000000000000000000000000
--- a/contrib/hxt/hxt_tetAdjacencies.c
+++ /dev/null
@@ -1,169 +0,0 @@
-#include "hxt_mesh.h"
-#include "hxt_vertices.h"
-#include "predicates.h"
-#include "hxt_sort.h"
-
-#define SWAP(x,y) do{uint32_t tmp=x; x=y; y=tmp;}while(0)
-
-/**********************************************
- (re-)compute adjacency of the tetrahedral mesh
- **********************************************/
-typedef struct{
-  uint32_t node[3];
-  uint64_t face;
-} triangleInfo;
-
-typedef struct{
-  uint64_t node;
-  uint64_t face;
-}triangleInfoFast;
-
-static inline uint32_t triangleKey0(triangleInfo* t, const void* userData){
-  return t->node[0];
-}
-
-static inline uint32_t triangleKey1(triangleInfo* t, const void* userData){
-  return t->node[1];
-}
-
-static inline uint32_t triangleKey2(triangleInfo* t, const void* userData){
-  return t->node[2];
-}
-
-static inline uint64_t triangleKeyFast(triangleInfoFast* t, const void* userData){
-  return t->node;
-}
-
-static HXTStatus triangleSort(triangleInfo* triangles, uint32_t n,
-                              uint32_t (*triangleKey)(triangleInfo*, const void*),
-                              uint32_t max){
-  HXTSORT32_UNIFORM(triangleInfo, triangles, n, max, triangleKey, NULL);
-  return HXT_STATUS_OK;
-}
-
-
-HXTStatus hxtComputeAdjacencies(HXTMesh* mesh){
-  const uint64_t nTet = mesh->tetrahedra.num;
-  const uint64_t n = mesh->vertices.num+1;
-
-  // make sure it was allocated
-  HXT_CHECK( hxtAlignedFree(&mesh->tetrahedra.neigh) );
-  HXT_CHECK( hxtAlignedMalloc(&mesh->tetrahedra.neigh, mesh->tetrahedra.size*4*sizeof(uint64_t)) );
-
-  #pragma omp parallel for
-  for (uint64_t i=0; i<mesh->tetrahedra.num*4; i++) {
-    mesh->tetrahedra.neigh[i] = HXT_NO_ADJACENT;
-  }
-
-  // first step, create all triangles...
-  triangleInfo* triangles;
-  triangleInfoFast* trianglesFast; // used when n^3 can fit in 64 bits
-
-  if(n > 2642245)// n^3 cannot be on 64 bits
-    HXT_CHECK( hxtAlignedMalloc(&triangles, nTet*4*sizeof(triangleInfo)) );
-  else
-    HXT_CHECK( hxtAlignedMalloc(&trianglesFast, nTet*4*sizeof(triangleInfoFast)) );
-
-  int hxtDeclareAligned indices[4][4] = {{1,2,3,0},{0,2,3,0},{0,1,3,0},{0,1,2,0}}; // 4th is just a padding
-
-  // fill the triangle structure
-  #pragma omp parallel for
-  for (uint64_t i=0; i<nTet; i++) {
-    uint32_t* node = mesh->tetrahedra.node + 4*i;
-    int hxtDeclareAligned order[4];
-
-    // sort a and b
-    int cmp1 = (node[0] <= node[1]);
-    int cmp2 = (node[2] <= node[3]);
-    order[0] = !cmp1;
-    order[1] = cmp1;
-    order[2] = 2 + !cmp2;
-    order[3] = 2 + cmp2;
-
-    // after this, order[0] is at the right place
-    if(node[order[2]] < node[order[0]]){
-      SWAP(order[0], order[2]);
-    }
-
-    // after this, order[3] is at the right place
-    if(node[order[3]] < node[order[1]]){
-      SWAP(order[3], order[1]);
-    }
-
-    // remains order[1] and order[2]
-    if(node[order[2]] < node[order[1]]){
-      SWAP(order[1], order[2]);
-    }
-
-    // on some architecture, this is a simple SIMD operation
-    uint32_t hxtDeclareAligned nodeOrdered[4];
-    for (int j=0; j<4; j++) {
-      nodeOrdered[j] = node[order[j]];
-    }
-
-    if(nodeOrdered[3]==HXT_GHOST_VERTEX)
-      nodeOrdered[3] = mesh->vertices.num;
-
-    if(n > 2642245){
-      for (int j=0; j<4; j++) {
-        triangles[i*4+j].node[0] = nodeOrdered[indices[j][0]];
-        triangles[i*4+j].node[1] = nodeOrdered[indices[j][1]];
-        triangles[i*4+j].node[2] = nodeOrdered[indices[j][2]];
-        triangles[i*4+j].face = i*4 + order[j];
-      }
-    }
-    else{
-      for (int j=0; j<4; j++) {
-        trianglesFast[i*4+j].node = nodeOrdered[indices[j][0]]*n*n
-                                  + nodeOrdered[indices[j][1]]*n
-                                  + nodeOrdered[indices[j][2]];
-        trianglesFast[i*4+j].face = i*4 + order[j];
-      }
-    }
-
-
-  }
-
-  // sort the triangles...
-  if(n > 2642245)
-  {
-    HXT_CHECK( triangleSort(triangles, nTet*4, triangleKey2, mesh->vertices.num) );
-    HXT_CHECK( triangleSort(triangles, nTet*4, triangleKey1, mesh->vertices.num) );
-    HXT_CHECK( triangleSort(triangles, nTet*4, triangleKey0, mesh->vertices.num) );
-
-    // now that triangles are sorted, when two are the same, we
-    #pragma omp parallel for
-    for (uint64_t i=0; i<nTet*4-1; i++) {
-      if(triangles[i].node[0]==triangles[i+1].node[0] &&
-         triangles[i].node[1]==triangles[i+1].node[1] &&
-         triangles[i].node[2]==triangles[i+1].node[2])
-      {
-        mesh->tetrahedra.neigh[triangles[i].face] = triangles[i+1].face;
-        mesh->tetrahedra.neigh[triangles[i+1].face] = triangles[i].face;
-        // i++; // can be done but break SIMD
-      }
-    }
-
-    HXT_CHECK( hxtAlignedFree(&triangles) );
-  }
-  else{
-    HXTSORT64_UNIFORM(triangleInfoFast, trianglesFast, nTet*4, n*n*n-1, triangleKeyFast, NULL);
-
-    #pragma omp parallel for
-    for (uint64_t i=0; i<nTet*4-1; i++) {
-      if(trianglesFast[i].node==trianglesFast[i+1].node)
-      {
-        mesh->tetrahedra.neigh[trianglesFast[i].face] = trianglesFast[i+1].face;
-        mesh->tetrahedra.neigh[trianglesFast[i+1].face] = trianglesFast[i].face;
-        // i++; // can be done but break SIMD
-      }
-    }
-
-    HXT_CHECK( hxtAlignedFree(&trianglesFast) );
-  }
-
-
-
-
-  return HXT_STATUS_OK;
-}
\ No newline at end of file
diff --git a/contrib/hxt/hxt_tetAdjacencies.h b/contrib/hxt/hxt_tetAdjacencies.h
deleted file mode 100644
index c21bd52e4ee1116be939a7f85d01516882433d58..0000000000000000000000000000000000000000
--- a/contrib/hxt/hxt_tetAdjacencies.h
+++ /dev/null
@@ -1,19 +0,0 @@
-#ifndef _HXT_TETADJACENCIES_
-#define _HXT_TETADJACENCIES_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include "hxt_mesh.h"
-
-/* (re-)compute the adjacency of a mesh */
-HXTStatus hxtComputeAdjacencies(HXTMesh* mesh);
-
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
-
diff --git a/contrib/hxt/hxt_tetFlag.c b/contrib/hxt/hxt_tetFlag.c
new file mode 100644
index 0000000000000000000000000000000000000000..bf068fd7677023574aabaa6f9c8183725d5456df
--- /dev/null
+++ b/contrib/hxt/hxt_tetFlag.c
@@ -0,0 +1,645 @@
+#include "hxt_sort.h"
+#include "hxt_tetFlag.h"
+
+
+static inline void sort3ints(uint32_t i[3]){
+  if(i[0]>i[1]){
+    uint32_t tmp = i[0]; i[0] = i[1]; i[1] = tmp;
+  }
+
+  if(i[1]>i[2]){
+    uint32_t tmp = i[1]; i[1] = i[2]; i[2] = tmp;
+
+    if(i[0]>i[1]){
+      uint32_t tmp = i[0]; i[0] = i[1]; i[1] = tmp;
+    }
+  }
+}
+// just a function such that:
+// if hashedCompare(a,b)==true
+// then hashedCompare(b,a)==false
+//
+// BUT hashedCompare(a,b)==true and hashedCompare(b,c)==true
+// doesn't imply that hashedCompare(a,c)==true
+// and inversely if you replace true with false (no transitivity)
+static inline uint64_t hashedCompare(uint64_t a, uint64_t b) {
+  return ((a^b)&1)^(a<b);
+}
+
+
+static inline uint64_t hash64(uint64_t x) {
+    x = (x ^ (x >> 30)) * UINT64_C(0xbf58476d1ce4e5b9);
+    x = (x ^ (x >> 27)) * UINT64_C(0x94d049bb133111eb);
+    x = x ^ (x >> 31);
+    return x;
+}
+
+
+// just a function such that:
+// if transitiveHashedCmp(a,b)==true
+// then transitiveHashedCmp(b,a)==false
+//
+// AND transitiveHashedCmp(a,b)==true and transitiveHashedCmp(b,c)==true
+// imply that transitiveHashedCmp(a,c)==true
+// and inversely if you replace true with false (the relation is transitive)
+static inline uint64_t transitiveHashedCmp(uint64_t a, uint64_t b) {
+  return hash64(a) < hash64(b);
+}
+
+
+static HXTStatus hxtEdgesNotInTriangles(HXTMesh* mesh, uint32_t* lines, uint64_t* numLines, uint64_t* threadCount, const uint64_t n) {
+  HXTGroup2* edgeKey = NULL;
+
+  uint64_t numEdgesTotal = mesh->triangles.num*3+mesh->lines.num;
+  HXT_CHECK( hxtAlignedMalloc(&edgeKey, numEdgesTotal*sizeof(HXTGroup2)) );
+
+  #pragma omp parallel
+  {
+    #pragma omp for nowait
+    for (uint64_t i=0; i<mesh->lines.num; i++) {
+      if(mesh->lines.node[2*i]<mesh->lines.node[2*i+1]) {
+        edgeKey[i].v[0] = mesh->lines.node[2*i]*n + mesh->lines.node[2*i+1];
+        edgeKey[i].v[1] = 0;
+      }
+      else if(mesh->lines.node[2*i]<mesh->lines.node[2*i+1]){
+        edgeKey[i].v[0] = mesh->lines.node[2*i+1]*n + mesh->lines.node[2*i];
+        edgeKey[i].v[1] = 0;
+      }
+      else {
+        edgeKey[i].v[0] = mesh->lines.node[2*i]*n + mesh->lines.node[2*i]; // the line begins and ends at the same point...
+        edgeKey[i].v[1] = 1;
+      }
+    }
+
+    #pragma omp for nowait
+    for (uint64_t i=0; i<mesh->triangles.num; i++) {
+      uint32_t v[3] = {mesh->triangles.node[3*i+0],
+                       mesh->triangles.node[3*i+1],
+                       mesh->triangles.node[3*i+2]};
+
+      sort3ints(v);
+
+      edgeKey[mesh->lines.num+3*i].v[0] = v[0]*n + v[1];
+      edgeKey[mesh->lines.num+3*i].v[1] = 1;
+      edgeKey[mesh->lines.num+3*i+1].v[0] = v[0]*n + v[2];
+      edgeKey[mesh->lines.num+3*i+1].v[1] = 1;
+      edgeKey[mesh->lines.num+3*i+2].v[0] = v[1]*n + v[2];
+      edgeKey[mesh->lines.num+3*i+2].v[1] = 1;
+    }
+  }
+
+  group2_sort_v0(edgeKey, numEdgesTotal, n*(n-1)-1);
+
+  uint64_t total = 0;
+
+  // TODO: filter all edge (the sort is stable... maybe put triangles before lines :p)
+  #pragma omp parallel
+  {
+    int threadID = omp_get_thread_num();
+    uint64_t localNum = 0;
+
+    #pragma omp for schedule(static)
+    for (uint64_t i=0; i<numEdgesTotal; i++) {
+      if(edgeKey[i].v[1]==0) {
+        if(i==numEdgesTotal-1 || edgeKey[i].v[0] != edgeKey[i+1].v[0])
+          localNum++;
+        else
+          edgeKey[i].v[1]=1;
+      }
+    }
+
+    threadCount[threadID] = localNum;
+
+    #pragma omp barrier
+    #pragma omp single
+    {
+      int nthreads = omp_get_num_threads();
+      for (int i=0; i<nthreads; i++) {
+        uint32_t tsum = total + threadCount[i];
+        threadCount[i] = total;
+        total = tsum;
+      }
+    }
+
+    localNum = threadCount[threadID];
+    if(total) {
+      #pragma omp for schedule(static)
+      for (uint64_t i=0; i<numEdgesTotal; i++) {
+        if(edgeKey[i].v[1]==0) {
+          lines[2*localNum] = edgeKey[i].v[0]%n;
+          lines[2*localNum+1] = edgeKey[i].v[0]/n;
+          localNum++;
+        }
+      }
+    }
+  }
+
+  *numLines = total;
+
+  HXT_CHECK( hxtAlignedFree(&edgeKey) );
+
+  return HXT_STATUS_OK;
+}
+
+
+/********************************************************************************
+ *  report the number of missing edges or set tet.flag for constrained edges    *
+ ********************************************************************************/
+// every tetrahedra must have a neighbor HXT_NO_ADJACENT is NOT permitted !!!
+HXTStatus hxtConstrainEdgesNotInTriangles(HXTMesh* mesh, uint64_t* missing) {
+  const int nodeArray[4][4] = {{-1, 2, 3, 1},
+                               { 3,-1, 0, 2},
+                               { 1, 3,-1, 0},
+                               { 2, 0, 1,-1}};
+
+  const int facetToNumber[4][4] = {{-1, 0, 1, 2},
+                                   { 0,-1, 3, 4},
+                                   { 1, 3,-1, 5},
+                                   { 2, 4, 5,-1}};
+
+
+  const int numberToFacetMin[] = { 0, 0, 0, 1, 1, 2};
+  const int numberToFacetMax[] = { 1, 2, 3, 2, 3, 3};
+
+  const uint64_t n = mesh->vertices.num;
+  uint64_t* numEdges;
+  uint32_t* lines;
+  uint64_t numLines;
+  int maxThreads = omp_get_max_threads();
+  HXT_CHECK( hxtMalloc(&numEdges, maxThreads*sizeof(uint64_t)) );
+  HXT_CHECK( hxtAlignedMalloc(&lines, mesh->lines.num*2*sizeof(uint32_t)) );
+  
+  // we don't wont to constrain edge that are already in a triangle
+  HXT_CHECK( hxtEdgesNotInTriangles(mesh, lines, &numLines, numEdges, n) );
+
+  if(numLines==0) {
+    HXT_CHECK( hxtAlignedFree(&lines) );
+    HXT_CHECK( hxtFree(&numEdges) );
+    return HXT_STATUS_OK;
+  }
+
+  HXTGroup2* edgeKey = NULL;
+  uint64_t numEdgesTotal = 0;
+  HXTStatus status = HXT_STATUS_OK;
+
+  #pragma omp parallel
+  {
+    const int threadID = omp_get_thread_num();
+    uint64_t localNum = 0;
+
+    #pragma omp for schedule(static)
+    for (uint64_t i=0; i<mesh->tetrahedra.num; i++) { // for each tetrahedra
+      for (int j=0; j<4; j++) {
+        for (int k=j+1; k<4; k++) {
+          int in_facet = j;
+          int out_facet = k;
+
+          uint32_t p0 = mesh->tetrahedra.node[4*i + nodeArray[j][k]];
+          uint32_t p1 = mesh->tetrahedra.node[4*i + nodeArray[k][j]];
+
+          if(p0==HXT_GHOST_VERTEX || p1==HXT_GHOST_VERTEX)
+            continue;
+
+          int truth = 1;
+
+          uint64_t curTet = i;
+          do
+          {
+            uint32_t newV = mesh->tetrahedra.node[4*curTet + in_facet];
+
+            // go into the neighbor through out_facet
+            uint64_t neigh = mesh->tetrahedra.neigh[4*curTet + out_facet];
+            curTet = neigh/4;
+            in_facet = neigh%4;
+
+            if(transitiveHashedCmp(curTet, i)) {
+              truth=0;
+              break;
+            }
+
+            uint32_t* nodes = mesh->tetrahedra.node + 4*curTet;
+            for (out_facet=0; out_facet<3; out_facet++)
+              if(nodes[out_facet]==newV)
+                break;
+
+          } while (curTet!=i);
+
+          if(truth){
+            constrainEdge(mesh, i, j, k);
+            localNum++;
+          }
+        }
+      }
+    }
+
+    numEdges[threadID] = localNum;
+
+    #pragma omp barrier
+    #pragma omp single
+    {
+      int nthreads = omp_get_num_threads();
+      numEdgesTotal = numLines;
+      for (int i=0; i<nthreads; i++) {
+        // printf("%lu\n", numEdges[i]);
+        uint32_t tsum = numEdgesTotal + numEdges[i];
+        numEdges[i] = numEdgesTotal;
+        numEdgesTotal = tsum;
+      }
+
+#ifndef NDEBUG
+      if(numEdgesTotal>2*mesh->tetrahedra.num+numLines){
+        HXT_ERROR_MSG(HXT_STATUS_ERROR, "you should never go here..");
+        exit(EXIT_FAILURE);
+      }
+#endif
+
+      status = hxtAlignedMalloc(&edgeKey, numEdgesTotal*sizeof(HXTGroup2));
+    }
+
+    if(status==HXT_STATUS_OK) {
+      // copy the edges from mesh->lines in the edgeKey struct array
+      #pragma omp for
+      for (uint64_t i=0; i<numLines; i++) {
+        uint32_t p0 = lines[2*i+0];
+        uint32_t p1 = lines[2*i+1];
+
+        if(p0<p1) {
+          edgeKey[i].v[0] = p0*n*2 + p1*2 + 0;
+        }
+        else {
+          edgeKey[i].v[0] = p1*n*2 + p0*2 + 0;
+        }
+
+        edgeKey[i].v[1] = HXT_NO_ADJACENT; // this lines does not come from any tetrahedra
+      }
+
+      localNum = numEdges[threadID];
+      #pragma omp for schedule(static)
+      for (uint64_t i=0; i<mesh->tetrahedra.num; i++) {
+        for (int j=0; j<4; j++) {
+          for (int k=j+1; k<4; k++) {
+            if(isEdgeConstrained(mesh, i, j, k)){
+              uint32_t p0 = mesh->tetrahedra.node[4*i + nodeArray[j][k]];
+              uint32_t p1 = mesh->tetrahedra.node[4*i + nodeArray[k][j]];
+
+              if(p0==HXT_GHOST_VERTEX || p1==HXT_GHOST_VERTEX)
+              {
+                HXT_ERROR_MSG(HXT_STATUS_ERROR, "There were contrained edges before this function was called.");
+                exit(EXIT_FAILURE);
+              }
+
+              if(p0<p1){
+                edgeKey[localNum].v[0] = p0*n*2 + p1*2 + 1;
+              }
+              else {
+                edgeKey[localNum].v[0] = p1*n*2 + p0*2 + 1;
+              }
+              edgeKey[localNum].v[1] = 6*i+facetToNumber[j][k];
+
+              localNum++;
+
+              unconstrainEdge(mesh, i, j, k);
+            }
+          }
+        }
+      }
+    }
+  }
+
+  HXT_CHECK( hxtAlignedFree(&lines) );
+
+  HXT_CHECK(status);
+
+  group2_sort_v0(edgeKey, numEdgesTotal, (n-1)*n*2-1);
+
+  #pragma omp parallel
+  {
+    const int threadID = omp_get_thread_num();
+    numEdges[threadID] = 0;
+
+    #pragma omp for
+    for (uint64_t i=0; i<numEdgesTotal; i++) {
+      if(edgeKey[i].v[0]%2==0 && (i==numEdgesTotal-1 || edgeKey[i].v[0]/2!=edgeKey[i+1].v[0]/2))
+        numEdges[threadID]++; // the edge is missing          
+    }
+
+    #pragma omp barrier
+    #pragma omp single
+    {
+      int nthreads = omp_get_num_threads();
+      *missing = 0;
+      for (int i=0; i<nthreads; i++) {
+        *missing+=numEdges[i];
+      }
+    }
+  }
+
+  HXT_CHECK( hxtFree(&numEdges) );
+
+  if(*missing){
+    HXT_CHECK( hxtAlignedFree(&edgeKey) );
+    return HXT_STATUS_OK;
+  }
+
+  char* edgeFlag;
+  HXT_CHECK( hxtAlignedMalloc(&edgeFlag, 6*mesh->tetrahedra.num*sizeof(char)) );
+  memset(edgeFlag, 0, 6*mesh->tetrahedra.num*sizeof(char));
+
+  #pragma omp parallel for
+  for (uint64_t i=1; i<numEdgesTotal; i++) {
+    if(edgeKey[i-1].v[0]%2==0) {
+
+      // turn around the edge to set edgeFlag of all tetrahedra to 1...
+      uint64_t firstTet = edgeKey[i].v[1]/6;
+      uint64_t curTet = firstTet;
+      int edgeNumber = edgeKey[i].v[1]%6;
+      int in_facet = numberToFacetMin[edgeNumber];
+      int out_facet = numberToFacetMax[edgeNumber];
+
+      do
+      {
+      	edgeFlag[6*curTet + facetToNumber[in_facet][out_facet]] = 1;
+
+      	uint32_t newV = mesh->tetrahedra.node[4*curTet + in_facet];
+
+        // go into the neighbor through out_facet
+        uint64_t neigh = mesh->tetrahedra.neigh[4*curTet + out_facet];
+        curTet = neigh/4;
+        in_facet = neigh%4;
+        uint32_t* nodes = mesh->tetrahedra.node + 4*curTet;
+        for (out_facet=0; out_facet<3; out_facet++)
+          if(nodes[out_facet]==newV)
+            break;
+
+      } while (curTet!=firstTet);
+    }
+  }
+
+  HXT_CHECK( hxtAlignedFree(&edgeKey) );
+
+  #pragma omp parallel for
+  for (uint64_t i=0; i<mesh->tetrahedra.num; i++) {
+    for (int j=0; j<6; j++) {
+      if(edgeFlag[6*i+j])
+        constrainEdge(mesh, i, numberToFacetMin[j], numberToFacetMax[j]);
+    }
+  }
+
+
+  HXT_CHECK( hxtAlignedFree(&edgeFlag) );
+  
+
+  return HXT_STATUS_OK;
+}
+
+
+/********************************************************************************
+ *  report the number of missing triangles or set flag for constrained triangle *
+ ********************************************************************************/
+// every tetrahedra must have a neighbor HXT_NO_ADJACENT is NOT permitted !!!
+HXTStatus hxtConstrainTriangles(HXTMesh* mesh, uint64_t* missing) {
+  HXTGroup2 *triKey = NULL;
+  HXTGroup3* pairKey = NULL;
+
+  if(mesh->triangles.num==0) {
+    *missing = 0;
+    return HXT_STATUS_OK;
+  }
+
+  uint64_t *numTriangles;
+  int maxThreads = omp_get_max_threads();
+  HXT_CHECK( hxtMalloc(&numTriangles, maxThreads*sizeof(uint64_t)) );
+
+  const uint64_t n = mesh->vertices.num;
+  uint64_t numTrianglesTotal;
+
+  HXTStatus status = HXT_STATUS_OK;
+
+#ifndef NDEBUG
+  uint64_t nGhosts = 0;
+
+  #pragma omp parallel for reduction(+:nGhosts)
+  for (uint64_t i=0; i<mesh->tetrahedra.num; i++) {
+    if(mesh->tetrahedra.node[4*i+3]==HXT_GHOST_VERTEX)
+      nGhosts++;
+  }
+
+  if(n <= 2097152){
+    HXT_CHECK( hxtAlignedMalloc(&triKey, (2*mesh->tetrahedra.num-3*nGhosts/2+mesh->triangles.num)*sizeof(HXTGroup2)) );
+  }
+  else{
+    HXT_CHECK( hxtAlignedMalloc(&pairKey, (2*mesh->tetrahedra.num-3*nGhosts/2+mesh->triangles.num)*sizeof(HXTGroup3)) );
+  }
+#endif
+
+  // create a list of triangles in the mesh:
+  #pragma omp parallel
+  {
+    const int threadID = omp_get_thread_num();
+    uint64_t localNum = 0;
+
+    #pragma omp for schedule(static)
+    for (uint64_t i=0; i<mesh->tetrahedra.num; i++) {
+      if(mesh->tetrahedra.node[4*i+3]!=HXT_GHOST_VERTEX) {
+        for (int j=0; j<4; j++) {
+          uint64_t neigh = mesh->tetrahedra.neigh[4*i+j]/4;
+          if(hashedCompare(i, neigh) || 
+             mesh->tetrahedra.node[neigh*4+3]==HXT_GHOST_VERTEX)
+            localNum++;
+        }
+      }
+    }
+
+    numTriangles[threadID] = localNum;
+    #pragma omp barrier
+    #pragma omp single
+    {
+      int nthreads = omp_get_num_threads();
+      numTrianglesTotal = mesh->triangles.num;
+      for (int i=0; i<nthreads; i++) {
+        uint32_t tsum = numTrianglesTotal + numTriangles[i];
+        numTriangles[i] = numTrianglesTotal;
+        numTrianglesTotal = tsum;
+      }
+
+#ifndef NDEBUG
+      if(numTrianglesTotal!=2*mesh->tetrahedra.num-3*nGhosts/2+mesh->triangles.num){
+        HXT_ERROR_MSG(HXT_STATUS_ERROR, "you should never go here... (%lu!=2*%lu+3*%lu/2",numTrianglesTotal-mesh->triangles.num,
+                                                                                                mesh->tetrahedra.num, nGhosts);
+        exit(EXIT_FAILURE);
+      }
+#else
+      if(n <= 2097152){
+        status = hxtAlignedMalloc(&triKey, numTrianglesTotal*sizeof(HXTGroup2));
+      }
+      else{
+        status = hxtAlignedMalloc(&pairKey, numTrianglesTotal*sizeof(HXTGroup3));
+      }
+#endif
+    }
+
+    if(status==HXT_STATUS_OK) {
+      // copy the triangles from mesh->triangles in the triKey struct array
+      if(n <= 2097152){
+        #pragma omp for
+        for (uint64_t i=0; i<mesh->triangles.num; i++) {
+          uint32_t v[3] = {mesh->triangles.node[3*i+0],
+                           mesh->triangles.node[3*i+1],
+                           mesh->triangles.node[3*i+2]};
+          sort3ints(v);
+          triKey[i].v[1] = HXT_NO_ADJACENT; // this triangle does not come from any tetrahedra
+          triKey[i].v[0] = v[0]*(n-1)*n*2 + v[1]*n*2 + v[2]*2 + 0; // the lowest bit of triangles from mesh->triangles is unset
+        }
+      }
+      else{
+        #pragma omp for
+        for (uint64_t i=0; i<mesh->triangles.num; i++) {
+          uint32_t v[3] = {mesh->triangles.node[3*i+0],
+                           mesh->triangles.node[3*i+1],
+                           mesh->triangles.node[3*i+2]};
+          sort3ints(v);
+          pairKey[i].v[2] = HXT_NO_ADJACENT; // this triangle does not come from any tetrahedra
+          pairKey[i].v[1] = v[0]*(n-1) + v[1];
+          pairKey[i].v[0] = v[2]*2+0;
+        }
+      }
+
+      // add the triangle from the tetrahedral mesh to the triKey struct array
+      localNum = numTriangles[threadID];
+      if(n <= 2097152){
+        #pragma omp for schedule(static)
+        for (uint64_t i=0; i<mesh->tetrahedra.num; i++) {
+          if(mesh->tetrahedra.node[4*i+3]!=HXT_GHOST_VERTEX){
+            for (int j=0; j<4; j++) {
+              uint64_t neigh = mesh->tetrahedra.neigh[4*i+j]/4;
+              if(hashedCompare(i, neigh) || 
+                 mesh->tetrahedra.node[neigh*4+3]==HXT_GHOST_VERTEX) {
+                uint32_t v[3] = {mesh->tetrahedra.node[4*i+((j+1)&3)],
+                                 mesh->tetrahedra.node[4*i+((j+2)&3)],
+                                 mesh->tetrahedra.node[4*i+((j+3)&3)]};
+                sort3ints(v);
+                triKey[localNum].v[1] = 4*i+j;
+                triKey[localNum].v[0] = v[0]*(n-1)*n*2 + v[1]*n*2 + v[2]*2 + 1; // max: 2(n-3)(n-1)n + 2(n-2)n + 2(n-1) + 1
+                                                                                //    = 2(n-2)(n-1)n-1
+                localNum++;
+              }
+            }
+          }
+        }
+      }
+      else {
+        #pragma omp for schedule(static)
+        for (uint64_t i=0; i<mesh->tetrahedra.num; i++) {
+          if(mesh->tetrahedra.node[4*i+3]!=HXT_GHOST_VERTEX){
+            for (int j=0; j<4; j++) {
+              uint64_t neigh = mesh->tetrahedra.neigh[4*i+j]/4;
+              if(hashedCompare(i, neigh) || 
+                 mesh->tetrahedra.node[neigh*4+3]==HXT_GHOST_VERTEX) {
+                uint32_t v[3] = {mesh->tetrahedra.node[4*i+((j+1)&3)],
+                                 mesh->tetrahedra.node[4*i+((j+2)&3)],
+                                 mesh->tetrahedra.node[4*i+((j+3)&3)]};
+                sort3ints(v);
+                pairKey[localNum].v[2] = 4*i+j;
+                pairKey[localNum].v[1] = v[0]*(n-1) + v[1]; // max: (n-3)(n-1) + (n-2) = (n-2)(n-1) - 1
+                pairKey[localNum].v[0] = v[2]*2+1;          // max: (n-1)2+1 = 2n-1
+
+                localNum++;
+              }
+            }
+          }
+        }
+      }
+    }
+  }
+
+  HXT_CHECK(status);
+
+  if(n <= 2097152){
+    // sort triKey
+    group2_sort_v0(triKey, numTrianglesTotal, 2*(n-2)*(n-1)*n-1);
+  }
+  else{
+    group3_sort_v0(pairKey, numTrianglesTotal, 2*n-1);
+    group3_sort_v1(pairKey, numTrianglesTotal, (n-2)*(n-1)-1);
+  }
+
+  #pragma omp parallel
+  {
+    const int threadID = omp_get_thread_num();
+    numTriangles[threadID] = 0;
+
+    if(n <= 2097152){
+      #pragma omp for
+      for (uint64_t i=0; i<numTrianglesTotal; i++) {
+        if(triKey[i].v[0]%2==0 && (i==numTrianglesTotal-1 || triKey[i].v[0]/2!=triKey[i+1].v[0]/2))
+            numTriangles[threadID]++; // the triangle is missing
+      }
+    }
+    else{
+      #pragma omp for
+      for (uint64_t i=0; i<numTrianglesTotal; i++) {
+        if(pairKey[i].v[0]%2==0 && (i==numTrianglesTotal-1 || pairKey[i].v[0]/2!=pairKey[i+1].v[0]/2 || pairKey[i].v[1]!=pairKey[i+1].v[1]))
+            numTriangles[threadID]++;
+      }
+    }
+
+    #pragma omp barrier
+    #pragma omp single
+    {
+      int nthreads = omp_get_num_threads();
+      *missing = 0;
+      for (int i=0; i<nthreads; i++) {
+        *missing+=numTriangles[i];
+      }
+    }
+  }
+
+  HXT_CHECK( hxtFree(&numTriangles) );
+
+  if(*missing){
+    HXT_CHECK( hxtAlignedFree(&triKey) );
+    HXT_CHECK( hxtAlignedFree(&pairKey) );
+    return HXT_STATUS_OK;
+  }
+
+  char* faceFlag;
+  HXT_CHECK( hxtAlignedMalloc(&faceFlag, 4*mesh->tetrahedra.num*sizeof(char)) );
+  memset(faceFlag, 0, 4*mesh->tetrahedra.num*sizeof(char));
+
+  if(n <= 2097152){
+    #pragma omp parallel for
+    for (uint64_t i=1; i<numTrianglesTotal; i++) {
+      if(triKey[i-1].v[0]%2==0 ) {
+        faceFlag[triKey[i].v[1]] = 1;
+        if(mesh->tetrahedra.neigh[triKey[i].v[1]]!=HXT_NO_ADJACENT)
+          faceFlag[mesh->tetrahedra.neigh[triKey[i].v[1]]] = 1;
+      }
+    }
+  }
+  else{
+    #pragma omp parallel for
+    for (uint64_t i=1; i<numTrianglesTotal; i++) {     
+      if(pairKey[i-1].v[0]%2==0) {
+        faceFlag[pairKey[i].v[2]] = 1;
+        if(mesh->tetrahedra.neigh[pairKey[i].v[2]]!=HXT_NO_ADJACENT)
+          faceFlag[mesh->tetrahedra.neigh[pairKey[i].v[2]]] = 1;
+      }
+    }
+  }
+
+  HXT_CHECK( hxtAlignedFree(&triKey) );
+  HXT_CHECK( hxtAlignedFree(&pairKey) );
+
+
+  #pragma omp parallel for
+  for (uint64_t i=0; i<mesh->tetrahedra.num; i++) {
+    for (int j=0; j<4; j++) {
+      if(faceFlag[4*i+j])
+        constrainFacet(mesh, 4*i+j);
+    }
+  }
+
+  HXT_CHECK( hxtAlignedFree(&faceFlag) );
+
+  return HXT_STATUS_OK;
+}
\ No newline at end of file
diff --git a/contrib/hxt/hxt_tetFlag.h b/contrib/hxt/hxt_tetFlag.h
new file mode 100644
index 0000000000000000000000000000000000000000..829df847ae5f629e9c6a531311fcc377f12b4fbe
--- /dev/null
+++ b/contrib/hxt/hxt_tetFlag.h
@@ -0,0 +1,109 @@
+#ifndef _HXT_TETFLAG_
+#define _HXT_TETFLAG_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "hxt_mesh.h"
+
+// Verify if all facets are present in a tetrahedrization, missing is the number of missing facets
+// it then set tetrahedra.flag to account for the contrains on the facets
+HXTStatus hxtConstrainTriangles(HXTMesh* mesh, uint64_t* missing);
+HXTStatus hxtConstrainEdgesNotInTriangles(HXTMesh* mesh, uint64_t* missing);
+
+
+/* flag is a 16-bit number
+ *
+ *  0  facet 0 is contrained
+ *  1  edge between facet 0 and facet 1 is contrained
+ *  2  edge betwwen facet 0 and facet 2 is contrained
+ *  3  edge betwwen facet 0 and facet 3 is contrained
+ *  4  facet 1 is contrained
+ *  5  
+ *  6  edge betwwen facet 1 and facet 2 is contrained
+ *  7  edge betwwen facet 1 and facet 3 is contrained
+ *  8  facet 2 is contrained
+ *  9 
+ *  10 
+ *  11 edge between facet 2 and facet 3 is contrained
+ *  12 facet 3 is contrained
+ *  13
+ *  14 the tetrahedron has already been processed (a vertex was already inserted inside it and if failed)
+ *  15 the tetrahedron is deleted
+ */
+
+
+static inline uint16_t isAnyEdgeConstrained(HXTMesh* mesh, uint64_t tet) {
+  return mesh->tetrahedra.flag[tet] & UINT16_C(0x8CE);
+}
+
+static inline uint16_t isAnyEdgeOfFacetConstrained(HXTMesh* mesh, uint64_t facet) {
+  static uint16_t mask[4] = {0xE, 0xC2, 0x844, 0x888};
+  return mesh->tetrahedra.flag[facet/4] & mask[facet%4];
+}
+
+// static inline uint16_t isAnyThingConstrained(HXTMesh* mesh, uint64_t tet) {
+//   return mesh->tetrahedra.flag[tet] & 0x19DF;
+// }
+
+static inline uint16_t isAnyFacetConstrained(HXTMesh* mesh, uint64_t tet) {
+  return mesh->tetrahedra.flag[tet] & UINT16_C(0x1111);
+}
+
+static inline uint16_t isEdgeConstrained(HXTMesh* mesh, uint64_t tet, unsigned facetmin, unsigned facetmax) {
+  return mesh->tetrahedra.flag[tet] & (1U<<(facetmin*4+facetmax));
+}
+
+static inline uint16_t isEdgeConstrainedSafe(HXTMesh* mesh, uint64_t tet, unsigned facet1, unsigned facet2) {
+  return mesh->tetrahedra.flag[tet] & (1U<<(facet1<facet2?facet1*4+facet2:facet2*4+facet1));
+}
+
+static inline void constrainEdge(HXTMesh* mesh, uint64_t tet, unsigned facetmin, unsigned facetmax) {
+  mesh->tetrahedra.flag[tet] |= (1U<<(facetmin*4+facetmax));
+}
+
+static inline void unconstrainEdge(HXTMesh* mesh, uint64_t tet, unsigned facetmin, unsigned facetmax) {
+  mesh->tetrahedra.flag[tet] &= ~(1U<<(facetmin*4+facetmax));
+}
+
+// here facet = 4*tet + facett
+static inline uint16_t isFacetConstrained(HXTMesh* mesh, uint64_t facet) {
+  return mesh->tetrahedra.flag[facet/4] & (1U<<(facet%4*4));
+}
+
+static inline void constrainFacet(HXTMesh* mesh, uint64_t facet) {
+  mesh->tetrahedra.flag[facet/4] |= (1U<<(facet%4*4));
+}
+
+static inline uint16_t isTetDeleted(HXTMesh* mesh, uint64_t tet) {
+  return mesh->tetrahedra.flag[tet] & (1U<<15);
+}
+
+static inline void markTetAsDeleted(HXTMesh* mesh, uint64_t tet) {
+  mesh->tetrahedra.flag[tet] |= (1U<<15);
+}
+
+static inline void unmarkTetAsDeleted(HXTMesh* mesh, uint64_t tet) {
+  mesh->tetrahedra.flag[tet] &= ~(1U<<15);
+}
+
+static inline uint16_t isTetProcessed(HXTMesh* mesh, uint64_t tet) {
+  return mesh->tetrahedra.flag[tet] & (1U<<14);
+}
+
+static inline void markTetAsProcessed(HXTMesh* mesh, uint64_t tet) {
+  mesh->tetrahedra.flag[tet] |= (1U<<14);
+}
+
+
+static inline void unmarkTetAsProcessed(HXTMesh* mesh, uint64_t tet) {
+  mesh->tetrahedra.flag[tet] &= ~(1U<<14);
+}
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/contrib/hxt/hxt_tetOpti.c b/contrib/hxt/hxt_tetOpti.c
new file mode 100644
index 0000000000000000000000000000000000000000..d5714451ead9d40c3536137152f3920538a761c2
--- /dev/null
+++ b/contrib/hxt/hxt_tetOpti.c
@@ -0,0 +1,1595 @@
+#include "hxt_tet_aspect_ratio.h"
+#include "hxt_mesh.h"
+#include "hxt_tetUtils.h"
+#include "predicates.h"
+#include "hxt_tetRepair.h"
+#include "hxt_tetFlag.h"
+#include "hxt_vertices.h"
+#include "hxt_tools.h"
+#include "hxt_sort.h"
+#include <float.h>
+
+#define NV -128               // not a valid entry
+#define HXT_MAX_CAVITY_SIZE 7 // because no cavity is has more than 7 tetrahedra while swapping
+#define DELETED_BUFFER_SIZE 8192
+#define SMALLEST_ROUND 1024
+
+/* usefull macros */
+#define MAX(x,y) ((x)>(y) ? (x) : (y))
+#define MIN(x,y) ((x)<(y) ? (x) : (y))
+#define SWAP(x,y) do{int tmp=x; x=y; y=tmp;}while(0)
+
+
+/*
+An oriented edge {up,down} is described by its 'in' and 'out' facets:
+       v_up
+       |\`-_
+       | \  `-_ 
+       |  \    `-_                out_facet
+       |   \      `-_              |
+       |    \   up   `-_          /
+       |     \  facet   `-_   <--'
+our    |      \            `-_
+edge   | in    \              `-_
+-----> | facet  \v_in___________`>v_out
+       |        /               _-'
+       |       /             _-'
+       |      /  down     _-'
+       |     /  facet  _-'
+       |    /       _-'
+       |   /     _-'
+       |  /   _-'
+       | / _-'
+       |/-'
+      v_down
+        
+    _-'
+   /
+   \    we scan tetrahedra in a counterclockwise order when viewed from up
+    `-__->
+
+
+ We keep the numbering of facets and the orientation of tetrahedra as before so:
+
+ mesh->tetrahedra.node[4*t + in_facet] = v_out
+ mesh->tetrahedra.node[4*t + out_facet] = v_in
+ mesh->tetrahedra.node[4*t + down_facet] = v_up
+ mesh->tetrahedra.node[4*t + up_facet] = v_down
+
+
+There are 12 possible oriented edges (in_facet!=out_facet)
+There are also 12 possibilities for a valid tetrahedra, each can be uniquely defined by an oriented edge...
+
+in_f=0 out_f=1 up_f=2 down_f=3    in_f=0 out_f=2 up_f=3 down_f=1     in_f=0 out_f=3 up_f=1 down_f=2
+      v_3                              v_1                              v_2                    
+       |\`-_                            |\`-_                            |\`-_                 
+       | \  `-_                         | \  `-_                         | \  `-_              
+       |  \    `-_                      |  \    `-_                      |  \    `-_           
+       |   \      `-_                   |   \      `-_                   |   \      `-_        
+       |    \        `-_                |    \        `-_                |    \        `-_     
+       |     \v_1_______`>v_0           |     \v_2_______`>v_0           |     \v_3_______`>v_0
+       |     /         _-'              |     /         _-'              |     /         _-'   
+       |    /       _-'                 |    /       _-'                 |    /       _-'      
+       |   /     _-'                    |   /     _-'                    |   /     _-'         
+       |  /   _-'                       |  /   _-'                       |  /   _-'            
+       | / _-'                          | / _-'                          | / _-'               
+       |/-'                             |/-'                             |/-'                  
+      v_2                              v_3                              v_1
+
+
+in_f=1 out_f=2 up_f=0 down_f=3    in_f=1 out_f=0 up_f=3 down_f=2     in_f=1 out_f=3 up_f=2 down_f=0
+      v_3                              v_2                              v_0                    
+       |\`-_                            |\`-_                            |\`-_                 
+       | \  `-_                         | \  `-_                         | \  `-_              
+       |  \    `-_                      |  \    `-_                      |  \    `-_           
+       |   \      `-_                   |   \      `-_                   |   \      `-_        
+       |    \        `-_                |    \        `-_                |    \        `-_     
+       |     \v_2_______`>v_1           |     \v_0_______`>v_1           |     \v_3_______`>v_1
+       |     /         _-'              |     /         _-'              |     /         _-'   
+       |    /       _-'                 |    /       _-'                 |    /       _-'      
+       |   /     _-'                    |   /     _-'                    |   /     _-'         
+       |  /   _-'                       |  /   _-'                       |  /   _-'            
+       | / _-'                          | / _-'                          | / _-'               
+       |/-'                             |/-'                             |/-'                  
+      v_0                              v_3                              v_2
+
+
+in_f=2 out_f=3 up_f=0 down_f=1    in_f=2 out_f=0 up_f=1 down_f=3     in_f=2 out_f=1 up_f=3 down_f=0
+      v_1                              v_3                              v_0                    
+       |\`-_                            |\`-_                            |\`-_                 
+       | \  `-_                         | \  `-_                         | \  `-_              
+       |  \    `-_                      |  \    `-_                      |  \    `-_           
+       |   \      `-_                   |   \      `-_                   |   \      `-_        
+       |    \        `-_                |    \        `-_                |    \        `-_     
+       |     \v_3_______`>v_2           |     \v_0_______`>v_2           |     \v_1_______`>v_2
+       |     /         _-'              |     /         _-'              |     /         _-'   
+       |    /       _-'                 |    /       _-'                 |    /       _-'      
+       |   /     _-'                    |   /     _-'                    |   /     _-'         
+       |  /   _-'                       |  /   _-'                       |  /   _-'            
+       | / _-'                          | / _-'                          | / _-'               
+       |/-'                             |/-'                             |/-'                  
+      v_0                              v_1                              v_3                    
+
+
+in_f=3 out_f=1 up_f=0 down_f=2    in_f=3 out_f=0 up_f=2 down_f=1     in_f=3 out_f=2 up_f=1 down_f=0
+      v_2                              v_1                              v_0                    
+       |\`-_                            |\`-_                            |\`-_                 
+       | \  `-_                         | \  `-_                         | \  `-_              
+       |  \    `-_                      |  \    `-_                      |  \    `-_           
+       |   \      `-_                   |   \      `-_                   |   \      `-_        
+       |    \        `-_                |    \        `-_                |    \        `-_     
+       |     \v_1_______`>v_3           |     \v_0_______`>v_3           |     \v_2_______`>v_3
+       |     /         _-'              |     /         _-'              |     /         _-'   
+       |    /       _-'                 |    /       _-'                 |    /       _-'      
+       |   /     _-'                    |   /     _-'                    |   /     _-'         
+       |  /   _-'                       |  /   _-'                       |  /   _-'            
+       | / _-'                          | / _-'                          | / _-'               
+       |/-'                             |/-'                             |/-'                  
+      v_0                              v_2                              v_1                    
+*/
+
+const int _UP_FACET[4][4] = {{NV, 2, 3, 1},
+                             { 3,NV, 0, 2},
+                             { 1, 3,NV, 0},
+                             { 2, 0, 1,NV}};
+
+#define UP_FACET(in_facet, out_facet) _UP_FACET[in_facet][out_facet]
+#define DOWN_FACET(in_facet, out_facet) _UP_FACET[out_facet][in_facet]
+
+#define UP_VERTEX(in_facet, out_facet) DOWN_FACET(in_facet, out_facet)
+#define DOWN_VERTEX(in_facet, out_facet) UP_FACET(in_facet, out_facet)
+#define IN_VERTEX(in_facet, out_facet) (out_facet)
+#define OUT_VERTEX(in_facet, out_facet) (in_facet)
+
+/* that's everything we need to handle edges !
+
+ NOW, we need patterns of triangulation around edges to make swaps
+ */
+
+typedef struct {
+  const unsigned char (*triangles)[3];              /* triangles array                                     */
+  const uint64_t *triangle_in_triangul;   /* in which triangulation is the triangles (bit array) */
+  const unsigned char (*triangulations)[5];         /* triangulation array                                 */
+  const signed char (*triangul_neigh)[20];        /* array to find adjacencies back                      */
+  const int num_triangles;                /* number of different triangles                       */
+  const int num_triangulations ;          /* number of different triangulations                  */
+  // const int num_triangles_per_triangulation; /* simply the number of nodes +2...              */
+} SwapPattern ;
+
+
+const unsigned char triangles3[][3] = { {0,1,2} };
+const uint64_t triangle_in_triangul3[] = { 1};
+const unsigned char triangulations3[][5] = {{0}};
+const signed char triangul_neigh3[][20] = {
+  {-2,-3,-1,NV,NV,NV,NV,NV,NV,NV,NV,NV,NV,NV,NV,NV,NV,NV,NV,NV}
+};
+
+
+const unsigned char triangles4[][3] = {
+  {0,1,2}, {2,3,0}, {1,2,3}, {3,0,1}
+};
+const uint64_t triangle_in_triangul4[] = { 1, 1, 2, 2};
+const unsigned char triangulations4[][5] = {
+  {0,1}, {2,3}
+};
+const signed char triangul_neigh4[][20] = {
+  {-2,5,-1,NV,-4,1,-3,NV,NV,NV,NV,NV,NV,NV,NV,NV,NV,NV,NV,NV},
+  {-3,5,-2,NV,-1,1,-4,NV,NV,NV,NV,NV,NV,NV,NV,NV,NV,NV,NV,NV}
+};
+
+
+
+const unsigned char triangles5[][3] = {
+  {0,1,2}, {0,2,3}, {0,3,4}, {0,1,4}, {1,3,4},
+  {1,2,3}, {2,3,4}, {0,2,4}, {0,1,3}, {1,2,4}
+};
+const uint64_t triangle_in_triangul5[] = {5, 1, 9, 18, 2, 10, 20, 4, 8, 16};
+const unsigned char triangulations5[][5] = { {0,1,2}, {3,4,5}, {0,6,7}, {2,5,8}, {3,6,9} };
+const signed char triangul_neigh5[][20] = {
+  {-2,6,-1,NV,-3,10,1,NV,-4,-5,5,NV,NV,NV,NV,NV,NV,NV,NV,NV},
+  {5,-5,-1,NV,-4,0,9,NV,-3,6,-2,NV,NV,NV,NV,NV,NV,NV,NV,NV},
+  {-2,10,-1,NV,-4,8,-3,NV,5,-5,1,NV,NV,NV,NV,NV,NV,NV,NV,NV},
+  {-4,-5,9,NV,-3,8,-2,NV,5,2,-1,NV,NV,NV,NV,NV,NV,NV,NV,NV},
+  {9,-5,-1,NV,-4,8,-3,NV,5,0,-2,NV,NV,NV,NV,NV,NV,NV,NV,NV}
+};
+
+
+const unsigned char triangles6[][3] = {
+  {0,1,2}, {0,2,3}, {0,3,4}, {0,4,5}, {0,2,5}, {2,4,5}, {2,3,4}, {0,3,5},
+  {3,4,5}, {0,2,4}, {2,3,5}, {1,2,3}, {0,1,3}, {0,1,5}, {1,4,5}, {1,3,4},
+  {0,1,4}, {1,3,5}, {1,2,4}, {1,2,5}
+};
+const uint64_t triangle_in_triangul6[] = {
+  31, 5, 33, 2345, 18, 4098, 7178, 132, 8852, 8, 8208,
+  992, 160, 13888, 1088, 320, 2304, 512, 3072, 12288};
+const unsigned char triangulations6[][5] = {
+  {0,1,2,3}, {0,4,5,6}, {0,1,7,8}, {0,3,6,9}, {0,4,8,10},
+  {2,3,11,12}, {11,13,14,15}, {7,8,11,12}, {3,11,15,16},
+  {8,11,13,17}, {6,13,14,18}, {3,6,16,18}, {5,6,13,19},
+  {8,10,13,19}
+};
+const signed char triangul_neigh6[][20] = {
+  {-2,6,-1,NV,-3,10,1,NV,-4,14,5,NV,-5,-6,9,NV,NV,NV,NV,NV},
+  {-2,6,-1,NV,9,-6,1,NV,-5,4,13,NV,-4,10,-3,NV,NV,NV,NV,NV},
+  {-2,6,-1,NV,-3,10,1,NV,13,-6,5,NV,-5,8,-4,NV,NV,NV,NV,NV},
+  {-2,14,-1,NV,-5,-6,13,NV,-4,12,-3,NV,9,6,1,NV,NV,NV,NV,NV},
+  {-2,6,-1,NV,13,-6,1,NV,-5,12,-4,NV,9,4,-3,NV,NV,NV,NV,NV},
+  {-4,6,13,NV,-5,-6,1,NV,-3,12,-2,NV,9,2,-1,NV,NV,NV,NV,NV},
+  {-3,14,-2,NV,9,-6,-1,NV,-5,4,13,NV,-4,10,1,NV,NV,NV,NV,NV},
+  {5,-6,13,NV,-5,0,-4,NV,-3,12,-2,NV,9,2,-1,NV,NV,NV,NV,NV},
+  {-5,-6,13,NV,-3,10,-2,NV,-4,12,5,NV,9,2,-1,NV,NV,NV,NV,NV},
+  {-5,12,-4,NV,-3,14,-2,NV,13,-6,-1,NV,1,8,5,NV,NV,NV,NV,NV},
+  {-4,12,-3,NV,9,-6,-1,NV,-5,4,13,NV,1,10,-2,NV,NV,NV,NV,NV},
+  {-5,-6,9,NV,-4,12,-3,NV,13,2,-1,NV,5,8,-2,NV,NV,NV,NV,NV},
+  {-5,12,5,NV,-4,2,-3,NV,13,-6,-1,NV,1,8,-2,NV,NV,NV,NV,NV},
+  {-5,4,-4,NV,1,12,-3,NV,13,-6,-1,NV,5,8,-2,NV,NV,NV,NV,NV}
+};
+
+
+const unsigned char triangles7[][3] = { 
+  {0,1,2}, {0,2,3}, {0,3,4}, {0,4,5}, {0,5,6}, {0,3,6}, {3,5,6}, {3,4,5}, {0,4,6},
+  {4,5,6}, {0,3,5}, {3,4,6}, {0,2,4}, {2,3,4}, {0,2,6}, {2,5,6}, {2,4,5}, {0,2,5},
+  {2,4,6}, {2,3,5}, {2,3,6}, {0,1,3}, {1,2,3}, {0,1,4}, {1,3,4}, {0,1,6}, {1,5,6},
+  {1,4,5}, {0,1,5}, {1,4,6}, {1,3,5}, {1,3,6}, {1,2,4}, {1,2,5}, {1,2,6}
+};
+const uint64_t triangle_in_triangul7[] = {
+  16383, 31, 81925, 268976161, 294512118057, 294930, 1099578773506, 2061701913610,
+  1075904644, 2273256481428, 131080, 2199157743632, 160, 137170519008, 13888, 584115553344,
+  60129542464, 2304, 68719477248, 962072677376, 3298534895616, 507904, 268419072, 1344798720,
+  16252928, 4102458179584, 146583584768, 2689597440, 294243008512, 4303355904, 50331648, 201326592,
+  8321499136, 438086664192, 3951369912320
+ };
+const unsigned char triangulations7[][5] = {
+  {0,1,2,3,4}, {0,1,5,6,7}, {0,1,2,8,9}, {0,1,4,7,10}, {0,1,5,9,11}, {0,3,4,12,13},
+  {0,13,14,15,16}, {0,8,9,12,13}, {0,4,13,16,17}, {0,9,13,14,18}, {0,7,14,15,19},
+  {0,4,7,17,19}, {0,6,7,14,20}, {0,9,11,14,20}, {2,3,4,21,22}, {5,6,7,21,22},
+  {2,8,9,21,22}, {4,7,10,21,22}, {5,9,11,21,22}, {3,4,22,23,24}, {22,24,25,26,27},
+  {8,9,22,23,24}, {4,22,24,27,28}, {9,22,24,25,29}, {7,22,25,26,30}, {4,7,22,28,30},
+  {6,7,22,25,31}, {9,11,22,25,31}, {3,4,13,23,32}, {13,25,26,27,32}, {8,9,13,23,32},
+  {4,13,27,28,32}, {9,13,25,29,32}, {13,16,25,26,33}, {4,13,16,28,33},
+  {13,15,16,25,34}, {9,13,18,25,34}, {7,19,25,26,33}, {4,7,19,28,33},
+  {7,15,19,25,34}, {6,7,20,25,34}, {9,11,20,25,34}
+};
+const signed char triangul_neigh7[][20] = {
+  {-2,6,-1,NV,-3,10,1,NV,-4,14,5,NV,-5,18,9,NV,-6,-7,13,NV},
+  {-2,6,-1,NV,-3,10,1,NV,13,-7,5,NV,-6,8,17,NV,-5,14,-4,NV},
+  {-2,6,-1,NV,-3,10,1,NV,-4,14,5,NV,17,-7,9,NV,-6,12,-5,NV},
+  {-2,6,-1,NV,-3,18,1,NV,-6,-7,17,NV,-5,16,-4,NV,13,10,5,NV},
+  {-2,6,-1,NV,-3,10,1,NV,17,-7,5,NV,-6,16,-5,NV,13,8,-4,NV},
+  {-2,14,-1,NV,-5,10,13,NV,-6,-7,5,NV,17,6,1,NV,-4,12,-3,NV},
+  {-2,10,-1,NV,-4,18,-3,NV,13,-7,1,NV,-6,8,17,NV,-5,14,5,NV},
+  {-2,14,-1,NV,9,-7,13,NV,-6,4,-5,NV,17,6,1,NV,-4,12,-3,NV},
+  {-2,18,-1,NV,-6,-7,17,NV,-4,14,-3,NV,-5,16,9,NV,13,6,1,NV},
+  {-2,14,-1,NV,-6,16,-5,NV,-4,18,-3,NV,17,-7,1,NV,5,12,9,NV},
+  {-2,10,-1,NV,-5,16,-4,NV,13,-7,1,NV,-6,8,17,NV,5,14,-3,NV},
+  {-2,14,-1,NV,-6,-7,13,NV,-5,16,-4,NV,17,6,1,NV,9,12,-3,NV},
+  {-2,14,-1,NV,-6,16,9,NV,-5,6,-4,NV,17,-7,1,NV,5,12,-3,NV},
+  {-2,14,-1,NV,-6,8,-5,NV,5,16,-4,NV,17,-7,1,NV,9,12,-3,NV},
+  {-4,6,13,NV,-5,10,1,NV,-6,-7,5,NV,17,2,-1,NV,-3,12,-2,NV},
+  {5,-7,13,NV,-6,0,9,NV,-5,6,-4,NV,17,2,-1,NV,-3,12,-2,NV},
+  {-4,6,13,NV,9,-7,1,NV,-6,4,-5,NV,17,2,-1,NV,-3,12,-2,NV},
+  {-6,-7,9,NV,-5,8,-4,NV,5,2,13,NV,17,10,-1,NV,-3,12,-2,NV},
+  {9,-7,13,NV,-6,8,-5,NV,5,0,-4,NV,17,2,-1,NV,-3,12,-2,NV},
+  {-5,6,13,NV,-6,-7,1,NV,-3,18,-2,NV,17,2,-1,NV,-4,12,9,NV},
+  {-3,6,-2,NV,-4,18,1,NV,13,-7,-1,NV,-6,8,17,NV,-5,14,5,NV},
+  {5,-7,13,NV,-6,0,-5,NV,-3,18,-2,NV,17,2,-1,NV,-4,12,9,NV},
+  {-6,-7,17,NV,-3,10,-2,NV,-4,14,5,NV,-5,16,9,NV,13,2,-1,NV},
+  {-6,16,-5,NV,-3,10,-2,NV,-4,18,5,NV,17,-7,-1,NV,1,12,9,NV},
+  {-5,16,-4,NV,-3,18,-2,NV,13,-7,-1,NV,-6,8,17,NV,1,14,5,NV},
+  {-6,-7,13,NV,-5,16,-4,NV,-3,18,-2,NV,17,2,-1,NV,5,12,9,NV},
+  {-6,16,5,NV,-5,2,-4,NV,-3,18,-2,NV,17,-7,-1,NV,1,12,9,NV},
+  {-6,4,-5,NV,1,16,-4,NV,-3,18,-2,NV,17,-7,-1,NV,5,12,9,NV},
+  {-5,6,13,NV,-6,-7,1,NV,-4,16,-3,NV,17,2,-1,NV,9,12,-2,NV},
+  {-4,16,-3,NV,9,-7,-1,NV,-6,4,13,NV,-5,10,17,NV,1,14,-2,NV},
+  {5,-7,13,NV,-6,0,-5,NV,-4,16,-3,NV,17,2,-1,NV,9,12,-2,NV},
+  {-6,-7,13,NV,-4,16,-3,NV,-5,12,17,NV,9,2,-1,NV,5,10,-2,NV},
+  {-6,12,-5,NV,-4,16,-3,NV,13,-7,-1,NV,1,8,17,NV,5,14,-2,NV},
+  {-4,6,-3,NV,-5,16,1,NV,13,-7,-1,NV,-6,8,17,NV,5,14,-2,NV},
+  {-6,-7,13,NV,-4,10,-3,NV,-5,16,5,NV,17,2,-1,NV,9,12,-2,NV},
+  {-4,10,-3,NV,-6,16,9,NV,-5,6,1,NV,17,-7,-1,NV,5,12,-2,NV},
+  {-6,8,-5,NV,-4,10,-3,NV,1,16,5,NV,17,-7,-1,NV,9,12,-2,NV},
+  {-5,4,-4,NV,1,16,-3,NV,13,-7,-1,NV,-6,8,17,NV,5,14,-2,NV},
+  {-6,-7,13,NV,-5,8,-4,NV,5,16,-3,NV,17,2,-1,NV,9,12,-2,NV},
+  {-5,8,-4,NV,-6,16,9,NV,1,6,-3,NV,17,-7,-1,NV,5,12,-2,NV},
+  {-6,8,5,NV,-5,2,-4,NV,1,16,-3,NV,17,-7,-1,NV,9,12,-2,NV},
+  {-6,4,-5,NV,1,8,-4,NV,5,16,-3,NV,17,-7,-1,NV,9,12,-2,NV}
+};
+
+
+SwapPattern patterns[8] = {
+  {},{},{},
+  {
+    // pattern with 3 points around edge  | 3 tetra in, 2 tetra out
+    .triangles = triangles3,
+    .num_triangles = 1,
+    .triangulations = triangulations3,
+    .num_triangulations = 1,
+    .triangul_neigh = triangul_neigh3,
+    .triangle_in_triangul = triangle_in_triangul3
+  },
+
+  {
+    // pattern with 4 points around edge  | 4 tetra in, 4 tetra out
+    .triangles = triangles4,
+    .num_triangles = 4,
+    .triangulations = triangulations4,
+    .num_triangulations = 2,
+    .triangul_neigh = triangul_neigh4,
+    .triangle_in_triangul = triangle_in_triangul4
+  },
+
+  {
+    // pattern with 5 points around edge  | 5 tetra in, 6 tetra out
+    .triangles = triangles5,
+    .num_triangles = 10,
+    .triangulations = triangulations5,
+    .num_triangulations = 5,
+    .triangul_neigh = triangul_neigh5,
+    .triangle_in_triangul = triangle_in_triangul5
+  },
+
+  {
+    // pattern with 6 points around edge
+    .triangles = triangles6,
+    .num_triangles = 20,
+    .triangulations = triangulations6,
+    .num_triangulations = 14,
+    .triangul_neigh = triangul_neigh6,
+    .triangle_in_triangul = triangle_in_triangul6
+  },
+
+  {
+    // pattern with 7 points around edge
+    .triangles = triangles7,
+    .num_triangles = 35,
+    .triangulations = triangulations7,
+    .num_triangulations = 42, // see Catalan numbers ;)
+    .triangul_neigh = triangul_neigh7,
+    .triangle_in_triangul = triangle_in_triangul7
+  }
+};
+
+
+/**************** that's it for the DATAs ****************/
+
+typedef struct {
+  struct {
+    HXTGroup2* tetID_dist;
+    uint64_t num;
+    uint64_t size;
+  } badTets;
+
+  struct {
+    double* values;
+    double threshold;
+  } quality2;
+} ThreadShared;
+
+typedef struct 
+{
+  struct {
+    uint64_t neigh_up  [HXT_MAX_CAVITY_SIZE];
+    uint64_t neigh_down[HXT_MAX_CAVITY_SIZE];
+    unsigned char flag [HXT_MAX_CAVITY_SIZE];
+    uint32_t annulus   [HXT_MAX_CAVITY_SIZE];
+    uint32_t v_up;
+    uint32_t v_down;
+    uint32_t num;
+  } cavity;
+
+  struct {
+    uint64_t* tetID;
+    uint64_t num;
+    uint64_t size;
+  } deleted;
+
+  struct {
+    uint64_t startDist;
+    uint64_t endDist;
+    uint32_t first;
+    uint32_t length;
+  } partition;
+} ThreadLocal;
+
+
+
+/*
+ Compute the squared aspect ratio of a tet. quickly return 0.0 if the tet is inverted
+*/
+static inline double tetQuality2 (HXTMesh *mesh, uint32_t p0, uint32_t p1, uint32_t p2, uint32_t p3)
+{
+  return hxtTetAspectFastRatio (&mesh->vertices.coord[4*p0],
+                                &mesh->vertices.coord[4*p1],
+                                &mesh->vertices.coord[4*p2],
+                                &mesh->vertices.coord[4*p3]);
+}
+
+/***********************************************
+           re-allocation functions
+ ***********************************************/
+static HXTStatus synchronizeReallocation(HXTMesh* mesh, ThreadShared* shared, volatile int* toCopy, volatile int* copy){
+  // threads cant be doing something while the realloc portion happen
+  #pragma omp barrier
+  
+  // this unable us to have the same value of toCopy for everyone, as we are sure nothing happens to those variables here
+  if(toCopy!=copy){
+    *copy = *toCopy;
+  }
+
+  HXTStatus status = HXT_STATUS_OK;
+  // make reallocations in a critical section
+  #pragma omp single
+  {
+    if(mesh->tetrahedra.num > mesh->tetrahedra.size){
+      status = hxtAlignedRealloc(&shared->quality2.values, sizeof(double)*mesh->tetrahedra.num);
+      if(status==HXT_STATUS_OK) status = hxtTetrahedraReserve(mesh, mesh->tetrahedra.num);
+    }
+  } // implicit barrier here
+
+  if(status!=HXT_STATUS_OK)
+    HXT_TRACE(status);
+  
+  return status;
+}
+
+static inline HXTStatus reserveNewDeleted(ThreadLocal* local, uint64_t num){
+  num += local->deleted.num;
+  if(num > local->deleted.size){
+      HXT_CHECK( hxtAlignedRealloc(&local->deleted.tetID, 2*num*sizeof(uint64_t)) );
+      local->deleted.size = 2*num;
+  }
+
+  return HXT_STATUS_OK;
+}
+
+
+static inline HXTStatus createNewDeleted(HXTMesh* mesh, ThreadShared* shared, ThreadLocal* local) {
+  uint64_t needed = DELETED_BUFFER_SIZE-local->deleted.num;
+
+  uint64_t ntet;
+
+  // TODO: when multi-threading, don't forget atomic operation and critical sections
+  #pragma omp atomic capture
+  { ntet = mesh->tetrahedra.num; mesh->tetrahedra.num+=needed;}
+
+  HXT_CHECK( synchronizeReallocation(mesh, shared, NULL, NULL) );
+  HXT_CHECK( reserveNewDeleted(local, needed) );
+
+  #pragma omp simd
+  for (uint64_t i=0; i<needed; i++){
+    local->deleted.tetID[local->deleted.num+i] = ntet+i;
+    shared->quality2.values[ntet+i] = DBL_MAX;
+    // markTetAsDeleted(mesh, ntet+i);
+    // printf("adding tet %lu to deleted[%lu]\n", ntet+i, local->deleted.num+i);
+  }
+
+  local->deleted.num = DELETED_BUFFER_SIZE;
+  return HXT_STATUS_OK;
+}
+
+// suppose that the right capacity for quality2 values is already allocated
+static HXTStatus threadShared_update(HXTMesh* mesh, ThreadShared* shared) {
+  int maxThreads = omp_get_max_threads();
+  HXTStatus status = HXT_STATUS_OK;
+
+  uint64_t* badTetsCount;
+  HXT_CHECK( hxtMalloc(&badTetsCount, maxThreads*sizeof(uint64_t)) );
+
+  #pragma omp parallel
+  {
+    int threadID = omp_get_thread_num();
+    badTetsCount[threadID] = 0;
+
+    #pragma omp for schedule(static)
+    for (int i=0; i<mesh->tetrahedra.num; i++) {
+      if(mesh->tetrahedra.colors[i]!=UINT16_MAX && shared->quality2.values[i]<shared->quality2.threshold)
+        badTetsCount[threadID]++;
+    }
+
+    #pragma omp barrier
+    #pragma omp single
+    {
+      int nthreads = omp_get_num_threads();
+      shared->badTets.num = 0;
+      for (int i=0; i<nthreads; i++) {
+        uint64_t tsum = badTetsCount[i] + shared->badTets.num;
+        badTetsCount[i] = shared->badTets.num;
+        shared->badTets.num = tsum;
+      }
+
+      if(shared->badTets.num > shared->badTets.size){
+        status = hxtAlignedRealloc(&shared->badTets.tetID_dist, sizeof(HXTGroup2)*shared->badTets.num);
+        shared->badTets.size = shared->badTets.num;
+      }
+    }
+
+    if(status==HXT_STATUS_OK){
+      #pragma omp for schedule(static)
+      for (uint64_t i=0; i<mesh->tetrahedra.num; i++) {
+        if(mesh->tetrahedra.colors[i]!=UINT16_MAX && shared->quality2.values[i]<shared->quality2.threshold){
+          uint64_t badTetsID = badTetsCount[threadID]++;
+          shared->badTets.tetID_dist[badTetsID].v[1] = i;
+        }
+      }
+    }
+  }
+  HXT_CHECK( hxtFree(&badTetsCount) );
+
+  return status;
+}
+
+
+static HXTStatus threadShared_create(HXTMesh *mesh, double qualityThreshold, ThreadShared** shared) {
+  ThreadShared* newShared;
+  HXT_CHECK( hxtMalloc(&newShared, sizeof(ThreadShared)));
+
+  if(qualityThreshold<0.0)
+    return HXT_ERROR_MSG(HXT_STATUS_ERROR, "quality threshold must be positive");
+
+  newShared->badTets.num = 0;
+  newShared->badTets.size = 0;
+  newShared->quality2.threshold = qualityThreshold*qualityThreshold/24.;
+  HXT_CHECK( hxtAlignedMalloc(&newShared->quality2.values, sizeof(double)*mesh->tetrahedra.size) );
+  newShared->badTets.tetID_dist = NULL;
+
+  #pragma omp parallel for
+  for (uint64_t i=0; i<mesh->tetrahedra.num; i++) {
+    uint32_t* nodes = mesh->tetrahedra.node + 4*i;
+    if(nodes[3]==HXT_GHOST_VERTEX)
+      newShared->quality2.values[i] = DBL_MAX; // exterior tetrahedra have maximum quality
+    else
+      newShared->quality2.values[i] = tetQuality2(mesh, nodes[0], nodes[1], nodes[2], nodes[3]);
+  }
+
+  *shared = newShared;
+  return HXT_STATUS_OK;
+}
+
+
+
+static HXTStatus threadShared_destroy(ThreadShared** shared) {
+  HXT_CHECK( hxtAlignedFree(&(*shared)->quality2.values) );
+  HXT_CHECK( hxtAlignedFree(&(*shared)->badTets.tetID_dist) );
+  HXT_CHECK( hxtFree(shared) );
+  return HXT_STATUS_OK;
+}
+
+
+static inline int vertexOutOfPartition(HXTVertex* vertices, uint32_t v, uint64_t rel, uint64_t startDist) {
+  return vertices[v].padding.hilbertDist - startDist >= rel;
+}
+
+
+static HXTStatus threadLocals_create(ThreadLocal** locals_ptr, int nthreads) {
+  ThreadLocal* newLocal;
+  HXT_CHECK( hxtMalloc(&newLocal, nthreads*sizeof(ThreadLocal)));
+
+  for (int threadID=0; threadID<nthreads; threadID++) {
+    newLocal[threadID].deleted.num = 0;
+    newLocal[threadID].deleted.size = DELETED_BUFFER_SIZE;
+    HXT_CHECK( hxtAlignedMalloc(&newLocal[threadID].deleted.tetID,
+                                sizeof(uint64_t)*DELETED_BUFFER_SIZE) );
+  }
+
+  *locals_ptr = newLocal;
+  return HXT_STATUS_OK;
+}
+
+static HXTStatus threadLocals_update(HXTMesh* mesh, HXTBbox* bbox, double minSize,
+                                     ThreadShared* shared, ThreadLocal* locals,
+                                     int nthreads, uint32_t* seed, int changePartitions, uint64_t* nConflict_ptr, uint32_t* nbits) {
+
+  if(nthreads>1) {
+    HXTVertex* vertices = (HXTVertex*) mesh->vertices.coord;
+    HXTGroup2* badTets = shared->badTets.tetID_dist;
+
+    double indexShift_percent = (double) hxtReproducibleLCG(seed)/RAND_MAX;
+
+    if(changePartitions) {
+      double hxtDeclareAligned bboxShift[3];
+
+      bboxShift[0] = (double) hxtReproducibleLCG(seed)/RAND_MAX;
+      bboxShift[1] = (double) hxtReproducibleLCG(seed)/RAND_MAX;
+      bboxShift[2] = (double) hxtReproducibleLCG(seed)/RAND_MAX;
+
+      *nbits = hxtAdvancedHilbertBits(bbox, minSize, minSize,
+                                      mesh->vertices.num,
+                                      mesh->vertices.num,
+                                      mesh->vertices.num,
+                                      shared->badTets.num,
+                                      nthreads);
+
+      HXT_CHECK( hxtVerticesHilbertDist(bbox, vertices, mesh->vertices.num, nbits, bboxShift) );
+    }
+
+    #pragma omp parallel for simd aligned(badTets:SIMD_ALIGN)
+    for (uint64_t i=0; i<shared->badTets.num; i++) {
+      uint32_t firstNode = mesh->tetrahedra.node[4*badTets[i].v[1]];
+      badTets[i].v[0] = vertices[firstNode].padding.hilbertDist;
+    }
+
+    // sort the bad tetrahedrons following their first node hilbert dist
+    HXT_CHECK(  group2_sort_v0(badTets, shared->badTets.num, (UINT64_C(1)<<(*nbits)) - 1) );
+
+    const uint64_t step = shared->badTets.num/nthreads;
+    uint64_t indexShift = MIN(step-1,(uint64_t) indexShift_percent*step);
+    uint64_t nConflict = 0;
+
+    #pragma omp parallel num_threads(nthreads) reduction(+:nConflict)
+    {
+      const int threadID = omp_get_thread_num();
+
+      uint64_t first = step*threadID + indexShift;
+      uint64_t startDist = badTets[first].v[0];
+
+      uint64_t up = 1;
+      while(first+up<shared->badTets.num && startDist==badTets[first + up].v[0])
+        up++;
+
+      first = first+up==shared->badTets.num?0:first+up;
+      if(first > 0)
+        startDist = (badTets[first].v[0]
+                                              + badTets[first - 1].v[0] + 1)/2;
+      else
+        startDist = badTets[shared->badTets.num-1].v[0] + 
+                            (badTets[first].v[0] - badTets[shared->badTets.num - 1].v[0])/2;
+
+      locals[threadID].partition.first = first;
+      locals[threadID].partition.startDist = startDist;
+
+      #pragma omp barrier
+
+      uint64_t firstNext = locals[(threadID+1)%nthreads].partition.first;
+      uint64_t endDist = locals[(threadID+1)%nthreads].partition.startDist;
+      locals[threadID].partition.length = (firstNext + shared->badTets.num - first)%shared->badTets.num;
+      locals[threadID].partition.endDist = endDist;
+
+      uint64_t rel = startDist - endDist;
+
+      // dismiss tetrahedron that are in our list but not in our partition
+      for (uint64_t i=0; i<locals[threadID].partition.length; i++) {
+        uint64_t index = (locals[threadID].partition.first + i)%shared->badTets.num;
+        uint64_t curTet = shared->badTets.tetID_dist[index].v[1];
+
+        uint32_t* nodes = mesh->tetrahedra.node + 4*curTet;
+
+        if(vertexOutOfPartition(vertices, nodes[0], rel, startDist) + 
+           vertexOutOfPartition(vertices, nodes[1], rel, startDist) + 
+           vertexOutOfPartition(vertices, nodes[2], rel, startDist) + 
+           vertexOutOfPartition(vertices, nodes[3], rel, startDist) > 1) {
+          shared->badTets.tetID_dist[index].v[1] = HXT_NO_ADJACENT;
+          nConflict++;
+        }
+      }
+    }
+
+    *nConflict_ptr+=nConflict;
+  }
+  else {
+    locals[0].partition.startDist = 0;
+    locals[0].partition.endDist = UINT64_MAX;
+    locals[0].partition.first = 0;
+    locals[0].partition.length = shared->badTets.num;
+  }
+
+  return HXT_STATUS_OK;
+}
+
+
+static HXTStatus threadLocals_destroy(ThreadLocal** local, int nthreads) {
+  for (int threadID=0; threadID<nthreads; threadID++) {
+    HXT_CHECK( hxtAlignedFree(&(*local)[threadID].deleted.tetID) );
+  }
+  HXT_CHECK( hxtFree(local) );
+  return HXT_STATUS_OK;
+}
+
+
+static HXTStatus flip2_3(HXTMesh* mesh, ThreadShared* shared, ThreadLocal* local,
+                         const uint64_t tet_0, uint16_t color, unsigned out_facet_0)
+{
+  if(isFacetConstrained(mesh, 4*tet_0 + out_facet_0) || mesh->tetrahedra.neigh[4*tet_0 + out_facet_0]==HXT_NO_ADJACENT)
+    return HXT_STATUS_INTERNAL;
+
+  uint64_t neigh = mesh->tetrahedra.neigh[4*tet_0 + out_facet_0];
+  
+
+  uint64_t tet_1 = neigh/4;
+
+  HXT_ASSERT(tet_1<mesh->tetrahedra.num);
+  HXT_ASSERT(tet_1!=tet_0);
+  HXT_ASSERT(mesh->tetrahedra.neigh[neigh]==4*tet_0 + out_facet_0);
+  unsigned in_facet_1 = neigh%4;
+
+  { // check for conflict with other threads'partition
+    const uint64_t startDist = local->partition.startDist;
+    const uint64_t rel = local->partition.endDist - startDist;
+    HXTVertex* vertices = (HXTVertex*) mesh->vertices.coord;
+    if(vertexOutOfPartition(vertices, mesh->tetrahedra.node[4*tet_1 + 0], rel, startDist) +
+       vertexOutOfPartition(vertices, mesh->tetrahedra.node[4*tet_1 + 1], rel, startDist) +
+       vertexOutOfPartition(vertices, mesh->tetrahedra.node[4*tet_1 + 2], rel, startDist) +
+       vertexOutOfPartition(vertices, mesh->tetrahedra.node[4*tet_1 + 3], rel, startDist) > 1)
+       return HXT_STATUS_CONFLICT;
+   }
+
+  double worst_qual = shared->quality2.values[tet_0];
+
+  if(shared->quality2.values[tet_1]<worst_qual)
+    worst_qual = shared->quality2.values[tet_1];
+
+  local->cavity.v_up = mesh->tetrahedra.node[4*tet_0 + out_facet_0];
+  local->cavity.v_down = mesh->tetrahedra.node[4*tet_1 + in_facet_1];
+
+  // choose a reference facet in the tet_0
+  unsigned in_facet_0 = (out_facet_0+1)%4;
+
+  // adding vertices of the annulus:
+  local->cavity.annulus[0] = mesh->tetrahedra.node[4*tet_0 + UP_VERTEX(in_facet_0, out_facet_0)];
+  local->cavity.annulus[1] = mesh->tetrahedra.node[4*tet_0 + DOWN_VERTEX(in_facet_0, out_facet_0)];
+  local->cavity.annulus[2] = mesh->tetrahedra.node[4*tet_0 + OUT_VERTEX(in_facet_0, out_facet_0)];
+
+  local->cavity.neigh_up[0] = mesh->tetrahedra.neigh[4*tet_0 + in_facet_0];
+  local->cavity.neigh_up[1] = mesh->tetrahedra.neigh[4*tet_0 + DOWN_FACET(in_facet_0, out_facet_0)];
+  local->cavity.neigh_up[2] = mesh->tetrahedra.neigh[4*tet_0 + UP_FACET(in_facet_0, out_facet_0)];
+
+  uint32_t v = local->cavity.annulus[2];
+
+  // find one of the vertex in the tet_1
+  uint32_t* nodes = mesh->tetrahedra.node + 4*tet_1;
+
+  unsigned out_facet_1;
+  for (out_facet_1=0; out_facet_1<4; out_facet_1++)
+      if(nodes[out_facet_1]==v)
+        break;
+
+  // HXT_ASSERT(out_facet_1!=4);
+  // HXT_ASSERT(out_facet_1!=in_facet_1);
+  // HXT_ASSERT((isEdgeConstrainedSafe(mesh, tet_0, in_facet_0, out_facet_0)!=0)==(isEdgeConstrainedSafe(mesh, tet_1, in_facet_1, out_facet_1)!=0));
+  // HXT_ASSERT((isEdgeConstrainedSafe(mesh, tet_0, in_facet_0, out_facet_0)!=0)==(isEdgeConstrainedSafe(mesh, tet_1, in_facet_1, out_facet_1)!=0));
+  // HXT_ASSERT((isEdgeConstrainedSafe(mesh, tet_0, DOWN_FACET(in_facet_0, out_facet_0), out_facet_0)!=0)
+  //          ==(isEdgeConstrainedSafe(mesh, tet_1, DOWN_FACET(in_facet_1, out_facet_1), in_facet_1)!=0));
+  // HXT_ASSERT((isEdgeConstrainedSafe(mesh, tet_0, UP_FACET(in_facet_0, out_facet_0), out_facet_0)!=0)
+  //          ==(isEdgeConstrainedSafe(mesh, tet_1, UP_FACET(in_facet_1, out_facet_1), in_facet_1)!=0));
+  // HXT_ASSERT((isFacetConstrained(mesh, 4*tet_0 + out_facet_0)!=0)==(isFacetConstrained(mesh, 4*tet_1 + in_facet_1)!=0));
+  // if(mesh->tetrahedra.neigh[neigh]!=4*tet_0+out_facet_0)
+  //   return HXT_ERROR_MSG(HXT_STATUS_ERROR, "mesh->tetrahedra.neigh[%lu]==%lu instead of %lu",neigh,mesh->tetrahedra.neigh[neigh],4*tet_0+out_facet_0);
+
+  local->cavity.neigh_down[0] = mesh->tetrahedra.neigh[4*tet_1 + out_facet_1];
+  local->cavity.neigh_down[1] = mesh->tetrahedra.neigh[4*tet_1 + DOWN_FACET(in_facet_1, out_facet_1)];
+  local->cavity.neigh_down[2] = mesh->tetrahedra.neigh[4*tet_1 + UP_FACET(in_facet_1, out_facet_1)];
+
+  local->cavity.flag[0] = ((isFacetConstrained(mesh, 4*tet_0 + in_facet_0)!=0)<<12) +
+                          ((isFacetConstrained(mesh, 4*tet_1 + out_facet_1)!=0)<<8) +
+                          ((isEdgeConstrainedSafe(mesh, tet_1, out_facet_1, DOWN_FACET(in_facet_1, out_facet_1))!=0)<<2) +
+                          ((isEdgeConstrainedSafe(mesh, tet_0, in_facet_0, DOWN_FACET(in_facet_0, out_facet_0))!=0)<<3) +
+                          ((isEdgeConstrainedSafe(mesh, tet_1, out_facet_1, UP_FACET(in_facet_1, out_facet_1))!=0)<<6) +
+                          ((isEdgeConstrainedSafe(mesh, tet_0, in_facet_0, UP_FACET(in_facet_0, out_facet_0))!=0)<<7) +
+                          ((isEdgeConstrainedSafe(mesh, tet_0, in_facet_0, out_facet_0)!=0)<<11);
+
+  local->cavity.flag[1] = ((isFacetConstrained(mesh, 4*tet_0 + DOWN_FACET(in_facet_0, out_facet_0))!=0)<<12) +
+                          ((isFacetConstrained(mesh, 4*tet_1 + DOWN_FACET(in_facet_1, out_facet_1))!=0)<<8) +
+                          ((isEdgeConstrainedSafe(mesh, tet_1, DOWN_FACET(in_facet_1, out_facet_1), UP_FACET(in_facet_1, out_facet_1))!=0)<<2) +
+                          ((isEdgeConstrainedSafe(mesh, tet_0, DOWN_FACET(in_facet_0, out_facet_0), UP_FACET(in_facet_0, out_facet_0))!=0)<<3) +
+                          ((isEdgeConstrainedSafe(mesh, tet_1, DOWN_FACET(in_facet_1, out_facet_1), out_facet_1)!=0)<<6) +
+                          ((isEdgeConstrainedSafe(mesh, tet_0, DOWN_FACET(in_facet_0, out_facet_0), in_facet_0)!=0)<<7) +
+                          ((isEdgeConstrainedSafe(mesh, tet_0, DOWN_FACET(in_facet_0, out_facet_0), out_facet_0)!=0)<<11);
+
+  local->cavity.flag[2] = ((isFacetConstrained(mesh, 4*tet_0 + UP_FACET(in_facet_0, out_facet_0))!=0)<<12) +
+                          ((isFacetConstrained(mesh, 4*tet_1 + UP_FACET(in_facet_1, out_facet_1))!=0)<<8) +
+                          ((isEdgeConstrainedSafe(mesh, tet_1, UP_FACET(in_facet_1, out_facet_1), out_facet_1)!=0)<<2) +
+                          ((isEdgeConstrainedSafe(mesh, tet_0, UP_FACET(in_facet_0, out_facet_0), in_facet_0)!=0)<<3) +
+                          ((isEdgeConstrainedSafe(mesh, tet_1, UP_FACET(in_facet_1, out_facet_1), DOWN_FACET(in_facet_1, out_facet_1))!=0)<<6) +
+                          ((isEdgeConstrainedSafe(mesh, tet_0, UP_FACET(in_facet_0, out_facet_0), DOWN_FACET(in_facet_0, out_facet_0))!=0)<<7) +
+                          ((isEdgeConstrainedSafe(mesh, tet_0, UP_FACET(in_facet_0, out_facet_0), out_facet_0)!=0)<<11);
+
+  // now we have everything... we just need to test if the quality of tetrahedra would be good
+  double qual[3];
+  for (int i=0; i<3; i++) {
+    qual[i] = tetQuality2(mesh, local->cavity.annulus[i], local->cavity.annulus[(i+1)%3], local->cavity.v_up, local->cavity.v_down);
+    if(qual[i]<worst_qual){
+      return HXT_STATUS_INTERNAL;
+    }
+  }
+
+  if(local->deleted.num<1)
+    HXT_CHECK( createNewDeleted(mesh, shared, local) );
+
+  local->deleted.num--;
+  uint64_t newTet[3] = {tet_0, tet_1, local->deleted.tetID[local->deleted.num]};
+
+  for (unsigned i=0; i<3; i++) {
+    uint64_t curTet = newTet[i];
+    mesh->tetrahedra.node[4*curTet + 0] = local->cavity.annulus[i];
+    mesh->tetrahedra.node[4*curTet + 1] = local->cavity.annulus[(i+1)%3];
+    mesh->tetrahedra.node[4*curTet + 2] = local->cavity.v_up;
+    mesh->tetrahedra.node[4*curTet + 3] = local->cavity.v_down;
+
+    mesh->tetrahedra.neigh[4*curTet + 0] = 4*newTet[(i+1)%3] + 1;
+    // mesh->tetrahedra.neigh[4*curTet + 1] = 4*newTet[(i+4)%3] + 0;
+    mesh->tetrahedra.neigh[4*newTet[(i+1)%3] + 1] = 4*curTet + 0;
+
+    mesh->tetrahedra.neigh[4*curTet + 2] = local->cavity.neigh_down[i];
+    if( local->cavity.neigh_down[i]!=HXT_NO_ADJACENT)
+      mesh->tetrahedra.neigh[local->cavity.neigh_down[i]] = 4*curTet + 2;
+
+    mesh->tetrahedra.neigh[4*curTet + 3] = local->cavity.neigh_up[i];
+    if( local->cavity.neigh_up[i]!=HXT_NO_ADJACENT)
+      mesh->tetrahedra.neigh[local->cavity.neigh_up[i]] = 4*curTet + 3;
+
+    mesh->tetrahedra.colors[curTet] = color;
+
+    // TODO: verify flags are well done
+    mesh->tetrahedra.flag[curTet] = local->cavity.flag[i];
+    shared->quality2.values[curTet] = qual[i];
+  }
+
+  return HXT_STATUS_OK;
+  // return HXT_STATUS_INTERNAL;
+}
+
+
+static inline HXTStatus buildEdgeCavity(HXTMesh* mesh, ThreadLocal* local,
+                                        const uint64_t badTet, uint16_t color,
+                                        int in_facet, int out_facet)
+{
+  const uint64_t startDist = local->partition.startDist;
+  const uint64_t rel = local->partition.endDist - startDist;
+  int edgeOut = 0;
+  HXTVertex* vertices = (HXTVertex*) mesh->vertices.coord;
+
+  uint64_t curTet = badTet;
+  local->cavity.num = 0;
+
+  if(isEdgeConstrainedSafe(mesh, badTet, in_facet, out_facet))
+    return HXT_STATUS_INTERNAL;
+
+  local->cavity.v_up = mesh->tetrahedra.node[4*badTet + UP_VERTEX(in_facet, out_facet)];
+  edgeOut += vertexOutOfPartition(vertices, local->cavity.v_up, rel, startDist);
+  local->cavity.v_down = mesh->tetrahedra.node[4*badTet + DOWN_VERTEX(in_facet, out_facet)];
+  edgeOut += vertexOutOfPartition(vertices, local->cavity.v_down, rel, startDist);
+
+  HXT_CHECK( reserveNewDeleted(local, 7) );
+
+  do{
+    // add the current tetrahedra
+    local->deleted.tetID[local->deleted.num + local->cavity.num] = curTet;
+
+    // add the neighbor up and down
+    local->cavity.neigh_up[local->cavity.num] = mesh->tetrahedra.neigh[4*curTet + UP_FACET(in_facet, out_facet)];
+    local->cavity.neigh_down[local->cavity.num] = mesh->tetrahedra.neigh[4*curTet + DOWN_FACET(in_facet, out_facet)];
+
+    // TODO: just store one flag for up and down. the one of the default tetrahedron
+    local->cavity.flag[local->cavity.num] = (isFacetConstrained(mesh, 4*curTet + UP_FACET(in_facet, out_facet))!=0) +
+                                            ((isEdgeConstrainedSafe(mesh, curTet, UP_FACET(in_facet, out_facet), out_facet)!=0)<<1) +
+                                            ((isEdgeConstrainedSafe(mesh, curTet, UP_FACET(in_facet, out_facet), DOWN_FACET(in_facet, out_facet))!=0)<<2) +
+                                            ((isEdgeConstrainedSafe(mesh, curTet, UP_FACET(in_facet, out_facet), in_facet)!=0)<<3) +
+                                            ((isFacetConstrained(mesh, 4*curTet + DOWN_FACET(in_facet, out_facet))!=0)<<4) +
+                                            ((isEdgeConstrainedSafe(mesh, curTet, DOWN_FACET(in_facet, out_facet), out_facet)!=0)<<5) +
+                                            ((isEdgeConstrainedSafe(mesh, curTet, DOWN_FACET(in_facet, out_facet), in_facet)!=0)<<6) +
+                                            ((isEdgeConstrainedSafe(mesh, curTet, UP_FACET(in_facet, out_facet), DOWN_FACET(in_facet, out_facet))!=0)<<7);
+
+    // add the annulus vertex
+    uint32_t oldV = mesh->tetrahedra.node[4*curTet + IN_VERTEX(in_facet, out_facet)];
+    uint32_t newV = mesh->tetrahedra.node[4*curTet + OUT_VERTEX(in_facet, out_facet)];
+
+    local->cavity.annulus[local->cavity.num] = oldV;
+    local->cavity.num++;
+
+    // go into the neighbor through out_facet
+    uint64_t neigh = mesh->tetrahedra.neigh[4*curTet + out_facet];
+    if(neigh == HXT_NO_ADJACENT
+      || (isFacetConstrained(mesh, neigh)!=0)
+      || local->cavity.num>=HXT_MAX_CAVITY_SIZE) {
+      return HXT_STATUS_INTERNAL;
+    }
+
+    if(vertexOutOfPartition(vertices, oldV, rel, startDist) +
+       vertexOutOfPartition(vertices, newV, rel, startDist) + edgeOut > 1) {
+      return HXT_STATUS_CONFLICT;
+  }
+
+    curTet = neigh/4;
+    in_facet = neigh%4;
+
+    uint32_t* nodes = mesh->tetrahedra.node + 4*curTet;
+
+    for (out_facet=0; out_facet<3; out_facet++)
+      if(nodes[out_facet]==newV)
+        break;
+
+  }while(curTet!=badTet);
+
+  return HXT_STATUS_OK;
+}
+
+
+static HXTStatus edgeSwap(HXTMesh *mesh, ThreadShared* shared, ThreadLocal* local,
+                             const uint64_t badTet, const uint16_t color,
+                             int in_facet, int out_facet)
+{
+  HXT_CHECK( buildEdgeCavity(mesh, local, badTet, color, in_facet, out_facet) );
+
+  // find worst quality2 tet of the cavity
+  double worst = DBL_MAX;
+  for (uint64_t i=0; i<local->cavity.num; i++) {
+    uint64_t tet = local->deleted.tetID[local->deleted.num+i];
+    double qual = shared->quality2.values[tet];
+    if(qual<worst)
+      worst = qual;
+  }
+
+  const SwapPattern* patt = &patterns[local->cavity.num];
+  const int num_triangle_per_triangul = local->cavity.num-2;
+  uint32_t* annulus = local->cavity.annulus;
+
+  // calculate qualities of all possible tetrahedra
+  // TODO: optimize the fact that a facet is common in qual_up and qual_down
+  double hxtDeclareAligned qual_up[35];
+  double hxtDeclareAligned qual_down[35];
+  uint64_t mask = 0;
+  for (int i=0; i<patt->num_triangles; i++) {
+    uint32_t p0 = annulus[patt->triangles[i][0]];
+    uint32_t p1 = annulus[patt->triangles[i][1]];
+    uint32_t p2 = annulus[patt->triangles[i][2]];
+
+    // printf("%u %u %u\n", p0, p1, p2);
+
+    qual_up[i] = tetQuality2(mesh, p0, p1, local->cavity.v_up, p2);
+    qual_down[i] = tetQuality2(mesh, p0, p1, p2, local->cavity.v_down);
+
+    if(qual_up[i]<=worst || qual_down[i]<=worst)
+      mask |= patt->triangle_in_triangul[i];
+  }
+
+  // find the best triangulation
+  int best_triangulation = -1;
+  double best_worst = 0;
+  for (int i=0; i<patt->num_triangulations; i++) {
+    if((mask & (UINT64_C(1)<<i))==0) {
+      double cur_worst = DBL_MAX;
+      // this mean that no triangle in the triangulation
+      //   is worst than the current worst tetrahedron
+      for (int j=0; j<num_triangle_per_triangul; j++) {
+        double q_u = qual_up[patt->triangulations[i][j]];
+        double q_d = qual_down[patt->triangulations[i][j]];
+        if(q_u<best_worst || q_d<best_worst){
+          cur_worst = 0;
+          break;
+        }
+        if(q_u<cur_worst)
+          cur_worst = q_u;
+        if(q_d<cur_worst)
+          cur_worst = q_d;
+      }
+
+      if(cur_worst > best_worst){
+        best_worst = cur_worst;
+        best_triangulation = i;
+      }
+    }
+  }
+
+  // if no triangulation are better, return
+  if(best_triangulation==-1) {
+    return HXT_STATUS_INTERNAL;
+  }
+  
+  
+  // mark new deleted tet as deleted
+  for (uint64_t i=0; i<local->cavity.num; i++) {
+    shared->quality2.values[local->deleted.tetID[local->deleted.num+i]] = DBL_MAX; // deleted tets have good quality2
+    // markTetAsDeleted(mesh, local->deleted.tetID[local->deleted.num+i]);
+  }
+  local->deleted.num += local->cavity.num;
+
+  // reserve enough tetrahedra
+  if(2*num_triangle_per_triangul > local->deleted.num){ // tetrahedra are created...
+    HXT_CHECK(createNewDeleted(mesh, shared, local) );
+  }
+
+  uint64_t start = local->deleted.num - 2*num_triangle_per_triangul;
+
+  // make the swap
+  for (int i=0; i<num_triangle_per_triangul; i++) {
+    uint32_t tri = patt->triangulations[best_triangulation][i];
+    uint32_t p0 = annulus[patt->triangles[tri][0]];
+    uint32_t p1 = annulus[patt->triangles[tri][1]];
+    uint32_t p2 = annulus[patt->triangles[tri][2]];
+
+    // neighbor information. if positive, the neighbor is a new tetrahedra
+    int n0 = patt->triangul_neigh[best_triangulation][4*i];
+    int n1 = patt->triangul_neigh[best_triangulation][4*i+1];
+    int n2 = patt->triangul_neigh[best_triangulation][4*i+2];
+
+    const uint64_t newTet_up = local->deleted.tetID[start + i*2];
+    const uint64_t newTet_down = local->deleted.tetID[start + i*2 + 1];
+
+    // create the upper tet.
+    {
+      uint32_t* nodes = mesh->tetrahedra.node + 4*newTet_up;
+      uint64_t* neigh = mesh->tetrahedra.neigh + 4*newTet_up;
+
+      mesh->tetrahedra.colors[newTet_up] = color;
+      mesh->tetrahedra.flag[newTet_up] = 0;
+      shared->quality2.values[newTet_up] = qual_up[tri];
+
+      nodes[0] = p0;
+      nodes[1] = p1;
+      nodes[2] = local->cavity.v_up;
+      nodes[3] = p2;
+
+      if(n0>=0){
+        neigh[0] = 4*local->deleted.tetID[start + (n0/4)*2] + (n0%4) + (n0%4)/2;
+      }
+      else {
+        neigh[0] = local->cavity.neigh_up[-n0-1];
+
+        //  (down=2, in=3, out=1, up=0)
+        mesh->tetrahedra.flag[newTet_up] |= local->cavity.flag[-n0-1] & 0xF;
+
+        if(neigh[0]!=HXT_NO_ADJACENT)
+          mesh->tetrahedra.neigh[neigh[0]] = 4*newTet_up + 0;
+      }
+      if(n1>=0){
+        neigh[1] = 4*local->deleted.tetID[start + (n1/4)*2] + (n1%4) + (n1%4)/2;
+      }
+      else {
+        neigh[1] = local->cavity.neigh_up[-n1-1];
+
+        //  (down=2, in=0, out=3, up=1)
+        mesh->tetrahedra.flag[newTet_up] |= (local->cavity.flag[-n1-1]&5)<<4 |  // face (bit 0) is the up_facet => 1*4  (bit 4)
+                                            (local->cavity.flag[-n1-1]&2)<<6 |  // first edge (bit 1) was between up_facet and out_facet => 1-3   (bit 7)
+                                            // (local->cavity.flag[-n1-1]&4)<<4 |  // second edge (bit 2) was between up_facet and down_facet => 1-2 (bit 6)
+                                            (local->cavity.flag[-n1-1]&8)>>2;   // third edge (bit 3) was between up_facet and in_facet => 0-1    (bit 1)
+
+        if(neigh[1]!=HXT_NO_ADJACENT)
+          mesh->tetrahedra.neigh[neigh[1]] = 4*newTet_up + 1;
+      }
+      neigh[2] = newTet_down*4 + 3;
+      if(n2>=0){
+        neigh[3] = 4*local->deleted.tetID[start + (n2/4)*2] + (n2%4) + (n2%4)/2;
+      }
+      else {
+        neigh[3] = local->cavity.neigh_up[-n2-1];
+
+        //  (down=2, in=1, out=0, up=3)
+        mesh->tetrahedra.flag[newTet_up] |= (local->cavity.flag[-n2-1]&1)<<12 |  // face (bit 0) is the up_facet => 3*4  (bit 12)
+                                            (local->cavity.flag[-n2-1]&2)<<2 |  // first edge (bit 1) was between up_facet and out_facet => 0-3   (bit 3)
+                                            (local->cavity.flag[-n2-1]&4)<<9 |  // second edge (bit 2) was between up_facet and down_facet => 2-3 (bit 11)
+                                            (local->cavity.flag[-n2-1]&8)<<4;   // third edge (bit 3) was between up_facet and in_facet => 1-3    (bit 7)
+
+        if(neigh[3]!=HXT_NO_ADJACENT)
+          mesh->tetrahedra.neigh[neigh[3]] = 4*newTet_up + 3;
+      }
+    }
+
+    // create the down tet.
+    {
+      uint32_t* nodes = mesh->tetrahedra.node + 4*newTet_down;
+      uint64_t* neigh = mesh->tetrahedra.neigh + 4*newTet_down;
+
+      mesh->tetrahedra.colors[newTet_down] = color;
+      mesh->tetrahedra.flag[newTet_down] = 0;
+      shared->quality2.values[newTet_down] = qual_down[tri];
+
+      nodes[0] = p0;
+      nodes[1] = p1;
+      nodes[2] = p2;
+      nodes[3] = local->cavity.v_down;
+
+      if(n0>=0){
+        neigh[0] = 4*local->deleted.tetID[start + (n0/4)*2 +1] + (n0%4);
+      }
+      else {
+        neigh[0] = local->cavity.neigh_down[-n0-1];
+
+        //  (down=0, in=2, out=1, up=3)
+        mesh->tetrahedra.flag[newTet_down] |= (local->cavity.flag[-n0-1] & 0xF0)>>4;
+
+        if(neigh[0]!=HXT_NO_ADJACENT)
+          mesh->tetrahedra.neigh[neigh[0]] = 4*newTet_down + 0;
+      }
+      if(n1>=0){
+        neigh[1] = 4*local->deleted.tetID[start + (n1/4)*2 +1] + (n1%4);
+      }
+      else {
+        neigh[1] = local->cavity.neigh_down[-n1-1];
+
+        //  (down=1, in=0, out=2, up=3)
+        mesh->tetrahedra.flag[newTet_down] |= (local->cavity.flag[-n1-1]&0x90) |  // face (bit 4) is the down_facet => 1*4  (bit 4)
+                                              (local->cavity.flag[-n1-1]&32)<<1 |  // first edge (bit 5) was between down_facet and out_facet => 1-2   (bit 6)
+                                              (local->cavity.flag[-n1-1]&64)>>5;  // second edge (bit 6) was between down_facet and in_facet => 0-1   (bit 1)
+                                              //(local->cavity.flag[-n1-1]&128);   // third edge (bit 7) was between down_facet and up_facet => 1-3    (bit 7)
+
+        if(neigh[1]!=HXT_NO_ADJACENT)
+          mesh->tetrahedra.neigh[neigh[1]] = 4*newTet_down + 1;
+      }
+      if(n2>=0){
+        neigh[2] = 4*local->deleted.tetID[start + (n2/4)*2 +1] + (n2%4);
+      }
+      else {
+        neigh[2] = local->cavity.neigh_down[-n2-1];
+
+        //  (down=2, in=1, out=0, up=3)
+        mesh->tetrahedra.flag[newTet_down] |= (local->cavity.flag[-n2-1]&0x90)<<4 |  // face (bit 4) is the down_facet => 2*4  (bit 8)
+                                              (local->cavity.flag[-n2-1]&32)>>3 |  // first edge (bit 5) was between down_facet and out_facet => 0-2   (bit 2)
+                                              (local->cavity.flag[-n2-1]&64);  // second edge (bit 6) was between down_facet and in_facet => 1-2   (bit 6)
+                                              //(local->cavity.flag[-n2-1]&128)<<4;   // third edge (bit 7) was between down_facet and up_facet => 2-3    (bit 11)
+
+        if(neigh[2]!=HXT_NO_ADJACENT)
+          mesh->tetrahedra.neigh[neigh[2]] = 4*newTet_down + 2;
+      }
+      neigh[3] = newTet_up*4 + 2;
+    }
+  }
+
+  local->deleted.num = start;
+
+  return HXT_STATUS_OK;
+}
+
+
+static inline HXTStatus buildVertexCavity(HXTMesh* mesh, ThreadLocal* local,
+                                          uint64_t curFace,
+                                          const uint32_t numVerticesConstrained) {
+  const uint64_t startDist = local->partition.startDist;
+  const uint64_t rel = local->partition.endDist - startDist;
+  const uint32_t vertex = mesh->tetrahedra.node[curFace];
+  HXTVertex* vertices = (HXTVertex*) mesh->vertices.coord;
+
+  // the vertex we are moving should be in the partition or we don't even try...
+  if(vertex < numVerticesConstrained || vertexOutOfPartition(vertices, vertex, rel, startDist))
+    return HXT_STATUS_INTERNAL;
+
+  HXT_CHECK( reserveNewDeleted(local, 4) );
+  local->deleted.tetID[local->deleted.num++] = curFace;
+  markTetAsDeleted(mesh, curFace/4);
+
+  for (uint64_t start=local->deleted.num-1; start<local->deleted.num; start++) {
+    HXT_CHECK( reserveNewDeleted(local, 3) );
+
+    uint64_t curFace = local->deleted.tetID[start];
+    uint64_t curTet = curFace/4;
+    unsigned face = curFace%4;
+
+    // all the cases where this function will fail.
+    for (int j=0; j<3; j++) {
+      uint64_t f = (face+j+1)%4;
+      uint64_t neigh = mesh->tetrahedra.neigh[4*curTet + f];
+      uint64_t neighTet = neigh/4;
+      unsigned neighF = neigh%4;
+
+      if(neigh==HXT_NO_ADJACENT)
+        return HXT_STATUS_INTERNAL;
+
+      if(isTetDeleted(mesh, neighTet))
+        continue;
+
+      if(isFacetConstrained(mesh, 4*curTet+f))
+        return HXT_STATUS_INTERNAL;
+
+      for(int k=0; k<4; k++){
+        if(k!=f && isEdgeConstrainedSafe(mesh, curTet, f, k))
+          return HXT_STATUS_INTERNAL;
+      }
+
+      // uint32_t a = mesh->tetrahedra.node[(face+j)%4];
+      uint32_t b = mesh->tetrahedra.node[4*curTet + (face+j+2)%4];
+      uint32_t c = mesh->tetrahedra.node[4*curTet + (face+j+3)%4];
+      uint32_t d = mesh->tetrahedra.node[neigh];
+      if(vertexOutOfPartition(vertices, b, rel, startDist) +
+         vertexOutOfPartition(vertices, c, rel, startDist) +
+         vertexOutOfPartition(vertices, d, rel, startDist) > 1)
+        return HXT_STATUS_CONFLICT;
+
+      // we can add the face without any fears now, but we have to find where is the vertex
+      if(mesh->tetrahedra.node[4*neighTet + (neighF+1)%4]==vertex)
+        local->deleted.tetID[local->deleted.num++] = 4*neighTet + (neighF+1)%4;
+      else if(mesh->tetrahedra.node[4*neighTet + (neighF+2)%4]==vertex)
+        local->deleted.tetID[local->deleted.num++] = 4*neighTet + (neighF+2)%4;
+      else
+        local->deleted.tetID[local->deleted.num++] = 4*neighTet + (neighF+3)%4;
+
+      markTetAsDeleted(mesh, neighTet);
+    }
+  }
+
+  return HXT_STATUS_OK;
+}
+
+static double compute_worst_quality(HXTMesh* mesh, ThreadLocal* local, uint64_t prevNumDeleted) {
+  double worst = DBL_MAX;
+
+  // compute worst quality
+  for (uint64_t i=prevNumDeleted; i<local->deleted.num; i++) {
+    uint64_t tet = local->deleted.tetID[i]/4;
+    double qual = tetQuality2(mesh,
+                              mesh->tetrahedra.node[4*tet + 0],
+                              mesh->tetrahedra.node[4*tet + 1],
+                              mesh->tetrahedra.node[4*tet + 2],
+                              mesh->tetrahedra.node[4*tet + 3]);
+    if(qual < worst)
+      worst = qual;
+  }
+
+  return worst;
+}
+
+
+static inline void interpolate_coord(const double start[3], const double end[3], double coord[3], double alpha) {
+  for (int i=0; i<3; i++) {
+    coord[i] = alpha*(start[i])+(1-alpha)*end[i];
+  }
+}
+
+
+static HXTStatus golden_section_search(HXTMesh* mesh,
+                                       ThreadShared* shared,
+                                       ThreadLocal* local,
+                                       uint64_t prevNumDeleted,
+                                       double start[3],
+                                       double end[3],
+                                       uint32_t vertex)
+{
+  double* coord = mesh->vertices.coord + 4*vertex;
+  double tol = 0.00001;
+  const double invPhi = 0.6180339887498949;
+  double a = 1;
+  double b = 0;
+  double c = invPhi;
+  double d = 1-invPhi;
+
+  interpolate_coord(start, end, coord, a);
+  double fa = compute_worst_quality(mesh, local, prevNumDeleted);
+
+  interpolate_coord(start, end, coord, b);
+  double fb = compute_worst_quality(mesh, local, prevNumDeleted);
+
+  interpolate_coord(start, end, coord, c);
+  double fc = compute_worst_quality(mesh, local, prevNumDeleted);
+
+  interpolate_coord(start, end, coord, d);
+  double fd = compute_worst_quality(mesh, local, prevNumDeleted);
+
+
+  // double fstart = fa, fend = fb;
+  // int iter = 0;
+  
+  while(fc>fa && fc>fb && fd>fa && fd>fb && (fabs(fa-fb)>tol || fa==0.0 || fb==0.0)) {
+    if(fc>=fd) { // maximum is between a & d, (b <-- d and d <-- c, compute new position of c)
+      fb = fd;
+      b = d;
+      fd = fc;
+      d = c;
+      c = a + invPhi*(b-a); // (1-invPhi)*a+invPhi*b;
+      interpolate_coord(start, end, coord, c);
+      fc = compute_worst_quality(mesh, local, prevNumDeleted);
+    }
+    else { // maximum is between c & b, (a <-- c and c <-- d, compute new position of d)
+      fa = fc;
+      a = c;
+      fc = fd;
+      c = d;
+      d = b + invPhi*(a-b); // invPhi*a+(1-invPhi)*b;
+      interpolate_coord(start, end, coord, d);
+      fd = compute_worst_quality(mesh, local, prevNumDeleted);
+    }
+
+    // iter++;
+  }
+
+  // printf("fa:%f  fb:%f  fc:%f  fd:%f  fstart:%f  fend:%f  iter:%d\n", fa, fb, fc, fd, fstart, fend, iter);
+
+  // best of fa fb
+  if(fb > fa) {
+    // printf("%f\n", fb);
+    a = b;
+    interpolate_coord(start, end, coord, b);
+  }
+  else {
+    // printf("%f\n", fa);
+    interpolate_coord(start, end, coord, a);
+  }
+
+  if(a<0.999) {
+    // compute new qualities
+    for (uint64_t i=prevNumDeleted; i<local->deleted.num; i++) {
+      uint64_t tet = local->deleted.tetID[i]/4;
+      shared->quality2.values[tet] = tetQuality2(mesh,
+                                     mesh->tetrahedra.node[4*tet + 0],
+                                     mesh->tetrahedra.node[4*tet + 1],
+                                     mesh->tetrahedra.node[4*tet + 2],
+                                     mesh->tetrahedra.node[4*tet + 3]);
+    }
+  }
+  else if(a!=1.0) {
+    interpolate_coord(start, end, coord, 1.0);
+  }
+
+  return HXT_STATUS_OK;
+}
+
+// curFace is the face opposite to the vertex we are going to move
+static HXTStatus smoothing(HXTMesh *mesh,
+                           ThreadShared* shared,
+                           ThreadLocal* local,
+                           const uint64_t curFace,
+                           const uint32_t numVerticesConstrained)
+{
+  uint64_t prevNumDeleted = local->deleted.num;
+  HXTStatus status = buildVertexCavity(mesh, local, curFace, numVerticesConstrained);
+  if(status!=HXT_STATUS_OK && status!=HXT_STATUS_INTERNAL && status!=HXT_STATUS_CONFLICT){
+    HXT_TRACE(status);
+    return status;
+  }
+
+  for (uint64_t i=prevNumDeleted; i<local->deleted.num; i++) {
+    unmarkTetAsDeleted(mesh, local->deleted.tetID[i]/4);
+  }
+
+  if(status==HXT_STATUS_INTERNAL || status==HXT_STATUS_CONFLICT){
+    local->deleted.num = prevNumDeleted;
+    return status;
+  }
+
+  const uint64_t numTetInCavity = local->deleted.num - prevNumDeleted;
+
+  // move the point
+  uint32_t vertex = mesh->tetrahedra.node[curFace];
+  double oldCoord[3];
+  double centerCoord[3] = {0};
+  for (int j=0; j<3; j++) {
+    oldCoord[j] = mesh->vertices.coord[4*vertex+j];
+  }
+
+  for (uint64_t i=prevNumDeleted; i<local->deleted.num; i++) {
+    uint64_t tet = local->deleted.tetID[i]/4;
+    unsigned face = local->deleted.tetID[i]%4;
+    double* a = mesh->vertices.coord + 4*mesh->tetrahedra.node[4*tet + (face+1)%4];
+    double* b = mesh->vertices.coord + 4*mesh->tetrahedra.node[4*tet + (face+2)%4];
+    double* c = mesh->vertices.coord + 4*mesh->tetrahedra.node[4*tet + (face+3)%4];
+    for (int j=0; j<3; j++) {
+      centerCoord[j] += (a[j]+b[j]+c[j])/3.0;
+    }
+  }
+
+  for (int j=0; j<3; j++) {
+    centerCoord[j] /= (double) numTetInCavity ;
+  }
+
+  HXT_CHECK( golden_section_search(mesh, shared, local, prevNumDeleted, oldCoord, centerCoord, vertex) );
+
+  local->deleted.num = prevNumDeleted;
+
+  if(shared->quality2.values[curFace/4] < shared->quality2.threshold)
+    return HXT_STATUS_INTERNAL;
+  
+  return HXT_STATUS_OK;
+}
+
+
+HXTStatus hxtOptimizeTetrahedra(HXTMesh *mesh,
+                                HXTBbox* bbox,
+                                double minSize,
+                                double qualityThreshold,
+                                uint32_t numVerticesConstrained){
+  ThreadLocal* locals = NULL;
+  ThreadShared* shared = NULL;
+  volatile HXTStatus globalStatus = HXT_STATUS_OK;
+  const int maxThreads = omp_get_max_threads();
+  uint32_t seed = 1;
+  uint32_t nbits = 0;
+  int changePartitions = 1;
+
+  HXT_CHECK( threadShared_create(mesh, qualityThreshold, &shared) );
+  HXT_CHECK( threadLocals_create(&locals, maxThreads) );
+
+  int nthreads = maxThreads;
+  uint64_t nSwaps = UINT64_MAX;
+  uint64_t nConflict = 0;
+  uint64_t nAttempt = 1;
+
+  do {
+    int threadFinished = 0;
+
+    HXT_CHECK( threadShared_update(mesh, shared) );
+    if(shared->badTets.num==0)
+      break;
+
+    // TODO: we still need to update heuristics
+    if(nSwaps<10 || 100.*nConflict/nAttempt > 45.0) {
+      nthreads=(nthreads+1)/2;
+      changePartitions = 1;
+    }
+    else if(100.*nConflict/nAttempt > 8.0){
+      changePartitions=1;
+    }
+
+    nSwaps = 0;
+    nConflict = 0;
+
+    HXT_CHECK( threadLocals_update(mesh, bbox, minSize, shared, locals, nthreads, &seed, changePartitions, &nConflict, &nbits) );
+
+    nAttempt = nConflict;
+    changePartitions = 0;
+    HXT_INFO("%lu bad tetrahedra being optimized on %d threads", shared->badTets.num, nthreads);
+
+    #pragma omp parallel num_threads(nthreads) reduction(+:nSwaps,nConflict,nAttempt)
+    {
+      const int threadID = omp_get_thread_num();
+      ThreadLocal* local = locals + threadID;
+
+      for (uint64_t i=0; i<local->partition.length && globalStatus==HXT_STATUS_OK; i++) {
+        // eliminate the bad tetrahedra with swaps
+        uint64_t index = (local->partition.first + i)%shared->badTets.num;
+        uint64_t curTet = shared->badTets.tetID_dist[index].v[1];
+
+        if(curTet==HXT_NO_ADJACENT || shared->quality2.values[curTet]>= shared->quality2.threshold)
+          continue;
+
+        uint16_t color = mesh->tetrahedra.colors[curTet];
+        uint64_t* neighs = mesh->tetrahedra.neigh + 4*curTet;
+
+        /*** sort the neighbor by their qualities **/
+
+        double hxtDeclareAligned qual[4];
+        for (int i=0; i<4; i++) {
+          if(neighs[i]==HXT_NO_ADJACENT ||
+            mesh->tetrahedra.colors[neighs[i]/4]!=color) {
+            qual[i]=DBL_MAX;
+          }
+          else
+            qual[i] = shared->quality2.values[neighs[i]/4];
+        }
+
+        int hxtDeclareAligned order[4] = {0,1,2,3};
+
+        // sort first pair
+        if(qual[order[1]] < qual[order[0]])
+          SWAP(order[1], order[0]);
+
+        // sort second pair
+        if(qual[order[3]] < qual[order[2]])
+          SWAP(order[3], order[2]);
+
+        // after this, order[0] is at the right place
+        if(qual[order[2]] < qual[order[0]])
+          SWAP(order[2], order[0]);
+
+        // after this, order[3] is at the right place
+        if(qual[order[3]] < qual[order[1]])
+          SWAP(order[3], order[1]);
+
+        // remains order[1] and order[2]
+        if(qual[order[2]] < qual[order[1]])
+          SWAP(order[2], order[1]);
+
+        HXTStatus status=HXT_STATUS_OK;
+        status = smoothing(mesh, shared, local, 4*curTet+order[3], numVerticesConstrained);
+        nConflict+=status==HXT_STATUS_CONFLICT; nAttempt++;
+        if(status<=HXT_STATUS_INTERNAL) {
+          status = smoothing(mesh, shared, local, 4*curTet+order[2], numVerticesConstrained);
+          nConflict+=status==HXT_STATUS_CONFLICT; nAttempt++;
+          if(status<=HXT_STATUS_INTERNAL) {
+            status = smoothing(mesh, shared, local, 4*curTet+order[1], numVerticesConstrained);
+            nConflict+=status==HXT_STATUS_CONFLICT; nAttempt++;
+            if(status<=HXT_STATUS_INTERNAL) {
+              status = smoothing(mesh, shared, local, 4*curTet+order[0], numVerticesConstrained);
+              nConflict+=status==HXT_STATUS_CONFLICT; nAttempt++;
+            }
+          }
+        }
+
+
+        /*** make a swap whenever it is possible and it is an improvement ***/
+        if(status<=HXT_STATUS_INTERNAL  && qual[order[1]]!=DBL_MAX) {
+          status = edgeSwap(mesh, shared, local, curTet, color, order[0], order[1]);
+          nConflict+=status==HXT_STATUS_CONFLICT; nAttempt++;
+          if(status<=HXT_STATUS_INTERNAL && qual[order[2]]!=DBL_MAX) {
+            status = edgeSwap(mesh, shared, local, curTet, color, order[0], order[2]);
+            nConflict+=status==HXT_STATUS_CONFLICT; nAttempt++;
+            if(status<=HXT_STATUS_INTERNAL) {
+              status = edgeSwap(mesh, shared, local, curTet, color, order[1], order[2]);
+              nConflict+=status==HXT_STATUS_CONFLICT; nAttempt++;
+            }
+            if(status<=HXT_STATUS_INTERNAL && qual[order[3]]!=DBL_MAX) {
+              status = edgeSwap(mesh, shared, local, curTet, color, order[0], order[3]);
+              nConflict+=status==HXT_STATUS_CONFLICT; nAttempt++;
+              if(status<=HXT_STATUS_INTERNAL) {
+                status = edgeSwap(mesh, shared, local, curTet, color, order[1], order[3]);
+                nConflict+=status==HXT_STATUS_CONFLICT; nAttempt++;
+              }
+              if(status<=HXT_STATUS_INTERNAL) {
+                status = edgeSwap(mesh, shared, local, curTet, color, order[2], order[3]);
+                nConflict+=status==HXT_STATUS_CONFLICT; nAttempt++;
+              }
+            }
+          }
+        }
+
+        // if(status<=HXT_STATUS_INTERNAL && qual[order[0]]!=DBL_MAX) {
+        //   status = flip2_3(mesh, shared, local, curTet, color, order[0]);
+        //   if(status<=HXT_STATUS_INTERNAL && qual[order[1]]!=DBL_MAX) {
+        //     status = flip2_3(mesh, shared, local, curTet, color, order[1]);
+        //     if(status<=HXT_STATUS_INTERNAL && qual[order[2]]!=DBL_MAX) {
+        //       status = flip2_3(mesh, shared, local, curTet, color, order[2]);
+        //       if(status<=HXT_STATUS_INTERNAL && qual[order[3]]!=DBL_MAX) {
+        //         status = flip2_3(mesh, shared, local, curTet, color, order[3]);
+        //       }
+        //     }
+        //   }
+        // }
+
+        if(status<=HXT_STATUS_INTERNAL) // the cavity cannot be built
+        {
+          // HXT_WARNING("bad tetrahedron %lu cannot be improved", curTet);
+          // status=HXT_STATUS_OK;
+          continue;
+        }
+        else if(status!=HXT_STATUS_OK) {
+          #pragma omp atomic write
+          globalStatus = status;
+          break;
+        }
+
+        nSwaps++;
+      }
+
+      #pragma omp atomic update
+        threadFinished++;
+
+      int val = 0;
+      do{
+        // threads are waiting here for a reallocation
+        HXTStatus status = synchronizeReallocation(mesh, shared, &threadFinished, &val);
+
+        if(status!=HXT_STATUS_OK) {
+          #pragma omp atomic write
+          globalStatus = status;
+        }
+      }while(val<nthreads);
+
+    }
+
+    if(globalStatus!=HXT_STATUS_OK){
+      HXT_TRACE(globalStatus);
+      return globalStatus;
+    }
+
+    HXT_INFO("%lu mesh optimizations done (%.2f%%)", nSwaps, nSwaps*100.0/shared->badTets.num);
+    
+  } while(nConflict!=0 || nSwaps!=0);
+
+  for (int threadID=0; threadID<maxThreads; threadID++) {
+    for (int i=0; i<locals[threadID].deleted.num; i++) {
+      uint64_t delTet = locals[threadID].deleted.tetID[i];
+      markTetAsDeleted(mesh, delTet);
+      for (int j=0; j<4; j++)
+        mesh->tetrahedra.neigh[4*delTet+j] = HXT_NO_ADJACENT;
+    }
+  }
+
+  HXT_CHECK( hxtRemoveDeleted(mesh) );
+
+  double mean = 0;
+  // #pragma omp parallel for reduction(+:mean)
+  for (uint64_t i=0; i<mesh->tetrahedra.num; i++) {
+    if(shared->quality2.values[i]!=DBL_MAX && mesh->tetrahedra.colors[i]!=UINT16_MAX)
+      mean += sqrt(shared->quality2.values[i]*24.0)/mesh->tetrahedra.num;
+  }
+
+  double min = DBL_MAX;
+  // #pragma omp parallel for reduction(min:min)
+  for (uint64_t i=0; i<mesh->tetrahedra.num; i++) {
+    if(shared->quality2.values[i]!=DBL_MAX && mesh->tetrahedra.colors[i]!=UINT16_MAX)
+      min = fmin(sqrt(shared->quality2.values[i]*24.0), min);
+  }
+
+  printf("mean quality: %f | min quality: %f\n", mean, min);
+
+  HXT_CHECK( threadLocals_destroy(&locals, maxThreads) );
+  HXT_CHECK( threadShared_destroy(&shared) );
+
+  return HXT_STATUS_OK;
+}
+
diff --git a/contrib/hxt/hxt_tetOpti.h b/contrib/hxt/hxt_tetOpti.h
new file mode 100644
index 0000000000000000000000000000000000000000..0afd8121a090de670173669eb38c0bcc866e97f7
--- /dev/null
+++ b/contrib/hxt/hxt_tetOpti.h
@@ -0,0 +1,6 @@
+#ifndef HXT_MESH_H_
+#define HXT_MESH_H_
+#include "hxt_api.h"
+#include "hxt_mesh.h"
+HXTStatus hxtOptimizeTetrahedra(HXTMesh *mesh, HXTBbox* bbox, double minSize, double qualityThreshold, uint32_t numVerticesConstrained);
+#endif
diff --git a/contrib/hxt/hxt_tetPostpro.c b/contrib/hxt/hxt_tetPostpro.c
new file mode 100644
index 0000000000000000000000000000000000000000..ad2537193c66f77cf309ed8a5622b5cd6698acb6
--- /dev/null
+++ b/contrib/hxt/hxt_tetPostpro.c
@@ -0,0 +1,183 @@
+#include "predicates.h"
+#include "hxt_tetrahedra.h"
+#include "hxt_tetPostpro.h"
+#include "hxt_tetFlag.h"
+#include "hxt_tetUtils.h"
+#include "hxt_vertices.h"
+
+
+static void verticesPlaneOrient(HXTMesh* mesh, double* __restrict__ p0, double* __restrict__ p1, double* __restrict__ p2){
+  #pragma omp parallel for
+  for (uint32_t i=0; i<mesh->vertices.num; i++) {
+    mesh->vertices.coord[4*i+3] = orient3d(p0, p1, p2, mesh->vertices.coord + 4*i);
+  }
+}
+
+/** keep only tetrahedra whose intersection with a plane is not empty.
+The plane is defined by 3 points. */
+HXTStatus hxtTetPlaneIntersection(HXTMesh* mesh, double* p0, double* p1, double* p2){
+  verticesPlaneOrient(mesh, p0, p1, p2);
+
+  #pragma omp parallel for
+  for (uint64_t i=0; i<mesh->tetrahedra.num; i++) {
+    if(mesh->tetrahedra.node[4*i+3] == HXT_GHOST_VERTEX) {
+      markTetAsDeleted(mesh, i);
+    }
+    else {
+      double firstOrient = mesh->vertices.coord[4*mesh->tetrahedra.node[4*i]+3];
+      for (int j=1; j<4; j++) {
+        double secondOrient = mesh->vertices.coord[4*mesh->tetrahedra.node[4*i+j]+3];
+        if(firstOrient*secondOrient<=0.0){
+          firstOrient = 0.0;
+          break;
+        }
+      }
+
+      if(firstOrient!=0.0)
+        markTetAsDeleted(mesh, i);
+    }
+  }
+
+  HXT_CHECK( hxtRemoveDeleted(mesh) );
+
+  return HXT_STATUS_OK;
+}
+
+/** keep only tetrahedra with at least two vertices on the positive side of the plane {p0,p1,p2}.
+(p is on the positive side <=> orient3d(p0,p1,p2, p) > 0)
+When seen from the negative side, p0,p1,p2 is counter-clockwise  */
+HXTStatus hxtTetPlaneOrient(HXTMesh* mesh, double* p0, double* p1, double* p2){
+  verticesPlaneOrient(mesh, p0, p1, p2);
+
+  #pragma omp parallel for
+  for (uint64_t i=0; i<mesh->tetrahedra.num; i++) {
+    if(mesh->tetrahedra.node[4*i+3] == HXT_GHOST_VERTEX) {
+      markTetAsDeleted(mesh, i);
+    }
+    else {
+      for (int j=0; j<4; j++) {
+        if(mesh->vertices.coord[4*mesh->tetrahedra.node[4*i+j]+3]<0.0){
+          markTetAsDeleted(mesh, i);
+          break;
+        }
+      }
+    }
+  }
+
+  HXT_CHECK( hxtRemoveDeleted(mesh) );
+
+  return HXT_STATUS_OK;
+}
+
+
+/** keep only vertices that are either in the surface mesh or in the tetrahedra mesh */
+HXTStatus hxtFilterVertices(HXTMesh* mesh, double* nodalSizes){
+  HXTVertex* vertices = (HXTVertex*) mesh->vertices.coord;
+
+  #pragma omp parallel for
+  for (uint32_t i=0; i<mesh->vertices.num; i++) {
+    vertices[i].padding.status = HXT_STATUS_FALSE;
+  }
+
+  if(mesh->tetrahedra.node!=NULL){
+    #pragma omp parallel for
+    for (uint32_t i=0; i<mesh->tetrahedra.num; i++) {
+      vertices[mesh->tetrahedra.node[4*i+0]].padding.status = HXT_STATUS_TRUE;
+      vertices[mesh->tetrahedra.node[4*i+1]].padding.status = HXT_STATUS_TRUE;
+      vertices[mesh->tetrahedra.node[4*i+2]].padding.status = HXT_STATUS_TRUE;
+      vertices[mesh->tetrahedra.node[4*i+3]].padding.status = HXT_STATUS_TRUE;
+    }
+  }
+
+  if(mesh->triangles.node!=NULL){
+    #pragma omp parallel for
+    for (uint32_t i=0; i<mesh->triangles.num; i++) {
+      vertices[mesh->triangles.node[3*i+0]].padding.status = HXT_STATUS_TRUE;
+      vertices[mesh->triangles.node[3*i+1]].padding.status = HXT_STATUS_TRUE;
+      vertices[mesh->triangles.node[3*i+2]].padding.status = HXT_STATUS_TRUE;
+    }
+  }
+
+  if(mesh->lines.node!=NULL){
+    #pragma omp parallel for
+    for (uint32_t i=0; i<mesh->lines.num; i++) {
+      vertices[mesh->lines.node[2*i+0]].padding.status = HXT_STATUS_TRUE;
+      vertices[mesh->lines.node[2*i+1]].padding.status = HXT_STATUS_TRUE;
+    }
+  }
+
+  /* remove deleted vertices and change tetrahedra.node triangles.node and lines.nodes accordingly */
+  uint32_t* numInserted;
+  HXT_CHECK( hxtAlignedMalloc(&numInserted, omp_get_max_threads()*sizeof(uint32_t)) );
+  const uint32_t n = mesh->vertices.num;
+
+  // when a vertex was skipped, nodeInfo[i].status = HXT_STATUS_FALSE
+  #pragma omp parallel
+  {
+    uint32_t start = 0;
+    int threadID = omp_get_thread_num();
+    numInserted[threadID] = 0;
+
+    #pragma omp for schedule(static)
+    for (uint32_t i=0; i<n; i++) {
+      if(vertices[i].padding.status==HXT_STATUS_TRUE)
+        numInserted[threadID]++;
+    }// implicit barrier here
+
+    for (int i=0; i<threadID; i++) {
+      start+=numInserted[i];
+    }
+
+    // 3rd: compute where each vertices will be
+    #pragma omp for schedule(static)
+    for (uint32_t i=0; i<n; i++) {
+      uint32_t oldStart = start;
+
+      if(vertices[i].padding.status==HXT_STATUS_TRUE)
+        start++;
+
+      // index and status are at the same location (it's a union) we cannot put this above the "if" !
+      vertices[i].padding.index = oldStart;
+    }
+
+    // 4th: update tetrahedra.node accordingly
+    if(mesh->tetrahedra.node!=NULL){
+      #pragma omp for
+      for (uint64_t i=0; i<4*mesh->tetrahedra.num; i++) {
+        uint32_t index = mesh->tetrahedra.node[i];
+        if(index!=HXT_GHOST_VERTEX)
+          mesh->tetrahedra.node[i] = vertices[index].padding.index;
+      }
+    }
+
+    if(mesh->triangles.node!=NULL){
+      #pragma omp for
+      for (uint64_t i=0; i<3*mesh->triangles.num; i++) {
+        uint32_t index = mesh->triangles.node[i];
+        mesh->triangles.node[i] = vertices[index].padding.index;
+      }
+    }
+
+    if(mesh->lines.node!=NULL){
+      #pragma omp for
+      for (uint64_t i=0; i<2*mesh->lines.num; i++) {
+        uint32_t index = mesh->lines.node[i];
+        mesh->lines.node[i] = vertices[index].padding.index;
+      }
+    }
+  }
+
+  HXT_CHECK( hxtAlignedFree(&numInserted) );
+
+  // 5th: put vertices at the right indices
+  for (uint32_t i=0; i<mesh->vertices.num; i++) {
+    if(nodalSizes!=NULL){
+      nodalSizes[vertices[i].padding.index] = nodalSizes[i];
+    }
+    vertices[vertices[i].padding.index] = vertices[i];
+  }
+
+  mesh->vertices.num = vertices[mesh->vertices.num-1].padding.index + 1;
+
+  return HXT_STATUS_OK;
+}
diff --git a/contrib/hxt/hxt_tetPostpro.h b/contrib/hxt/hxt_tetPostpro.h
new file mode 100644
index 0000000000000000000000000000000000000000..62ebd5cc35389993dc19c03f7ee169a6b2d4762a
--- /dev/null
+++ b/contrib/hxt/hxt_tetPostpro.h
@@ -0,0 +1,32 @@
+#ifndef _HXT_TETPOSTPRO_
+#define _HXT_TETPOSTPRO_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "hxt_mesh.h"
+
+/**
+* \file hxt_tetPospro.h postprocessing to keep only some part of a tetrahedra mesh
+* \author Célestin Marot
+*/
+
+/** keep only tetrahedra whose intersection with a plane is not empty.
+The plane is defined by 3 points. */
+HXTStatus hxtTetPlaneIntersection(HXTMesh* mesh, double* p0, double* p1, double* p2);
+
+/** keep only tetrahedra with at least two vertices on the positive side of the plane {p0,p1,p2}.
+(p is on the positive side <=> orient3d(p0,p1,p2, p) > 0)
+When seen from the negative side, p0,p1,p2 is counter-clockwise  */
+HXTStatus hxtTetPlaneOrient(HXTMesh* mesh, double* p0, double* p1, double* p2);
+
+/** keep only vertices that are either in the surface mesh or in the tetrahedra mesh */
+HXTStatus hxtFilterVertices(HXTMesh* mesh, double* nodalSizes);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/contrib/hxt/hxt_tetRepair.c b/contrib/hxt/hxt_tetRepair.c
new file mode 100644
index 0000000000000000000000000000000000000000..4ba131f23e3404e3aa60d9347b4b3d2a7daa8189
--- /dev/null
+++ b/contrib/hxt/hxt_tetRepair.c
@@ -0,0 +1,399 @@
+#include "hxt_mesh.h"
+#include "hxt_vertices.h"
+#include "predicates.h"
+#include "hxt_sort.h"
+#include "hxt_tetRepair.h"
+#include "hxt_tetFlag.h"
+
+/**
+* \file hxt_tetRepair.c see header hxt_tetRepair.h
+* \author Célestin Marot
+*/
+
+#define SWAP(x,y) do{int tmp=x; x=y; y=tmp;}while(0)
+
+/**********************************************
+ (re-)compute adjacency of the tetrahedral mesh
+ **********************************************/
+HXTStatus hxtTetAdjacencies(HXTMesh* mesh){
+  const uint64_t nTet = mesh->tetrahedra.num;
+  const uint64_t n = mesh->vertices.num+1;
+
+  // make sure it was allocated
+  HXT_CHECK( hxtAlignedFree(&mesh->tetrahedra.neigh) );
+  HXT_CHECK( hxtAlignedMalloc(&mesh->tetrahedra.neigh, mesh->tetrahedra.size*4*sizeof(uint64_t)) );
+
+  #pragma omp parallel for
+  for (uint64_t i=0; i<mesh->tetrahedra.num*4; i++) {
+    mesh->tetrahedra.neigh[i] = HXT_NO_ADJACENT;
+  }
+
+  // first step, create all triangles...
+  HXTGroup3* triplet;
+  HXTGroup2* pair; // used when n^3 can fit in 64 bits
+
+  if(n > 2642245)// n^3 cannot be on 64 bits
+    HXT_CHECK( hxtAlignedMalloc(&triplet, nTet*4*sizeof(HXTGroup3)) );
+  else
+    HXT_CHECK( hxtAlignedMalloc(&pair, nTet*4*sizeof(HXTGroup2)) );
+
+  int hxtDeclareAligned indices[4][4] = {{1,2,3,0},{0,2,3,0},{0,1,3,0},{0,1,2,0}}; // 4th is just a padding
+
+  // fill the triangle structure
+  #pragma omp parallel for
+  for (uint64_t i=0; i<nTet; i++) {
+    uint32_t* node = mesh->tetrahedra.node + 4*i;
+    int hxtDeclareAligned order[4];
+
+    // sort a and b
+    int cmp1 = (node[0] <= node[1]);
+    int cmp2 = (node[2] <= node[3]);
+    order[0] = !cmp1;
+    order[1] = cmp1;
+    order[2] = 2 + !cmp2;
+    order[3] = 2 + cmp2;
+
+    // after this, order[0] is at the right place
+    if(node[order[2]] < node[order[0]]){
+      SWAP(order[0], order[2]);
+    }
+
+    // after this, order[3] is at the right place
+    if(node[order[3]] < node[order[1]]){
+      SWAP(order[3], order[1]);
+    }
+
+    // remains order[1] and order[2]
+    if(node[order[2]] < node[order[1]]){
+      SWAP(order[1], order[2]);
+    }
+
+    // on some architecture, this is a simple SIMD operation
+    uint32_t hxtDeclareAligned nodeOrdered[4];
+    for (int j=0; j<4; j++) {
+      nodeOrdered[j] = node[order[j]];
+    }
+
+    if(nodeOrdered[3]==HXT_GHOST_VERTEX)
+      nodeOrdered[3] = mesh->vertices.num;
+
+    if(n > 2642245){
+      for (int j=0; j<4; j++) {
+        triplet[i*4+j].v[0] = nodeOrdered[indices[j][0]]*n
+                            + nodeOrdered[indices[j][1]];
+        triplet[i*4+j].v[1] = nodeOrdered[indices[j][2]];
+        triplet[i*4+j].v[2] = i*4 + order[j];
+      }
+    }
+    else{
+      for (int j=0; j<4; j++) {
+        pair[i*4+j].v[0] = nodeOrdered[indices[j][0]]*n*n
+                         + nodeOrdered[indices[j][1]]*n
+                         + nodeOrdered[indices[j][2]];
+        pair[i*4+j].v[1] = i*4 + order[j];
+      }
+    }
+
+
+  }
+
+  // sort the triangles...
+  if(n > 2642245)
+  {
+    HXT_CHECK( group3_sort_v1(triplet, nTet*4, n-1) );
+    HXT_CHECK( group3_sort_v0(triplet, nTet*4, n*n-1) );
+
+    // now that triangles are sorted, when two are the same, they are neighbors
+    #pragma omp parallel for
+    for (uint64_t i=0; i<nTet*4-1; i++) {
+      if(triplet[i].v[0]==triplet[i+1].v[0] &&
+         triplet[i].v[1]==triplet[i+1].v[1])
+      {
+        mesh->tetrahedra.neigh[triplet[i].v[2]] = triplet[i+1].v[2];
+        mesh->tetrahedra.neigh[triplet[i+1].v[2]] = triplet[i].v[2];
+        // i++; // can be done but break SIMD
+      }
+    }
+
+    HXT_CHECK( hxtAlignedFree(&triplet) );
+  }
+  else{
+    HXT_CHECK( group2_sort_v0(pair, nTet*4, n*n*n-1) );
+
+    #pragma omp parallel for
+    for (uint64_t i=0; i<nTet*4-1; i++) {
+      if(pair[i].v[0]==pair[i+1].v[0])
+      {
+        mesh->tetrahedra.neigh[pair[i].v[1]] = pair[i+1].v[1];
+        mesh->tetrahedra.neigh[pair[i+1].v[1]] = pair[i].v[1];
+        // i++; // can be done but break SIMD
+      }
+    }
+
+    HXT_CHECK( hxtAlignedFree(&pair) );
+  }
+
+  return HXT_STATUS_OK;
+}
+
+
+
+HXTStatus hxtTetReorder(HXTMesh* mesh)
+{
+  const uint64_t nTet = mesh->tetrahedra.num;
+  const uint64_t n = mesh->vertices.num;
+  uint64_t max = n*n*n-1;
+
+  HXTGroup3* triplet;
+  HXTGroup2* pair; // used when n^3 can fit in 64 bits
+
+  if(n > 2642245){// n^3 cannot be on 64 bits
+    HXT_CHECK( hxtAlignedMalloc(&triplet, nTet*4*sizeof(HXTGroup3)) );
+    max = n*n-1;
+  }
+
+  HXT_CHECK( hxtAlignedMalloc(&pair, nTet*4*sizeof(HXTGroup2)) );
+
+  if(n > 2642245){
+    #pragma omp parallel for
+    for (uint64_t i=0; i<nTet; i++) {
+      triplet[i].v[0] = mesh->tetrahedra.node[4*i]*n
+                        + mesh->tetrahedra.node[4*i+1];
+      triplet[i].v[1] = mesh->tetrahedra.node[4*i+2];
+      triplet[i].v[2] = i;
+    }
+  }
+  else{
+    #pragma omp parallel for
+    for (uint64_t i=0; i<nTet; i++) {
+      pair[i].v[0] = mesh->tetrahedra.node[4*i]*n*n
+                   + mesh->tetrahedra.node[4*i+1]*n
+                   + mesh->tetrahedra.node[4*i+2];
+      pair[i].v[1] = i;
+    }
+  }
+
+  if(n > 2642245)
+  {
+    HXT_CHECK( group3_sort_v1(triplet, nTet, n-1) );
+
+    #pragma omp parallel for
+    for (uint64_t i=0; i<nTet; i++) {
+      pair[i].v[0] = triplet[i].v[0];
+      pair[i].v[1] = triplet[i].v[2];
+    }
+
+    HXT_CHECK( hxtAlignedFree(&triplet) );
+  }
+
+
+  HXT_CHECK( group2_sort_v0(pair, nTet, max) );
+
+  // we do not care about key anymore, put inverse index
+  #pragma omp parallel for
+  for (uint64_t i=0; i<nTet; i++) {
+    pair[pair[i].v[1]].v[0] = i;
+  }
+
+  uint64_t* newNeigh;
+  HXT_CHECK( hxtAlignedMalloc(&newNeigh, mesh->tetrahedra.size*4*sizeof(uint64_t)));
+
+  #pragma omp parallel for
+  for (uint64_t i=0; i<nTet; i++) {
+    uint64_t index = pair[i].v[1];
+    for (unsigned j=0; j<4; j++){
+      uint64_t oldNeigh = mesh->tetrahedra.neigh[4*index+j];
+      if(oldNeigh==HXT_NO_ADJACENT)
+        newNeigh[i*4+j] = HXT_NO_ADJACENT;
+      else
+        newNeigh[i*4+j] = pair[oldNeigh/4].v[0]*4 + oldNeigh%4;
+    }
+  }
+
+  HXT_CHECK( hxtAlignedFree(&mesh->tetrahedra.neigh) );
+  mesh->tetrahedra.neigh = newNeigh;
+
+  uint32_t* newNode;
+  HXT_CHECK( hxtAlignedMalloc(&newNode, mesh->tetrahedra.size*4*sizeof(uint32_t)));
+
+  #pragma omp parallel for
+  for (uint64_t i=0; i<nTet; i++) {
+    uint64_t index = pair[i].v[1];
+    uint32_t* oldNode = mesh->tetrahedra.node + 4*index;
+    #pragma omp simd aligned(newNode:SIMD_ALIGN) aligned(oldNode:16)
+    for (unsigned j=0; j<4; j++)
+      newNode[i*4+j] = oldNode[j]; 
+  }
+
+  HXT_CHECK( hxtAlignedFree(&mesh->tetrahedra.node) );
+  mesh->tetrahedra.node = newNode;
+
+  uint16_t* newColor;
+  HXT_CHECK( hxtAlignedMalloc(&newColor, mesh->tetrahedra.size*sizeof(uint16_t)));
+
+  #pragma omp parallel for
+  for (uint64_t i=0; i<nTet; i++) {
+    uint64_t index = pair[i].v[1];
+    newColor[i] = mesh->tetrahedra.colors[index]; 
+  }
+
+  HXT_CHECK( hxtAlignedFree(&mesh->tetrahedra.colors) );
+  mesh->tetrahedra.colors = newColor;
+
+  uint16_t* newFlag;
+  HXT_CHECK( hxtAlignedMalloc(&newFlag, mesh->tetrahedra.size*sizeof(uint16_t)));
+
+  #pragma omp parallel for
+  for (uint64_t i=0; i<nTet; i++) {
+    uint64_t index = pair[i].v[1];
+    newFlag[i] = mesh->tetrahedra.flag[index]; 
+  }
+
+  HXT_CHECK( hxtAlignedFree(&mesh->tetrahedra.flag) );
+  mesh->tetrahedra.flag = newFlag;
+
+  HXT_CHECK( hxtAlignedFree(&pair) );
+
+  return HXT_STATUS_OK;
+}
+
+
+// assume there is no ghost tetrahedron, this is to repair an imported mesh
+HXTStatus hxtTetOrientNodes(HXTMesh* mesh)
+{
+  const uint64_t nTet = mesh->tetrahedra.num;
+  const double* coord = mesh->vertices.coord;
+
+  #pragma omp parallel for
+  for (uint64_t i=0; i<nTet; i++) {
+    uint32_t* node = mesh->tetrahedra.node + 4*i;
+
+    if(orient3d(coord+4*node[0], coord+4*node[1], coord+4*node[2], coord+4*node[3])<0){
+      uint32_t tmp = node[0];
+      node[0] = node[1];
+      node[1] = tmp;
+    }
+  }
+
+  return HXT_STATUS_OK;
+}
+
+
+
+HXTStatus hxtTetVerify(HXTMesh* mesh)
+{
+  volatile int errorOccured = 0;
+  HXTVertex* vertices = (HXTVertex*) mesh->vertices.coord;
+
+  #pragma omp parallel for
+  for (uint64_t i=0; i<mesh->tetrahedra.num; i++)
+  {
+
+    // if(errorOccured)
+    //   continue;
+
+    uint32_t* Node = mesh->tetrahedra.node + i*4;
+    uint64_t* Neigh = mesh->tetrahedra.neigh + i*4;
+
+    if(isTetDeleted(mesh, i)){
+      // HXT_WARNING("deleted tetrahedra remain in the mesh");
+      continue;
+    }
+
+    // check for good placement of ghost vertex
+    if(Node[0]==HXT_GHOST_VERTEX || Node[1]==HXT_GHOST_VERTEX || Node[2]==HXT_GHOST_VERTEX){
+      HXT_ERROR_MSG(HXT_STATUS_ERROR, "ghost vertex at wrong place in tet. %lu", i);
+      errorOccured=1;
+      continue;
+    }
+
+    double* a = vertices[Node[0]].coord;
+    double* b = vertices[Node[1]].coord;
+    double* c = vertices[Node[2]].coord;
+
+    // check for the orientation
+    // if(Node[3]==0){
+    //   HXT_WARNING("ghost tet. %lu remains in the array (did you clean the mesh?)",i*4);
+    // }
+    // else
+    if(Node[3]!=HXT_GHOST_VERTEX && orient3d(a,b,c,vertices[Node[3]].coord)<=0.0){
+      HXT_ERROR_MSG(HXT_STATUS_ERROR, "orientation of tet %lu is wrong",i);
+      errorOccured=1;
+      continue;
+    }
+
+    // check the neighbors
+    for (unsigned j=0; j<4; j++)
+    {
+      uint64_t neigh = Neigh[j];
+
+      if(neigh==HXT_NO_ADJACENT){
+        continue;
+      }
+
+      if(neigh>=mesh->tetrahedra.num*4) {
+        HXT_ERROR_MSG(HXT_STATUS_ERROR, "%uth neighbor of tet %lu does not exist", j, i);
+        errorOccured=1;
+        continue;
+      }
+
+      // uint64_t* NeighNeigh = mesh->tetrahedra.neigh + neigh;
+      uint32_t* NeighNode = mesh->tetrahedra.node + neigh/4*4;
+      
+      if(mesh->tetrahedra.neigh[neigh]!=i*4+j){
+        HXT_ERROR_MSG(HXT_STATUS_ERROR, "tet %lu (%lu/4) is not the neighbor of its %uth neighbor %lu (%lu/4)", i, i*4,j, neigh/4, neigh);
+        errorOccured=1;
+        continue;
+      }
+
+      uint32_t V[3] = { Node[((j+1)&3)], Node[((j&2)^3)], Node[((j+3)&2)]};
+      unsigned l;
+
+      for (l=0; l<3; l++)
+      {
+        if(NeighNode[(((neigh&3)+1)&3)]==V[l] && NeighNode[(((neigh&3)&2)^3)]==V[(l+1)%3] && NeighNode[(((neigh&3)+3)&2)]==V[(l+2)%3])
+           break;
+      }
+
+      if(l!=3){
+        HXT_ERROR_MSG(HXT_STATUS_ERROR, "neighbor %u of tet. %lu is intersecting it (common face has the same orientation)",j,i);
+        errorOccured=1;
+        continue;
+      }
+
+      for (l=0; l<3; l++)
+      {
+        if(NeighNode[(((neigh&3)+1)&3)]==V[l] && NeighNode[(((neigh&3)&2)^3)]==V[(l+2)%3] && NeighNode[(((neigh&3)+3)&2)]==V[(l+1)%3])
+           break;
+      }
+
+      if(l==3){
+        HXT_ERROR_MSG(HXT_STATUS_ERROR, "neighbor %u of tet. %lu doesn't contain 3 common vertices",j,i);
+        errorOccured=1;
+        continue;
+      }
+
+
+      if((isFacetConstrained(mesh, i*4+j)!=0) ^ (isFacetConstrained(mesh, neigh)!=0)) {
+        HXT_ERROR_MSG(HXT_STATUS_ERROR, "constraint is not consistent on both side of facet 4*%lu+%u",i,j);
+        errorOccured=1;
+        continue;
+      }
+
+      // only for delaunay triangulation...
+      // if(Node[3]!=HXT_GHOST_VERTEX && NeighNode[neigh&3]!=HXT_GHOST_VERTEX && insphere(vertices[Node[0]].coord,
+      //             vertices[Node[1]].coord,
+      //             vertices[Node[2]].coord,
+      //             vertices[Node[3]].coord,
+      //             vertices[NeighNode[neigh&3]].coord)>0.0){
+      //   HXT_ERROR_MSG(HXT_STATUS_ERROR, "neighbor %u of tet %lu has it's non-common node in the sphere (insphere(%u %u %u %u %u)>0)",j,i*4, Node[0], Node[1], Node[2], Node[3], NeighNode[neigh&3]);
+      //   errorOccured=1;
+      //   continue;
+      // }
+    }
+  }
+
+  if(errorOccured)
+    return HXT_STATUS_ERROR;
+  return HXT_STATUS_OK;
+}
diff --git a/contrib/hxt/hxt_tetRepair.h b/contrib/hxt/hxt_tetRepair.h
new file mode 100644
index 0000000000000000000000000000000000000000..8f87bfbc4f2b02f0652443b3b4b4ac8533069e7e
--- /dev/null
+++ b/contrib/hxt/hxt_tetRepair.h
@@ -0,0 +1,34 @@
+#ifndef _HXT_TETREPAIR_
+#define _HXT_TETREPAIR_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "hxt_mesh.h"
+
+/**
+* \file hxt_tetRepair.h Repair a tetrahedra mesh
+* \author Célestin Marot
+*/
+
+/** (re-)compute the adjacency of a mesh */
+HXTStatus hxtTetAdjacencies(HXTMesh* mesh);
+
+/** orient tetrahedra correctly
+ \warning The mesh cannot contain any ghost vertex */
+HXTStatus hxtTetOrientNodes(HXTMesh* mesh);
+
+/** verify the consistency of a tetrahedral mesh */
+HXTStatus hxtTetVerify(HXTMesh* mesh);
+
+/** reorder tetrahedra in a reproducible manner */
+HXTStatus hxtTetReorder(HXTMesh* mesh);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
diff --git a/contrib/hxt/hxt_tetUtils.c b/contrib/hxt/hxt_tetUtils.c
new file mode 100644
index 0000000000000000000000000000000000000000..ef618ab7892761f06b17cacea0bb5599514bc4e9
--- /dev/null
+++ b/contrib/hxt/hxt_tetUtils.c
@@ -0,0 +1,194 @@
+#include "hxt_tetUtils.h"
+#include "hxt_tetFlag.h"
+#include "hxt_sort.h"
+
+#define MAX(x,y) ((x)>(y) ? (x) : (y))
+
+/***********************************
+ * remove deleted tetrahedra of mesh
+ ***********************************/
+// TODO: a parallel technique
+HXTStatus hxtRemoveDeleted(HXTMesh* mesh)
+{
+  #pragma omp parallel for
+  for (uint64_t i=0; i<mesh->tetrahedra.num; i++) {
+    if(isTetDeleted(mesh, i)){
+      for (unsigned j=0; j<4; j++) {
+        uint64_t neigh = mesh->tetrahedra.neigh[4*i+j]; // neighbor of the deleted tet
+        if(neigh!=HXT_NO_ADJACENT) // the deleted tet had a neighbor pointing to him...
+          mesh->tetrahedra.neigh[neigh] = HXT_NO_ADJACENT;
+      }
+    }
+  }
+
+  uint64_t right = mesh->tetrahedra.num-1;
+  uint64_t left = 0;
+
+  while(1) {
+    while(left < right && isTetDeleted(mesh, right)) right--;
+    while(left < right && isTetDeleted(mesh, left)==0) left++;
+
+    if(left >= right)
+      break;
+
+    mesh->tetrahedra.colors[left] = mesh->tetrahedra.colors[right];
+    mesh->tetrahedra.flag[left] = mesh->tetrahedra.flag[right];
+
+    // swap the two tetrahedra
+    for (unsigned j=0; j<4; j++) {
+      uint64_t neighR = mesh->tetrahedra.neigh[4*right+j]; // neighbor of the tet that will be moved (not deleted)
+      if(neighR!=HXT_NO_ADJACENT)
+        mesh->tetrahedra.neigh[neighR] = 4*left+j;
+
+      mesh->tetrahedra.node[4*left+j] = mesh->tetrahedra.node[4*right+j];
+      mesh->tetrahedra.neigh[4*left+j] = neighR;
+    }
+
+    left++; right--;
+  }
+
+  if(left==right && isTetDeleted(mesh, left)==0) left++;
+
+  mesh->tetrahedra.num = left;
+
+  return HXT_STATUS_OK;
+}
+
+
+/***************************************
+ * remove ghost tetrahedra from the mesh
+ * see header for more information
+ ***************************************/
+HXTStatus hxtRemoveGhosts(HXTMesh* mesh){
+  #pragma omp parallel for
+  for (uint64_t i=0; i<mesh->tetrahedra.num; i++) {
+    if(mesh->tetrahedra.node[4*i+3]==HXT_GHOST_VERTEX){
+      markTetAsDeleted(mesh, i);
+    }
+  }
+
+  HXT_CHECK( hxtRemoveDeleted(mesh) );
+
+  return HXT_STATUS_OK;
+}
+
+
+
+/**********************************
+ * add ghost tetrahedra to the mesh
+ * see header for more information
+ **********************************/
+HXTStatus hxtAddGhosts(HXTMesh* mesh){
+  int maxThreads = omp_get_max_threads();
+
+  uint64_t* hullCount;
+  HXTGroup2* edges;
+  uint64_t totalHullCount = 0;
+  HXT_CHECK( hxtMalloc(&hullCount, maxThreads*sizeof(uint64_t)) );
+
+  HXTStatus status = HXT_STATUS_OK;
+
+  #pragma omp parallel
+  {
+    int threadID = omp_get_thread_num();
+    hullCount[threadID] = 0;
+
+    // count the number of convex hull faces
+    #pragma omp for schedule(static)
+    for (uint64_t i=0; i<mesh->tetrahedra.num; i++) {
+      for (unsigned j=0; j<4; j++) {
+        if(mesh->tetrahedra.neigh[4*i+j]==HXT_NO_ADJACENT)
+          hullCount[threadID]++;
+      }
+    }
+
+    // exclusive scan + allocation
+    #pragma omp barrier
+    #pragma omp single
+    {
+      int nthreads = omp_get_num_threads();
+
+      for (int i=0; i<nthreads; i++) {
+        uint64_t tsum = hullCount[i] + totalHullCount;
+        hullCount[i] = totalHullCount;
+        totalHullCount = tsum;
+      }
+
+      status = hxtTetrahedraReserve(mesh, totalHullCount+mesh->tetrahedra.num);
+      if(status!=HXT_STATUS_OK)
+        HXT_TRACE(status);
+      else {
+        status = hxtAlignedMalloc(&edges, 3*totalHullCount*sizeof(HXTGroup2));
+        if(status!=HXT_STATUS_OK)
+          HXT_TRACE(status);
+      }
+    }
+
+
+    if(status== HXT_STATUS_OK){
+      // create the Ghost tet.
+      #pragma omp for schedule(static)
+      for (uint64_t i=0; i<mesh->tetrahedra.num; i++) {
+        for (unsigned j=0; j<4; j++) {
+          if(mesh->tetrahedra.neigh[4*i+j]==HXT_NO_ADJACENT){
+            uint64_t newGhost = hullCount[threadID] + mesh->tetrahedra.num;
+
+            mesh->tetrahedra.neigh[4*i+j] = 4*newGhost+3;
+            mesh->tetrahedra.neigh[4*newGhost+3] = 4*i+j;
+
+            mesh->tetrahedra.colors[newGhost] = UINT16_MAX;
+            mesh->tetrahedra.flag[newGhost] = 0;
+            if(isFacetConstrained(mesh, 4*i+j))
+              constrainFacet(mesh, 4*newGhost+3);
+
+            uint32_t v0, v1, v2;
+
+            v0 = mesh->tetrahedra.node[4*i+((j+1)&3)];
+            v1 = mesh->tetrahedra.node[4*i+((j+3)&2)];
+            v2 = mesh->tetrahedra.node[4*i+((j&2)^3)];
+
+            mesh->tetrahedra.node[4*newGhost+0] = v0;
+            mesh->tetrahedra.node[4*newGhost+1] = v1;
+            mesh->tetrahedra.node[4*newGhost+2] = v2;
+            mesh->tetrahedra.node[4*newGhost+3] = HXT_GHOST_VERTEX;
+
+            uint64_t index = 3*hullCount[threadID];
+
+            edges[index+0].v[0] = (v0<v1)?(uint64_t) v0*mesh->vertices.num+v1 : (uint64_t) v1*mesh->vertices.num+v0;
+            edges[index+0].v[1] = 4*newGhost+2;
+            edges[index+1].v[0] = (v0<v2)?(uint64_t) v0*mesh->vertices.num+v2 : (uint64_t) v2*mesh->vertices.num+v0;
+            edges[index+1].v[1] = 4*newGhost+1;
+            edges[index+2].v[0] = (v1<v2)?(uint64_t) v1*mesh->vertices.num+v2 : (uint64_t) v2*mesh->vertices.num+v1;
+            edges[index+2].v[1] = 4*newGhost+0;
+
+            hullCount[threadID]++;
+          }
+        }
+      }
+    }
+  }
+
+  if(status!=HXT_STATUS_OK){
+    return status;
+  }
+
+  mesh->tetrahedra.num+=totalHullCount;
+
+  // now we have to find the adjacencies between ghosts
+  const uint64_t max = (uint64_t) mesh->vertices.num*mesh->vertices.num;
+  const uint64_t n = totalHullCount*3;
+  HXT_CHECK(  group2_sort_v0(edges, n, max) );
+
+  // connect adjacencies
+  #pragma omp parallel for
+  for (uint64_t i=0; i<n; i+=2) {
+    mesh->tetrahedra.neigh[edges[i].v[1]] = edges[i+1].v[1];
+    mesh->tetrahedra.neigh[edges[i+1].v[1]] = edges[i].v[1];
+  }
+
+  // first make a list with all edges
+  HXT_CHECK( hxtAlignedFree(&edges) );
+  HXT_CHECK( hxtFree(&hullCount) );
+
+  return HXT_STATUS_OK;
+}
\ No newline at end of file
diff --git a/contrib/hxt/hxt_tetUtils.h b/contrib/hxt/hxt_tetUtils.h
new file mode 100644
index 0000000000000000000000000000000000000000..b8d382e1bb1f151c3e2e1f34fd5c7134248c1c33
--- /dev/null
+++ b/contrib/hxt/hxt_tetUtils.h
@@ -0,0 +1,61 @@
+#ifndef _HXT_TETUTILS_
+#define _HXT_TETUTILS_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "hxt_mesh.h"
+
+/**
+* \file tetUtils.h utility for the tetrahedral mesh
+* \author Célestin Marot
+*/
+
+/**
+ * Reserve memory space for tetrahedra assuming ntet tetrahedra will be added to the triangulation
+ * it is efficient to give ntet = ~8 times the number of vertices to be added
+ * as in average, the number of tetrahedra is ~6.5x greater than the number of vertices
+ */
+static inline HXTStatus hxtTetrahedraReserve(HXTMesh* mesh, uint64_t totalTet){
+  if(totalTet > mesh->tetrahedra.size){
+    HXT_CHECK( hxtAlignedRealloc(&mesh->tetrahedra.flag, totalTet*sizeof(uint16_t)) );
+    HXT_CHECK( hxtAlignedRealloc(&mesh->tetrahedra.colors, totalTet*sizeof(uint16_t)) );
+    HXT_CHECK( hxtAlignedRealloc(&mesh->tetrahedra.node,   totalTet*4*sizeof(uint32_t)) );
+    HXT_CHECK( hxtAlignedRealloc(&mesh->tetrahedra.neigh,  totalTet*4*sizeof(uint64_t)) );
+    mesh->tetrahedra.size = totalTet;
+  }
+  return HXT_STATUS_OK;
+}
+
+static inline HXTStatus hxtTetrahedraDoubleSize(HXTMesh* mesh) {
+  return hxtTetrahedraReserve(mesh, 2*mesh->tetrahedra.num);
+}
+
+
+/**
+ * Remove tetrahedra 't' whose flag indicates they are deleted
+ * it updates the adjacency of the neighbors of 't' referencing 't' 
+ * to HXT_NO_ADJACENT.
+ * You must set the adjacency of 't' to HXT_NO_ADJACENT if you don't want this behavior
+ */
+HXTStatus hxtRemoveDeleted(HXTMesh* mesh);
+
+/** Removes ghost tetrahedra */
+HXTStatus hxtRemoveGhosts(HXTMesh* mesh);
+
+/** Adds ghost tetrahedra to adjoin tet. whose neighbors are HXT_NO_ADJACENT.\n
+ * THIS FUNCTION SUPPOSE 2 THINGS:
+ *  - there are no ghost tetrahedra
+ *  - face with 1 tetrahedra (neigh[face]==HXT_NO_ADJACENT) are all on the convex hull
+ *
+ * this function will not work properly in any other cases...
+ */
+HXTStatus hxtAddGhosts(HXTMesh* mesh);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/contrib/hxt/hxt_tet_aspect_ratio.c b/contrib/hxt/hxt_tet_aspect_ratio.c
new file mode 100644
index 0000000000000000000000000000000000000000..fc1a9011d3ba960231f901d818666a07092315af
--- /dev/null
+++ b/contrib/hxt/hxt_tet_aspect_ratio.c
@@ -0,0 +1,192 @@
+#include <math.h>
+#include "predicates.h"
+
+void hxt_cross_prod(double a[3], double b[3], double c[3])
+{
+  c[2] = a[0] * b[1] - a[1] * b[0];
+  c[1] = -a[0] * b[2] + a[2] * b[0];
+  c[0] = a[1] * b[2] - a[2] * b[1];
+}
+
+double hxt_distance2(double p0[3], double p1[3]){
+  double a = p0[0]-p1[0];
+  double b = p0[1]-p1[1];
+  double c = p0[2]-p1[2];
+  return a*a+b*b+c*c;
+}
+
+double hxt_triangle_area(double p0[3], double p1[3], double p2[3])
+{
+  double a[3], b[3], c[3];
+
+  a[0] = p2[0] - p1[0];
+  a[1] = p2[1] - p1[1];
+  a[2] = p2[2] - p1[2];
+
+  b[0] = p0[0] - p1[0];
+  b[1] = p0[1] - p1[1];
+  b[2] = p0[2] - p1[2];
+
+  hxt_cross_prod(a, b, c);
+  return 0.5 * sqrt(c[0] * c[0] + c[1] * c[1] + c[2] * c[2]);
+}
+
+double hxtTetAspectRatio (double p0[3],  double p1[3], double p2[3], double p3[3]) {
+  double volume = orient3d (p0,p1,p2,p3) / 6.0;
+
+  double s1 = fabs(hxt_triangle_area(p0, p1, p2));
+  double s2 = fabs(hxt_triangle_area(p0, p2, p3));
+  double s3 = fabs(hxt_triangle_area(p0, p1, p3));
+  double s4 = fabs(hxt_triangle_area(p1, p2, p3));
+
+  double rhoin = 3. * volume / (s1 + s2 + s3 + s4);
+  double l  = hxt_distance2 (p0,p1);
+  double l2 = hxt_distance2 (p0,p2);
+  double l3 = hxt_distance2 (p0,p3);
+  double l4 = hxt_distance2 (p1,p2);
+  double l5 = hxt_distance2 (p1,p3);
+  double l6 = hxt_distance2 (p2,p3);
+
+  l = l2 > l ? l2:l;
+  l = l3 > l ? l3:l;
+  l = l4 > l ? l4:l;
+  l = l5 > l ? l5:l;
+  l = l6 > l ? l6:l;
+  
+  return sqrt(24./l) * rhoin ;
+}
+
+
+
+//!\ this function does not return the aspect ratio 'r'. It returns 'r^2/24'
+double hxtTetAspectFastRatio (double a[3],  double b[3], double c[3], double d[3]) {
+  double ab[3], ac[3], ad[3], bc[3], cd[3], db[3];
+  for (int i=0; i<3; i++) {
+    ab[i] = b[i] - a[i]; // AB
+    ac[i] = c[i] - a[i]; // AC
+    ad[i] = d[i] - a[i]; // AD
+  }
+ 
+  double adxac0 = ad[1]*ac[2] - ad[2]*ac[1];
+  double abxad0 = ab[1]*ad[2] - ab[2]*ad[1];
+  double acxab0 = ac[1]*ab[2] - ac[2]*ab[1];
+  double volume6 = ab[0]*adxac0 + ac[0]*abxad0 + ad[0]*acxab0;
+ 
+   // abort as early as possible
+  if(volume6<=0.0)
+    return 0.0;
+ 
+  double adxac1 = ad[2]*ac[0] - ad[0]*ac[2];
+  double adxac2 = ad[0]*ac[1] - ad[1]*ac[0];
+ 
+  double abxad1 = ab[2]*ad[0] - ab[0]*ad[2];
+  double abxad2 = ab[0]*ad[1] - ab[1]*ad[0];
+ 
+  double acxab1 = ac[2]*ab[0] - ac[0]*ab[2];
+  double acxab2 = ac[0]*ab[1] - ac[1]*ab[0];
+
+  for (int i=0; i<3; i++) {
+    db[i] = b[i] - d[i]; // DB = B-D = AB-AD
+    bc[i] = c[i] - b[i]; // BC = C-B = AC-AB
+    cd[i] = d[i] - c[i]; // CD = D-c = AD-AC
+  }
+ 
+  double cdxbc0 = cd[1]*bc[2] - cd[2]*bc[1]; // = adxac0+acxab0+abxad0;
+  double cdxbc1 = cd[2]*bc[0] - cd[0]*bc[2]; // = adxac1+acxab1+abxad1;
+  double cdxbc2 = cd[0]*bc[1] - cd[1]*bc[0]; // = adxac2+acxab2+abxad2;
+
+  double areaSum = sqrt(adxac0*adxac0 + adxac1*adxac1 + adxac2*adxac2)
+                 + sqrt(abxad0*abxad0 + abxad1*abxad1 + abxad2*abxad2)
+                 + sqrt(acxab0*acxab0 + acxab1*acxab1 + acxab2*acxab2)
+                 + sqrt(cdxbc0*cdxbc0 + cdxbc1*cdxbc1 + cdxbc2*cdxbc2);
+
+  double l = ab[0]*ab[0] + ab[1]*ab[1] + ab[2]*ab[2]; // |AB|²
+  double l2 = ac[0]*ac[0] + ac[1]*ac[1] + ac[2]*ac[2]; // |AC|²
+  double l3 = ad[0]*ad[0] + ad[1]*ad[1] + ad[2]*ad[2]; // |AD|²
+  double l4 = bc[0]*bc[0] + bc[1]*bc[1] + bc[2]*bc[2]; // |BC|²
+  double l5 = cd[0]*cd[0] + cd[1]*cd[1] + cd[2]*cd[2]; // |CD|²
+  double l6 = db[0]*db[0] + db[1]*db[1] + db[2]*db[2]; // |DB|²
+
+  if(l2>l) l=l2;
+  if(l3>l) l=l3;
+  if(l4>l) l=l4;
+  if(l5>l) l=l5;
+  if(l6>l) l=l6;
+
+  return volume6*volume6/(l*areaSum*areaSum);
+}
+
+
+// for a pair of tetrahedra: (abcd) and (abec)
+// void hxtTetAspectFastRatioPair (double a[3],  double b[3], double c[3], double d[3], double e[3], double* quality1, double quality2) {
+//   double ab[3], ac[3], ad[3], bc[3], cd[3], db[3];
+//   for (int i=0; i<3; i++) {
+//     ab[i] = b[i] - a[i]; // AB
+//     ac[i] = c[i] - a[i]; // AC
+//     ad[i] = d[i] - a[i]; // AD
+//     ae[i] = e[i] - a[i]; // AE
+//   }
+ 
+//   double adxac0 = ad[1]*ac[2] - ad[2]*ac[1];
+//   double abxad0 = ab[1]*ad[2] - ab[2]*ad[1];
+//   double acxab0 = ac[1]*ab[2] - ac[2]*ab[1];
+//   double acxae0 = ac[1]*ae[2] - ac[2]*ae[1];
+//   double aexab0 = ae[1]*ab[2] - ae[2]*ab[1];
+//   double volume1 = ab[0]*adxac0 + ac[0]*abxad0 + ad[0]*acxab0;
+//   double volume2 = ab[0]*acxae0 - ae[0]*acxab0 + ac[0]*aexab0;
+ 
+//    // abort as early as possible
+//   if(volume1<=0.0 || volume2<=0.0){
+//     *quality1 = 0.0;
+//     *quality2 = 0.0;
+//     return;
+//   }
+ 
+//   double adxac1 = ad[2]*ac[0] - ad[0]*ac[2];
+//   double adxac2 = ad[0]*ac[1] - ad[1]*ac[0];
+ 
+//   double abxad1 = ab[2]*ad[0] - ab[0]*ad[2];
+//   double abxad2 = ab[0]*ad[1] - ab[1]*ad[0];
+ 
+//   double acxab1 = ac[2]*ab[0] - ac[0]*ab[2];
+//   double acxab2 = ac[0]*ab[1] - ac[1]*ab[0];
+
+//   double acxae1 = ac[2]*ae[0] - ac[0]*ae[2];
+//   double acxae2 = ac[0]*ae[1] - ac[1]*ae[0];
+
+//   double aexab1 = ae[2]*ab[0] - ae[0]*ab[2];
+//   double aexab2 = ae[0]*ab[1] - ae[1]*ab[0];
+
+//   for (int i=0; i<3; i++) {
+//     db[i] = b[i] - d[i]; // DB = B-D = AB-AD
+//     bc[i] = c[i] - b[i]; // BC = C-B = AC-AB
+//     cd[i] = d[i] - c[i]; // CD = D-c = AD-AC
+
+//   }
+ 
+//   double cdxbc0 = cd[1]*bc[2] - cd[2]*bc[1]; // = adxac0+acxab0+abxad0;
+//   double cdxbc1 = cd[2]*bc[0] - cd[0]*bc[2]; // = adxac1+acxab1+abxad1;
+//   double cdxbc2 = cd[0]*bc[1] - cd[1]*bc[0]; // = adxac2+acxab2+abxad2;
+
+//   double commonArea = sqrt(acxab0*acxab0 + acxab1*acxab1 + acxab2*acxab2);
+
+//   double areaSum1 = commonArea
+//                   + sqrt(adxac0*adxac0 + adxac1*adxac1 + adxac2*adxac2)
+//                   + sqrt(abxad0*abxad0 + abxad1*abxad1 + abxad2*abxad2)
+//                   + sqrt(cdxbc0*cdxbc0 + cdxbc1*cdxbc1 + cdxbc2*cdxbc2);
+
+//   double l = ab[0]*ab[0] + ab[1]*ab[1] + ab[2]*ab[2]; // |AB|²
+//   double l2 = ac[0]*ac[0] + ac[1]*ac[1] + ac[2]*ac[2]; // |AC|²
+//   double l3 = ad[0]*ad[0] + ad[1]*ad[1] + ad[2]*ad[2]; // |AD|²
+//   double l4 = bc[0]*bc[0] + bc[1]*bc[1] + bc[2]*bc[2]; // |BC|²
+//   double l5 = cd[0]*cd[0] + cd[1]*cd[1] + cd[2]*cd[2]; // |CD|²
+//   double l6 = db[0]*db[0] + db[1]*db[1] + db[2]*db[2]; // |DB|²
+
+//   if(l2>l) l=l2;
+//   if(l3>l) l=l3;
+//   if(l4>l) l=l4;
+//   if(l5>l) l=l5;
+//   if(l6>l) l=l6;
+
+//   *quality1 = volume1*volume1/(l*areaSum1*areaSum1);
+// }
\ No newline at end of file
diff --git a/contrib/hxt/hxt_tet_aspect_ratio.h b/contrib/hxt/hxt_tet_aspect_ratio.h
new file mode 100644
index 0000000000000000000000000000000000000000..0ecc2bd5f74fab9a547954bef5af5ba83a02bd85
--- /dev/null
+++ b/contrib/hxt/hxt_tet_aspect_ratio.h
@@ -0,0 +1,9 @@
+#ifndef _HXT_TET_ASPECT_RATIO_
+#define _HXT_TET_ASPECT_RATIO_
+
+double hxtTetAspectRatio (double p0[3],  double p1[3], double p2[3], double p3[3]);
+
+//!\ this function does not return the aspect ratio 'r'. It returns 'r^2/24'
+double hxtTetAspectFastRatio (double p0[3],  double p1[3], double p2[3], double p3[3]);
+
+#endif
diff --git a/contrib/hxt/hxt_tetrahedra.c b/contrib/hxt/hxt_tetrahedra.c
index 68d1801a3f93e1ad6fe776edf0ed8ce896bf2a4a..d90368f3d88d33dbd98269c59744777da9698614 100644
--- a/contrib/hxt/hxt_tetrahedra.c
+++ b/contrib/hxt/hxt_tetrahedra.c
@@ -1,16 +1,19 @@
 #include "hxt_tetrahedra.h"
 #include "predicates.h"
+#include "hxt_tetRepair.h"
+#include "hxt_tetUtils.h"
+#include "hxt_tetFlag.h"
 #include "hxt_sort.h"
 
+/**
+* \file hxt_tetrahedra.c see header hxt_tetrahedra.h.
+* \author Célestin Marot
+*/
 
 /* compile-time parameters */
 #define SMALLEST_ROUND 2048
-#define ROUND_RATIO 7
-#define THREAD_RATIO 8
 #define DELETED_BUFFER_SIZE 8182
-// #define HXT_DELAUNAY_MAX_MEMORY /* use per thread bufffer instead of a global one (it's faster if it fits into RAM) */
 // #define HXT_DELAUNAY_LOW_MEMORY /* doesn't use any buffer (a lot slower, except if you are at the limit of filling the RAM) */
-// (if both are defined, MAX_MEMORY takes over MIN_MEMORY)
 
 /* usefull macros */
 #define ABS(x) ((x) >= 0 ? (x) : -(x))
@@ -26,102 +29,54 @@
     } \
   }while(0)
 
-/* usefull mask */
-static const uint64_t NEIGH_H = UINT64_C(0xFFFFFFFFFFFFFFFC);
 
-typedef struct TetLocalStruct {
-  uint64_t hxtDeclareAligned Map[1024];
-
-  struct{
-    uint32_t hxtDeclareAligned node[4];
-    uint64_t otherside; // the tet on the other side of the boundar
-  } *bnd;
-  uint64_t numBnd;
-  uint64_t sizeBnd;
+typedef struct{
+  uint32_t hxtDeclareAligned node[3];
+  uint16_t flag;
+  uint64_t neigh; // the tet on the other side of the boundar
+} cavityBnd_t;
 
-  uint64_t* deleted;
-  uint64_t numDeleted;
-  uint64_t sizeDeleted;
-
-  uint32_t* vertices;
-  uint32_t numVertices;
-  uint32_t sizeVertices;
+typedef struct {
+#ifndef HXT_DELAUNAY_LOW_MEMORY
+  uint64_t hxtDeclareAligned Map[1024];
+#endif
 
-  uint64_t startDist;
-  uint64_t endDist;
-  uint32_t localStart;
+  struct {
+    cavityBnd_t* bnd;
+    uint64_t num;
+    uint64_t size;
+  } ball;
+
+  struct {
+    uint64_t* tetID;
+    uint64_t num;
+    uint64_t size;
+  } deleted;
+
+  struct {
+    uint64_t startDist;
+    uint64_t endDist;
+    uint32_t first;
+  } partition;
 } TetLocal;
 
 
-static inline HXTStatus walking2Cavity(HXTMesh* mesh, TetLocal* local, uint64_t* curTet, const uint32_t vta);
-static inline HXTStatus diggingACavity(HXTMesh* mesh, TetLocal* local, uint64_t curTet, const uint32_t vta, const uint16_t color);
-static inline HXTStatus fillingACavity(HXTMesh* mesh, TetLocal* local, uint32_t* verticesID, uint64_t* curTet, const uint16_t color);
-static inline HXTStatus filterCavity (TetLocal* local, HXTMesh *mesh, const double *nodalSizes);
-// static HXTStatus hxtTetrahedraVerifyInternal(HXTMesh* mesh, TetLocal* Locals, int nthreads);
-
-
-
-HXTStatus hxtTetrahedraReserve(HXTMesh* mesh, uint64_t ntet){
-  ntet+=mesh->tetrahedra.num;
-  if(ntet > mesh->tetrahedra.size){
-    HXT_CHECK( hxtAlignedRealloc(&mesh->tetrahedra.colors, ntet*sizeof(uint16_t)) );
-    HXT_CHECK( hxtAlignedRealloc(&mesh->tetrahedra.node,   ntet*4*sizeof(uint32_t)) );
-    HXT_CHECK( hxtAlignedRealloc(&mesh->tetrahedra.neigh,  ntet*4*sizeof(uint64_t)) );
-    mesh->tetrahedra.size = ntet;
-  }
-  return HXT_STATUS_OK;
-}
-
-
 /***********************************
- * remove deleted tetrahedra of mesh
+ * create the initial tetrahedron 
+ * surrounded by 4 ghost tetrahedra
  ***********************************/
-//TODO: maybe do this in parallel (like in a sort pass)
-HXTStatus hxtRemoveDeleted(HXTMesh* mesh){
-
-  uint64_t right = mesh->tetrahedra.num-1;
-  uint64_t left = 0;
-
-  while(1) {
-    while(left < right && mesh->tetrahedra.colors[right]==HXT_DELETED_COLOR) right--;
-    while(left < right && mesh->tetrahedra.colors[left]!=HXT_DELETED_COLOR) left++;
-
-    if(left >= right)
-      break;
-
-    mesh->tetrahedra.colors[left] = mesh->tetrahedra.colors[right];
-
-    // swap the two tetrahedra
-    for (unsigned j=0; j<4; j++) {
-
-      uint64_t neigh = mesh->tetrahedra.neigh[4*right+j];
-      if(neigh!=HXT_NO_ADJACENT)
-        mesh->tetrahedra.neigh[neigh] = 4*left+j;
-
-      mesh->tetrahedra.node[4*left+j] = mesh->tetrahedra.node[4*right+j];
-      mesh->tetrahedra.neigh[4*left+j] = neigh;
-    }
-
-    left++; right--;
-  }
-
-  if(left==right && mesh->tetrahedra.colors[left]!=HXT_DELETED_COLOR) left++;
-
-  mesh->tetrahedra.num = left;
-
-  return HXT_STATUS_OK;
-}
-
-
-static inline HXTStatus hxtTetrahedraInit(HXTMesh* mesh, hxtNodeInfo* nodeInfo, uint32_t nToInsert){
+static inline HXTStatus hxtTetrahedraInit(HXTMesh* mesh, hxtNodeInfo* nodeInfo, uint32_t nToInsert, int verbosity){
   if(nToInsert < 4){
     return HXT_ERROR_MSG(HXT_STATUS_ERROR, "cannot mesh less than four vertices");
   }
   if(mesh->tetrahedra.size < 5){
-    HXT_INFO("Initialization reserved %lu Tet.", 10UL*nToInsert);
-    hxtTetrahedraReserve(mesh, 10UL*nToInsert);
+    uint32_t maxSizeEstim = MAX(omp_get_max_threads()*DELETED_BUFFER_SIZE+8UL*nToInsert, 10UL*nToInsert);
+    HXT_CHECK( hxtTetrahedraReserve(mesh, maxSizeEstim) );
+    HXT_INFO_COND(verbosity>1, "Initialization reserved %lu Tet.", mesh->tetrahedra.size);
   }
 
+  HXTVertex* vertices = (HXTVertex*) mesh->vertices.coord;
+
   // find non-coplanar vertices
   double orientation = 0.0;
 
@@ -134,10 +89,10 @@ static inline HXTStatus hxtTetrahedraInit(HXTMesh* mesh, hxtNodeInfo* nodeInfo,
       {
         for (l=k+1; orientation==0.0 && l<nToInsert; l++)
         {
-          orientation = orient3d(mesh->vertices.coord + 4*nodeInfo[i].node,
-                                 mesh->vertices.coord + 4*nodeInfo[j].node,
-                                 mesh->vertices.coord + 4*nodeInfo[k].node,
-                                 mesh->vertices.coord + 4*nodeInfo[l].node);
+          orientation = orient3d(vertices[nodeInfo[i].node].coord,
+                                 vertices[nodeInfo[j].node].coord,
+                                 vertices[nodeInfo[k].node].coord,
+                                 vertices[nodeInfo[l].node].coord);
         }
       }
     }
@@ -151,24 +106,28 @@ static inline HXTStatus hxtTetrahedraInit(HXTMesh* mesh, hxtNodeInfo* nodeInfo,
 
   // swap 0<->i  1<->j 2<->k 3<->l
   {
-    uint32_t tmp = nodeInfo[i].node;
-    nodeInfo[i].node = nodeInfo[0].node;
-    nodeInfo[0].node = tmp;
+    hxtNodeInfo tmp = nodeInfo[i];
+    nodeInfo[i] = nodeInfo[0];
+    nodeInfo[0] = tmp;
+    nodeInfo[0].status = HXT_STATUS_TRUE;
     i = 0;
 
-    tmp = nodeInfo[j].node;
-    nodeInfo[j].node = nodeInfo[1].node;
-    nodeInfo[1].node = tmp;
+    tmp = nodeInfo[j];
+    nodeInfo[j] = nodeInfo[1];
+    nodeInfo[1] = tmp;
+    nodeInfo[1].status = HXT_STATUS_TRUE;
     j = 1;
 
-    tmp = nodeInfo[k].node;
-    nodeInfo[k].node = nodeInfo[2].node;
-    nodeInfo[2].node = tmp;
+    tmp = nodeInfo[k];
+    nodeInfo[k] = nodeInfo[2];
+    nodeInfo[2] = tmp;
+    nodeInfo[2].status = HXT_STATUS_TRUE;
     k = 2;
 
-    tmp = nodeInfo[l].node;
-    nodeInfo[l].node = nodeInfo[3].node;
-    nodeInfo[3].node = tmp;
+    tmp = nodeInfo[l];
+    nodeInfo[l] = nodeInfo[3];
+    nodeInfo[3] = tmp;
+    nodeInfo[3].status = HXT_STATUS_TRUE;
     l = 3;
   }
 
@@ -179,128 +138,111 @@ static inline HXTStatus hxtTetrahedraInit(HXTMesh* mesh, hxtNodeInfo* nodeInfo,
     j = tmp;
   }
 
-  mesh->tetrahedra.neigh[ 0] = 19;        mesh->tetrahedra.node[ 0] = nodeInfo[l].node;
-  mesh->tetrahedra.neigh[ 1] = 15;        mesh->tetrahedra.node[ 1] = nodeInfo[k].node;
-  mesh->tetrahedra.neigh[ 2] = 11;        mesh->tetrahedra.node[ 2] = nodeInfo[j].node;
-  mesh->tetrahedra.neigh[ 3] =  7;        mesh->tetrahedra.node[ 3] = nodeInfo[i].node;
+  mesh->tetrahedra.neigh[ 0] = 19;    mesh->tetrahedra.node[ 0] = nodeInfo[l].node;
+  mesh->tetrahedra.neigh[ 1] = 15;    mesh->tetrahedra.node[ 1] = nodeInfo[k].node;
+  mesh->tetrahedra.neigh[ 2] = 11;    mesh->tetrahedra.node[ 2] = nodeInfo[j].node;
+  mesh->tetrahedra.neigh[ 3] =  7;    mesh->tetrahedra.node[ 3] = nodeInfo[i].node;
 
-  mesh->tetrahedra.neigh[ 4] = 18;        mesh->tetrahedra.node[ 4] = nodeInfo[l].node;
-  mesh->tetrahedra.neigh[ 5] = 10;        mesh->tetrahedra.node[ 5] = nodeInfo[j].node;
-  mesh->tetrahedra.neigh[ 6] = 13;        mesh->tetrahedra.node[ 6] = nodeInfo[k].node;
-  mesh->tetrahedra.neigh[ 7] =  3;        mesh->tetrahedra.node[ 7] = HXT_GHOST_VERTEX;
+  mesh->tetrahedra.neigh[ 4] = 18;    mesh->tetrahedra.node[ 4] = nodeInfo[l].node;
+  mesh->tetrahedra.neigh[ 5] = 10;    mesh->tetrahedra.node[ 5] = nodeInfo[j].node;
+  mesh->tetrahedra.neigh[ 6] = 13;    mesh->tetrahedra.node[ 6] = nodeInfo[k].node;
+  mesh->tetrahedra.neigh[ 7] =  3;    mesh->tetrahedra.node[ 7] = HXT_GHOST_VERTEX;
 
-  mesh->tetrahedra.neigh[8 ] = 17;        mesh->tetrahedra.node[ 8] = nodeInfo[l].node;
-  mesh->tetrahedra.neigh[9 ] = 14;        mesh->tetrahedra.node[ 9] = nodeInfo[k].node;
-  mesh->tetrahedra.neigh[10] =  5;        mesh->tetrahedra.node[10] = nodeInfo[i].node;
-  mesh->tetrahedra.neigh[11] =  2;        mesh->tetrahedra.node[11] = HXT_GHOST_VERTEX;
+  mesh->tetrahedra.neigh[8 ] = 17;    mesh->tetrahedra.node[ 8] = nodeInfo[l].node;
+  mesh->tetrahedra.neigh[9 ] = 14;    mesh->tetrahedra.node[ 9] = nodeInfo[k].node;
+  mesh->tetrahedra.neigh[10] =  5;    mesh->tetrahedra.node[10] = nodeInfo[i].node;
+  mesh->tetrahedra.neigh[11] =  2;    mesh->tetrahedra.node[11] = HXT_GHOST_VERTEX;
 
-  mesh->tetrahedra.neigh[12] = 16;        mesh->tetrahedra.node[12] = nodeInfo[l].node;
-  mesh->tetrahedra.neigh[13] =  6;        mesh->tetrahedra.node[13] = nodeInfo[i].node;
-  mesh->tetrahedra.neigh[14] =  9;        mesh->tetrahedra.node[14] = nodeInfo[j].node;
-  mesh->tetrahedra.neigh[15] =  1;        mesh->tetrahedra.node[15] = HXT_GHOST_VERTEX;
+  mesh->tetrahedra.neigh[12] = 16;    mesh->tetrahedra.node[12] = nodeInfo[l].node;
+  mesh->tetrahedra.neigh[13] =  6;    mesh->tetrahedra.node[13] = nodeInfo[i].node;
+  mesh->tetrahedra.neigh[14] =  9;    mesh->tetrahedra.node[14] = nodeInfo[j].node;
+  mesh->tetrahedra.neigh[15] =  1;    mesh->tetrahedra.node[15] = HXT_GHOST_VERTEX;
 
-  mesh->tetrahedra.neigh[16] = 12;        mesh->tetrahedra.node[16] = nodeInfo[k].node;
-  mesh->tetrahedra.neigh[17] =  8;        mesh->tetrahedra.node[17] = nodeInfo[j].node;
-  mesh->tetrahedra.neigh[18] =  4;        mesh->tetrahedra.node[18] = nodeInfo[i].node;
-  mesh->tetrahedra.neigh[19] =  0;        mesh->tetrahedra.node[19] = HXT_GHOST_VERTEX;
+  mesh->tetrahedra.neigh[16] = 12;    mesh->tetrahedra.node[16] = nodeInfo[k].node;
+  mesh->tetrahedra.neigh[17] =  8;    mesh->tetrahedra.node[17] = nodeInfo[j].node;
+  mesh->tetrahedra.neigh[18] =  4;    mesh->tetrahedra.node[18] = nodeInfo[i].node;
+  mesh->tetrahedra.neigh[19] =  0;    mesh->tetrahedra.node[19] = HXT_GHOST_VERTEX;
 
   mesh->tetrahedra.num = 5;
 
-  for (uint64_t i=0; i<5 ;i++)  mesh->tetrahedra.colors[i] = UINT16_MAX;
+  for (uint64_t i=0; i<5 ;i++){
+    mesh->tetrahedra.colors[i] = UINT16_MAX;
+    mesh->tetrahedra.flag[i] = 0;
+  }
 
   return HXT_STATUS_OK;
 }
 
-
-// fill the passes array and the number of threads to beginwith
-// return the number of passes
-static unsigned computePasses(uint32_t passes[12], uint32_t nInserted, uint32_t nToInsert,
-                                     int* nthreads)
+/***********************************
+ * fill the passes array which tells
+ * the size of each BRIO round.
+ * return the number of BRIO passes
+ ***********************************/
+static unsigned computePasses(uint32_t passes[12], uint32_t nInserted, uint32_t nToInsert)
 {
-  unsigned npasses;
-  *nthreads = 1;
+  unsigned npasses=0;
+  passes[0] = nToInsert;
 
-  passes[0] = 0;
-  if(nInserted*ROUND_RATIO < SMALLEST_ROUND)
-    passes[1] = SMALLEST_ROUND;
-  else{
-    passes[1] = nInserted*ROUND_RATIO;
-
-    // we already begin with a good number of points in the mesh
-    // => compute how much threads we can launch
-    uint32_t tmp = nInserted/SMALLEST_ROUND;
-    while(tmp>0){
-      tmp/=ROUND_RATIO;
-      *nthreads*=THREAD_RATIO;
+  for (unsigned i=0; i<10; i++) {
+    if(passes[i] < SMALLEST_ROUND || passes[i]/8 < nInserted){
+      passes[i+1] = 0;
+      npasses = i+1;
+      break;
     }
+    passes[i+1] = passes[i]/7.5;
   }
 
-  if(nToInsert < passes[1]){
-    passes[1] = nToInsert;
-    npasses = 1;
-  }
-  else{
-    npasses = 2;
-    for (uint32_t i=ROUND_RATIO*MAX(passes[1],SMALLEST_ROUND); i<nToInsert; i*=ROUND_RATIO)
-      passes[npasses++] = i;
-
-    passes[npasses] = nToInsert;
+  for(unsigned i=0; i<=npasses/2; i++){
+    uint32_t tmp = passes[i];
+    passes[i] = passes[npasses-i];
+    passes[npasses-i] = tmp;
   }
 
   return npasses;
 }
 
-
-
+/******************************************
+ * initialisation of the TetLocal structure
+ ******************************************/
 static inline HXTStatus localInit(TetLocal* local){
-    local->sizeBnd = 1020; // accounting for the offset in aligned malloc, to avoid additional memory page
-    local->numBnd = 0;
-    local->bnd = NULL;
-    local->sizeDeleted = DELETED_BUFFER_SIZE;
-    local->numDeleted = 0;
-    local->deleted = NULL;
-    local->sizeVertices = 1014;
-    local->numVertices = 0;
-    local->vertices = NULL;
-
-    HXT_CHECK( hxtAlignedMalloc(&local->bnd, local->sizeBnd*sizeof(local->bnd[0])) );
-    HXT_CHECK( hxtAlignedMalloc(&local->deleted, local->sizeDeleted*sizeof(uint64_t)) );
-    HXT_CHECK( hxtAlignedMalloc(&local->vertices, local->sizeVertices*sizeof(uint32_t)) );
+    local->ball.size = 1020; // accounting for the offset in aligned malloc, to avoid additional memory page
+    local->ball.num = 0;
+    local->ball.bnd = NULL;
+    local->deleted.size = DELETED_BUFFER_SIZE;
+    local->deleted.num = 0;
+    local->deleted.tetID = NULL;
+
+    HXT_CHECK( hxtAlignedMalloc(&local->ball.bnd, local->ball.size*sizeof(cavityBnd_t)) );
+    HXT_CHECK( hxtAlignedMalloc(&local->deleted.tetID, local->deleted.size*sizeof(uint64_t)) );
 
     return HXT_STATUS_OK;
 }
 
-
+/***********************************************
+           re-allocation functions
+ ***********************************************/
 static HXTStatus synchronizeReallocation(HXTMesh* mesh, volatile int* toCopy, volatile int* copy){
   // threads cant be doing something while the realloc portion happen
   #pragma omp barrier
   
-  // this unable us to have the same value of toCopy fore everyone, as we are sure nothing happens to those variables here
+  // this unable us to have the same value of toCopy for everyone, as we are sure nothing happens to those variables here
   if(toCopy!=copy){
     *copy = *toCopy;
   }
 
-  HXTStatus fail = HXT_STATUS_OK;
+  HXTStatus status = HXT_STATUS_OK;
   // make reallocations in a critical section
   #pragma omp single
   {
-    uint64_t nTet = mesh->tetrahedra.num;
-    uint64_t tetSize = mesh->tetrahedra.size;
-    if(nTet > tetSize){
-      fail = hxtAlignedRealloc(&mesh->tetrahedra.neigh, nTet*8*sizeof(uint64_t));
-      if(fail>=0) fail = hxtAlignedRealloc(&mesh->tetrahedra.node, nTet*8*sizeof(uint32_t));
-      if(fail>=0) fail = hxtAlignedRealloc(&mesh->tetrahedra.colors, nTet*2*sizeof(uint16_t));
-      mesh->tetrahedra.size = 2*nTet;
-      HXT_INFO("reallocation happened\n");
+    if(mesh->tetrahedra.num > mesh->tetrahedra.size){
+      status = hxtTetrahedraDoubleSize(mesh);
     }
   } // implicit barrier here
 
-  if(fail<0){
-    HXT_TRACE(fail);
-    return fail;
-  }
+  if(status!=HXT_STATUS_OK)
+    HXT_TRACE(status);
 
-  return HXT_STATUS_OK;
+  return status;
 }
 
 
@@ -314,1390 +256,1617 @@ static inline HXTStatus reserveNewTet(HXTMesh* mesh){
 }
 
 static inline HXTStatus reserveNewDeleted(TetLocal* local, uint64_t num){
-  if(num > local->sizeDeleted){
-      HXT_CHECK( hxtAlignedRealloc(&local->deleted, 2*num*sizeof(uint64_t)) );
-      local->sizeDeleted = 2*num;
+  num += local->deleted.num;
+  if(num > local->deleted.size){
+      HXT_CHECK( hxtAlignedRealloc(&local->deleted.tetID, 2*num*sizeof(uint64_t)) );
+      local->deleted.size = 2*num;
   }
 
   return HXT_STATUS_OK;
 }
 
 static inline HXTStatus reserveNewBnd(TetLocal* local, uint64_t num){
-  if(num > local->sizeBnd){
-      HXT_CHECK( hxtAlignedRealloc(&local->bnd, 2*num*sizeof(local->bnd[0])) );
-      local->sizeBnd = 2*num;
+  num += local->ball.num;
+  if(num > local->ball.size){
+      HXT_CHECK( hxtAlignedRealloc(&local->ball.bnd, 2*num*sizeof(cavityBnd_t)) );
+      local->ball.size = 2*num;
   }
 
   return HXT_STATUS_OK;
 }
+/***********************************************/
 
-
-static inline HXTStatus reserveNewVertices(TetLocal* local, uint32_t num){
-  if(num > local->sizeVertices){
-      HXT_CHECK( hxtAlignedRealloc(&local->vertices, 2*num*sizeof(uint32_t)) );
-      local->sizeVertices = 2*num;
-  }
-
-  return HXT_STATUS_OK;
-}
-
-
-
-static inline HXTStatus checkTetrahedron(hxtVertex* vertices, TetLocal* local, const uint32_t* nodes){
-  uint64_t rel = local->endDist - local->startDist;
-
-  if(rel==UINT64_MAX) // we are working with one thread only...
+/************************************
+ * check if a tetrahedra is entirely
+ * in the calling thread's partition
+ ***********************************/
+static inline HXTStatus checkTetrahedron(HXTVertex* vertices, TetLocal* local, const uint32_t* nodes){
+  /* Actually, one vertex (not more) could be in another partition without creating a conflict.
+   However, all threads would have to have a verticesID array => a lot of memory space wasted.
+   Instead, we only allow the ghost vertex to be in another partition, it is handle differently in
+   computeAdjacenciesFast function */
+  uint64_t rel = local->partition.endDist - local->partition.startDist;
+
+  if(local->partition.endDist==UINT64_MAX) // if we are working with one thread only
     return HXT_STATUS_OK;
 
-  // unsigned wrap around is defined by the standard, so we improve the speed
-  uint64_t d0 = vertices[nodes[0]].padding.hilbertDist - local->startDist;
-  uint64_t d1 = vertices[nodes[1]].padding.hilbertDist - local->startDist;
-  uint64_t d2 = vertices[nodes[2]].padding.hilbertDist - local->startDist;
-  uint64_t d3 = (nodes[3]==HXT_GHOST_VERTEX?0:vertices[nodes[3]].padding.hilbertDist) - local->startDist;
-
-#if defined( HXT_DELAUNAY_MAX_MEMORY ) || defined( HXT_DELAUNAY_LOW_MEMORY )
-  if( (d0>=rel) + (d1>=rel) + (d2>=rel) + (d3>=rel) > 1)
-    return HXT_STATUS_INTERNAL;
-#else
+  // unsigned wrap around is defined by the standard
+  uint64_t d0 = vertices[nodes[0]].padding.hilbertDist - local->partition.startDist;
+  uint64_t d1 = vertices[nodes[1]].padding.hilbertDist - local->partition.startDist;
+  uint64_t d2 = vertices[nodes[2]].padding.hilbertDist - local->partition.startDist;
+  uint64_t d3 = nodes[3]==HXT_GHOST_VERTEX ? d2: (vertices[nodes[3]].padding.hilbertDist - local->partition.startDist);
+  
   if((d0>=rel) || (d1>=rel) || (d2>=rel) || (d3>=rel))
     return HXT_STATUS_INTERNAL;
-#endif
 
   return HXT_STATUS_OK;
 
 }
 
 
-#define HXT_SQR(X) (X)*(X)
+static inline HXTStatus pointIsTooClose(const double* __restrict__ p1, const double* __restrict__ p2, double nodalSize){
+  double d2 = (p1[0]-p2[0])*(p1[0]-p2[0])
+            + (p1[1]-p2[1])*(p1[1]-p2[1])
+            + (p1[2]-p2[2])*(p1[2]-p2[2]); 
+  if (d2 < 0.8*0.8*nodalSize*nodalSize){
+    return  HXT_STATUS_INTERNAL;
+  }
+
+  return HXT_STATUS_OK;
+}
 
-static inline HXTStatus filterCavity (TetLocal* local, HXTMesh *mesh, const double *nodalSizes)
+/* if one edge of the cavity is shorter than the nodalSize, return HXT_STATUS_INTERNAL */
+static inline HXTStatus filterCavity (TetLocal* local, HXTMesh *mesh, const double *nodalSizes, const uint32_t vta)
 {
-  uint32_t vta = local->bnd[0].node[0];
   double *vtaCoord = mesh->vertices.coord + 4*vta;
   double vtaNodalSize = nodalSizes[vta];
 
-  for (uint64_t i = 0 ; i< local->numBnd ; i++) {
-    for (uint64_t j=1;j<4;j++) {
-      uint32_t nodej = local->bnd[i].node[j];
+  for (uint64_t i = 0 ; i< local->ball.num ; i++) {
+    for (unsigned j=0;j<3;j++) {
+      uint32_t nodej = local->ball.bnd[i].node[j];
 
       if (j!=3 || nodej != HXT_GHOST_VERTEX){
         double *Xj = mesh->vertices.coord + 4*nodej;
-        double d2 = HXT_SQR (vtaCoord[0]-Xj[0])
-                  + HXT_SQR (vtaCoord[1]-Xj[1])
-                  + HXT_SQR (vtaCoord[2]-Xj[2]);
-        double minSizeAllowed = 0.8 * 0.5 * ( vtaNodalSize + nodalSizes[nodej]);  
-        if (d2 < HXT_SQR(minSizeAllowed)){
-          return  HXT_STATUS_INTERNAL;
-        }
+        HXT_CHECK( pointIsTooClose(vtaCoord, Xj, 0.5*( vtaNodalSize + nodalSizes[nodej])) );
       }
     }
   }
   return  HXT_STATUS_OK;
 }
 
+static inline HXTStatus filterTet(HXTMesh* mesh, const double *nodalSizes, const uint64_t curTet, const uint32_t vta){
+  HXTVertex* vertices = (HXTVertex*) mesh->vertices.coord;
 
-static inline void restoreDeleted(HXTMesh* mesh, TetLocal* local, const uint64_t prevDeleted, const uint16_t color){
-  for (uint64_t i=prevDeleted; i<local->numDeleted; i++)
-    mesh->tetrahedra.colors[local->deleted[i]/4] = color;
+  double *vtaCoord = vertices[vta].coord;
+  double vtaNodalSize = nodalSizes[vta];
 
-  local->numDeleted = prevDeleted;
+  for (unsigned j=0; j<4; j++) {
+    uint32_t nodej = mesh->tetrahedra.node[4*curTet+j];
+
+    if (j!=3 || nodej != HXT_GHOST_VERTEX){
+      double* Xj = vertices[nodej].coord;
+      double otherNodalSize = nodalSizes[nodej];
+      if(otherNodalSize==DBL_MAX){
+        otherNodalSize = vtaNodalSize;
+      }
+      HXT_CHECK( pointIsTooClose(vtaCoord, Xj, 0.5*( vtaNodalSize + otherNodalSize)) );
+    }
+  }
+  return HXT_STATUS_OK;
 }
 
 
-static inline HXTStatus doTheWork(HXTMesh* mesh,
-                                  uint32_t* verticesID,
-                                  TetLocal* local,
-                                  const double* nodalSizes,
-                                  uint64_t* curTet,
-                                  const uint32_t vta){
-  const uint64_t prevDeleted = local->numDeleted;
+/* restore the structure as it was before the failed insertion attempt */
+static inline void restoreDeleted(HXTMesh* mesh, TetLocal* local, const uint64_t prevDeleted, const uint16_t color){
+  for (uint64_t i=prevDeleted; i<local->deleted.num; i++)
+    unmarkTetAsDeleted(mesh, local->deleted.tetID[i]);
 
-  HXT_CHECK( walking2Cavity(mesh, local, curTet, vta) );
+  local->deleted.num = prevDeleted;
+}
 
 
-  const uint16_t color = mesh->tetrahedra.colors[*curTet/4];
-  HXTStatus status = diggingACavity(mesh, local, *curTet, vta, color);
+/***********************************
+ * insphere predicate & perturbation
+ ***********************************/
+// see Perturbations and Vertex Removal in a 3D Delaunay Triangulation, O. Devillers & M. Teillaud
+static double symbolicPerturbation (uint32_t indices[5] ,  const double* __restrict__ i,
+                                                           const double* __restrict__ j,
+                                                           const double* __restrict__ k,
+                                                           const double* __restrict__ l,
+                                                           const double* __restrict__ m){
+  double const* pt[5] = {i,j,k,l,m};
 
-  if(status==HXT_STATUS_INTERNAL){
-    restoreDeleted(mesh, local, prevDeleted, color);
-    return HXT_STATUS_TRYAGAIN;
-  }
-  else{
-    HXT_CHECK(status);
-  }
+  // Sort the five points such that their indices are in the increasing
+  //   order. An optimized bubble sort algorithm is used, i.e., it has
+  //   the worst case O(n^2) runtime, but it is usually much faster.
+  int swaps = 0; // Record the total number of swaps.
+  int n = 5;
+  int count;
+  do {
+    count = 0;
+    n = n - 1;
+    for (int iter = 0; iter < n; iter++) {
+      if (indices[iter] > indices[iter+1]) {
 
-  // printf("deleted: %lu, bnd:%lu\n", local->numDeleted, local->numBnd);
-  if(nodalSizes!=NULL && filterCavity(local, mesh, nodalSizes)==HXT_STATUS_INTERNAL){
-    restoreDeleted(mesh, local, prevDeleted, color);
-    return HXT_STATUS_FALSE;
+        const double *swappt = pt[iter];
+        pt[iter] = pt[iter+1];
+        pt[iter+1] = swappt;
+
+        uint32_t sw = indices [iter];
+        indices[iter] = indices[iter+1];
+        indices[iter+1] = sw;
+        count++;
+      }
+    }
+    swaps += count;
+  } while (count > 0); // Continue if some points are swapped.
+  
+  double oriA = orient3d(pt[1], pt[2], pt[3], pt[4]);
+  if (oriA != 0.0) {
+    // Flip the sign if there are odd number of swaps.
+    if ((swaps % 2) != 0) oriA = -oriA;
+    return oriA;
   }
+  
+  double oriB = -orient3d(pt[0], pt[2], pt[3], pt[4]);
+  if (oriB == 0.0) HXT_WARNING("Symbolic perturbation failed (2 superposed vertices ?)");
 
+  // Flip the sign if there are odd number of swaps.
+  if ((swaps % 2) != 0) oriB = -oriB;
+  return oriB;
+}
 
-  if(local->numBnd > local->numDeleted){
-    uint64_t needed = MAX(DELETED_BUFFER_SIZE-local->numDeleted, local->numBnd - local->numDeleted);
 
-    uint64_t ntet;
+/* wrapper around the insphere predicate that handles
+   the ghost vertex and symbolic perturbation if needed */
+double tetInsphere(HXTMesh* mesh, const uint64_t curTet, const uint32_t vta){
+  HXTVertex* vertices = (HXTVertex*) mesh->vertices.coord;
+  uint32_t* Node = mesh->tetrahedra.node + curTet;
 
-    #pragma omp atomic capture
-    { ntet = mesh->tetrahedra.num; mesh->tetrahedra.num+=needed;}
+  const double* __restrict__ a = vertices[Node[0]].coord;
+  const double* __restrict__ b = vertices[Node[1]].coord;
+  const double* __restrict__ c = vertices[Node[2]].coord;
+  const double* __restrict__ e = vertices[vta].coord;
 
-    reserveNewTet(mesh);
-    reserveNewDeleted(local, local->numDeleted+needed);
+  if(Node[3]==HXT_GHOST_VERTEX){ 
+    double det = orient3d(a,b,c,e);
 
-    #pragma omp simd
-    for (uint64_t i=0; i<needed; i++){
-      local->deleted[local->numDeleted+i] = 4*(ntet+i);
-      mesh->tetrahedra.colors[ntet+i] = HXT_DELETED_COLOR;
+    if(det!=0.0){
+      return det;
     }
 
-    local->numDeleted+=needed;
+    // we never go here, except when point are aligned on boundary
+    // HXT_INFO("insphere using opposite vertex");
+    uint32_t oppositeNode = mesh->tetrahedra.node[mesh->tetrahedra.neigh[curTet+3]];
+    double* const __restrict__ oppositeVertex = vertices[oppositeNode].coord;
+    det = insphere(a,b,c,oppositeVertex,e);
+
+    if (det == 0.0) {
+      uint32_t nn[5] = {Node[0],Node[1],Node[2],oppositeNode,vta};
+      // HXT_INFO("symbolic perturbation on boundary");
+      det = symbolicPerturbation (nn, a,b,c,oppositeVertex,e);
+      
+    }
+    return -det;
   }
 
-  HXT_CHECK( fillingACavity(mesh, local, verticesID, curTet, color) );
+  double* const __restrict__ d = vertices[Node[3]].coord;
 
-  return HXT_STATUS_TRUE;
+  double det = insphere(a,b,c,d,e);
+  if (det == 0.0) {
+    uint32_t nn[5] = {Node[0],Node[1],Node[2],Node[3],vta};
+    // HXT_INFO("symbolic perturbation");
+    det = symbolicPerturbation (nn, a,b,c,d,e);
+  }
+  return det;
 }
 
 
+/***********************************
+ * walk to cavity
+ ***********************************/
+static HXTStatus walking2Cavity(HXTMesh* mesh, TetLocal* local, uint64_t* __restrict__ curTet, const uint32_t vta){
+  uint64_t nextTet = *curTet;
+  uint32_t seed = 1;
+  HXTVertex* vertices = (HXTVertex*) mesh->vertices.coord;
 
+  /* if nextTet is a ghost triangle, go to the neighbor that is not a ghost triangle */
+  if(mesh->tetrahedra.node[4*nextTet+3]==HXT_GHOST_VERTEX)
+    nextTet = mesh->tetrahedra.neigh[4*nextTet+3]/4;
 
-static HXTStatus hxtTetrahedraCompute(HXTMesh* mesh,
-                                      HXTDelaunayOptions* options,
-                                      hxtNodeInfo* nodeInfo,
-                                      const uint64_t nToInsert,
-                                      int noReordering)
-{
+  double* const vtaCoord = vertices[vta].coord;
+  unsigned enteringFace = 4;
 
-  double time = omp_get_wtime();
+#ifndef NDEBUG
+  uint64_t TotalCount = 0;
+#endif
+  
 
-  uint64_t numTetrahedra = mesh->tetrahedra.num;
-  uint32_t totalNumSkipped = 0;
+  while(1){
+    const uint32_t* __restrict__ curNode = mesh->tetrahedra.node + 4*nextTet;
+    const uint64_t* __restrict__ curNeigh = mesh->tetrahedra.neigh + 4*nextTet;
 
-  // third, divide indices in different passes
-  const int maxThreads = omp_get_max_threads();
-  int threadToBeginWith;
-  uint32_t passes[12];
+  #ifndef NDEBUG
+    if(curNode[3]==HXT_GHOST_VERTEX){
+      return HXT_ERROR_MSG(HXT_STATUS_FAILED, "walked outside of the domain");
+    }
+  #endif
 
-  unsigned npasses = computePasses(passes, options->numVerticesInMesh, nToInsert, &threadToBeginWith);
-  threadToBeginWith = MIN(threadToBeginWith, maxThreads);
+    unsigned neigh = 4;
+    unsigned outside = 0;
+    uint32_t randomU = hxtReproducibleLCG(&seed);
+    for (unsigned i=0; i<4; i++)
+    {
+      uint32_t index = (i+randomU)%4;
+      if (index!=enteringFace) {
+        // we walk where the volume is minimum
+        const double* __restrict__ a = vertices[curNode[(index+1)&3]].coord;
+        const double* __restrict__ b = vertices[curNode[(index&2)^3]].coord;
+        const double* __restrict__ c = vertices[curNode[(index+3)&2]].coord;
+
+        if (orient3d(a,b,c, vtaCoord) < 0.0){
+          if(curNeigh[index]==HXT_NO_ADJACENT) { // the point is outside the triangulation
+            return HXT_ERROR_MSG(HXT_STATUS_ERROR,
+                                "vertex {%f %f %f} outside the triangulation and no ghost tetrahedra",
+                                 vtaCoord[0], vtaCoord[1], vtaCoord[2]);
+          }
 
-  // that ugly cast because people want an array of double into the mesh structure
-  hxtVertex* vertices = (hxtVertex*) mesh->vertices.coord;
-  
+          uint64_t tet = curNeigh[index]/4;
+          const uint32_t* __restrict__ neighNodes = mesh->tetrahedra.node + tet*4;
+          if(checkTetrahedron(vertices, local, neighNodes)==HXT_STATUS_OK){
+            if(neighNodes[3]==HXT_GHOST_VERTEX){
+              *curTet = tet;
+              return HXT_STATUS_OK;
+            }
+            neigh=index;
+            break;
+          }
+          outside = 1;
+        }
+      }
+    }
 
-  /******************************************************
-          shuffle (and optimize cache locality)
-  ******************************************************/
-  if(noReordering){
-    // shuffle nodeInfo
-    HXT_CHECK( hxtNodeInfoShuffle(nodeInfo, nToInsert) );
-  }
-  else{
-    hxtVertex* verticesToInsert = vertices + mesh->vertices.num - nToInsert;
-    // shuffle the vertices to insert, then sort each pass except the first according to the hilbert curve...
-    HXT_CHECK( hxtVerticesShuffle(verticesToInsert, nToInsert) );
+    if(neigh==4){
+      const double* __restrict__ a = vertices[curNode[0]].coord;
+      const double* __restrict__ b = vertices[curNode[1]].coord;
+      const double* __restrict__ c = vertices[curNode[2]].coord;
+      const double* __restrict__ d = vertices[curNode[3]].coord;
+      if(outside ||
+         (orient3d(a,b,c,vtaCoord)<=0.0) +
+         (orient3d(a,b,vtaCoord,d)<=0.0) +
+         (orient3d(a,vtaCoord,c,d)<=0.0) +
+         (orient3d(vtaCoord,b,c,d)<=0.0)>2){
+        return HXT_STATUS_TRYAGAIN;
+      }
+      *curTet = nextTet;
+      return HXT_STATUS_OK;
+    }
 
-    uint32_t nbits = 0;
-    HXT_CHECK( hxtVerticesHilbertDist(options->bbox, verticesToInsert, nToInsert, &nbits, NULL) );
+    //    printf("nextTet %u %g %u %u\n",nextTet,Min, count, neigh);
+    nextTet = curNeigh[neigh]/4;
+    enteringFace = curNeigh[neigh]&3;
 
-    for (unsigned i=1; i<npasses; i++) {
-      HXT_CHECK( hxtVerticesSort(verticesToInsert+passes[i], passes[i+1]-passes[i], nbits) );
+  #ifndef NDEBUG
+    if(TotalCount>mesh->tetrahedra.num){
+      return HXT_ERROR_MSG(HXT_STATUS_FAILED, "infinite walk to find the cavity");
     }
+    // printf("%lu\n",TotalCount);
+    TotalCount++;
+  #endif
   }
-
-  /******************************************************
-        Initializations and allocations
-  ******************************************************/
-  if(mesh->tetrahedra.num<5){
-    HXT_INFO_COND(options->verbosity>0,
-                  "Initializing mesh");
-    HXT_CHECK( hxtTetrahedraInit(mesh, nodeInfo, nToInsert) );
-    options->numVerticesInMesh = 4; // not counting the ghost vertex
-    passes[0] = 4;
-  }
+}
 
 
-  uint32_t*  verticesID;
-#if defined( HXT_DELAUNAY_MAX_MEMORY )
-  HXT_CHECK( hxtAlignedMalloc(&verticesID, maxThreads*(mesh->vertices.num+SIMD_ALIGN/4)*sizeof(uint32_t)) );
-#elif defined( HXT_DELAUNAY_LOW_MEMORY )
-  verticesID = NULL; // we do not need it
-#else
-  HXT_CHECK( hxtAlignedMalloc(&verticesID, (mesh->vertices.num+1)*sizeof(uint32_t)) );
-#endif
+/***********************************
+ * digging cavity
+ ***********************************/
 
-  TetLocal* Locals;
-  HXT_CHECK( hxtMalloc(&Locals, maxThreads*sizeof(TetLocal)) );
-  // HXT_CHECK( hxtMalloc())
+/* pushing cavity boundary information to local->ball */
+static inline void bndPush( TetLocal* local, uint16_t flag,
+              const uint32_t node1, const uint32_t node2,
+              const uint32_t node3, const uint64_t neigh){
+  uint64_t n = local->ball.num;
+  local->ball.bnd[n].node[0] = node1;
+  local->ball.bnd[n].node[1] = node2;
+  local->ball.bnd[n].node[2] = node3;
+  local->ball.bnd[n].flag = flag;
+  local->ball.bnd[n].neigh = neigh;
+  local->ball.num++;
+}
 
-  for (int i=0; i<maxThreads; i++)
-    localInit(&Locals[i]);
+/* delete a tetrahedra being part of the cavity */
+static inline HXTStatus deletedPush(HXTMesh* mesh, TetLocal* local, const uint64_t neigh){
+  // check if 3 points of the new tetrahedra are owned by this thread
+  HXT_CHECK( checkTetrahedron((HXTVertex*) mesh->vertices.coord, local, mesh->tetrahedra.node + neigh*4) );
+  local->deleted.tetID[local->deleted.num++] = neigh;
+  markTetAsDeleted(mesh, neigh);
 
+  return HXT_STATUS_OK;
+}
 
-  HXT_INFO_COND(options->verbosity>0,
-                "insertion of %10u vertices (%3d threads )\t- mesh.nvert: %-10u",
-                passes[npasses] - passes[0], maxThreads, options->numVerticesInMesh);
+/* check if the cavity is star shaped
+   This isn't usefull for pure Delaunay but when we constrain cavity with colors,
+   it is usefull */
+static HXTStatus isStarShaped(TetLocal* local, HXTMesh* mesh, const uint32_t vta, uint64_t* blindFaceIndex)
+{
+  HXTVertex* vertices = (HXTVertex*) mesh->vertices.coord;
+  double *vtaCoord = vertices[vta].coord;
 
-  for (uint32_t p=0; p<npasses; p++)
-  {
+  for (uint64_t i=0; i<local->ball.num; i++) {
+    if(local->ball.bnd[i].node[2]==HXT_GHOST_VERTEX){
 
-    double percent = 200;
+    }
+    else{
+      double* b = vertices[local->ball.bnd[i].node[0]].coord;
+      double* c = vertices[local->ball.bnd[i].node[1]].coord;
+      double* d = vertices[local->ball.bnd[i].node[2]].coord;
+      if(orient3d(vtaCoord, b, c, d)<=0.0){
+        *blindFaceIndex = i;
+        return HXT_STATUS_INTERNAL;
+      }
+    }
+  }
+  return HXT_STATUS_OK;
+}
 
-    int nthreads = threadToBeginWith;
-    threadToBeginWith=MIN(threadToBeginWith*THREAD_RATIO, maxThreads);
-    const uint32_t initialPassLength = passes[p+1] - passes[p];
 
-    for(uint32_t n=0; passes[p+1]-passes[p]; n++)
-    {
-      const uint32_t passStart = passes[p];
-      const uint32_t passEnd = passes[p+1];
-      const uint32_t passLength = passEnd - passStart;
-
-      /******************************************************
-                      choosing number of threads
-      ******************************************************/
-      
-
-      if(percent<140/nthreads || passLength<SMALLEST_ROUND){
-        nthreads=1;
-      }
-      else if(percent<20){
-        nthreads/=2;
-      }
-      else if(passLength < nthreads*SMALLEST_ROUND)
-        nthreads/=2;
+static HXTStatus undeleteTetrahedron(TetLocal* local, HXTMesh* mesh, const uint32_t vta, uint64_t tetToUndelete) {
+  // the tetrahedra should not be deleted anymore
+  for (uint64_t i=local->deleted.num; ; i--) {
+    if(local->deleted.tetID[i-1]==tetToUndelete) {
+      local->deleted.num--;
+      local->deleted.tetID[i-1] = local->deleted.tetID[local->deleted.num];
+      break;
+    }
+#ifdef DEBUG
+    if(i==1)
+      return HXT_ERROR_MSG(HXT_STATUS_ERROR, "could not find the tetrahedra in the deleted array");
+#endif
+  }
+  unmarkTetAsDeleted(mesh, tetToUndelete);
+
+  uint64_t bndFaces[4] = {HXT_NO_ADJACENT, HXT_NO_ADJACENT, HXT_NO_ADJACENT, HXT_NO_ADJACENT};
+  int nbndFace = 0;
+
+  // we should update the boundary (that's the difficult part...)
+  // first remove all the boundary faces that come from the tetrahedron we just remove from the cavity
+  for (uint64_t i=local->ball.num; nbndFace<4 && i>0; i--) {
+    if(mesh->tetrahedra.neigh[local->ball.bnd[i-1].neigh]/4==tetToUndelete) {
+      bndFaces[nbndFace++] = local->ball.bnd[i-1].neigh;
+      local->ball.num--;
+      local->ball.bnd[i-1] = local->ball.bnd[local->ball.num];
+    }
+  }
 
+  // we must replace them by all the other faces of the tetrahedron we just removed
+  const uint64_t* __restrict__ curNeigh = mesh->tetrahedra.neigh + tetToUndelete*4;
+  const uint32_t* __restrict__ curNode = mesh->tetrahedra.node + tetToUndelete*4;
 
-      /******************************************************
-                      Sorting vertices
-      ******************************************************/
+#ifdef DEBUG
+  int nbndFace2 = (isTetDeleted(mesh, curNeigh[0]/4)==0) + (isTetDeleted(mesh, curNeigh[1]/4)==0) + (isTetDeleted(mesh, curNeigh[2]/4)==0) + (isTetDeleted(mesh, curNeigh[3]/4)==0);
+  if(nbndFace!=nbndFace2)
+    return HXT_ERROR_MSG(HXT_STATUS_ERROR, "found %d non-deleted tet adjacent to the tet we unremove but there should be %d %lu %lu %lu %lu", nbndFace, nbndFace2, bndFaces[0], bndFaces[1], bndFaces[2], bndFaces[3]);
+#endif
 
-      double hxtDeclareAligned bboxShift[4]={0.5,0.5,0.5,0};
+  HXT_CHECK( reserveNewBnd(local, 3) );
 
+  if(curNeigh[0]!=bndFaces[0] && curNeigh[0]!=bndFaces[1] && curNeigh[0]!=bndFaces[2] && curNeigh[0]!=bndFaces[3])
+    bndPush(local,  (mesh->tetrahedra.flag[tetToUndelete] & 0x9) | 
+                    (isEdgeConstrained(mesh, tetToUndelete, 0 , 2)>>1) |
+                    (isEdgeConstrained(mesh, tetToUndelete, 0 , 1)<<1), curNode[2], curNode[1], curNode[3], 4*tetToUndelete+0);
 
-      if(percent<100 && nthreads>1)
-      {
-        bboxShift[0] = (double) rand()/RAND_MAX;
-        bboxShift[1] = (double) rand()/RAND_MAX;
-        bboxShift[2] = (double) rand()/RAND_MAX;
-        bboxShift[3] = (double) rand()/RAND_MAX; // this is not a bbox deformation, it's an index shift
-      }
+  if(curNeigh[1]!=bndFaces[0] && curNeigh[1]!=bndFaces[1] && curNeigh[1]!=bndFaces[2] && curNeigh[1]!=bndFaces[3])
+    bndPush(local,  ((mesh->tetrahedra.flag[tetToUndelete] & 0xD0)>>4) | 
+                    isEdgeConstrained(mesh, tetToUndelete, 0 , 1), curNode[0], curNode[2], curNode[3], 4*tetToUndelete+1);
 
-      uint32_t nbits = hxtAdvancedHilbertBits(initialPassLength, passLength, nthreads, maxThreads);
-      if(noReordering){
-        HXT_CHECK( hxtVerticesHilbertDist(options->bbox, vertices, mesh->vertices.num, &nbits, bboxShift) );
-      }
-      else{
-        HXT_CHECK( hxtVerticesHilbertDist(options->bbox, vertices, mesh->vertices.num - nToInsert + passEnd, &nbits, bboxShift) );
-      }
-      
+  if(curNeigh[2]!=bndFaces[0] && curNeigh[2]!=bndFaces[1] && curNeigh[2]!=bndFaces[2] && curNeigh[2]!=bndFaces[3])
+    bndPush(local,  ((mesh->tetrahedra.flag[tetToUndelete] & 0x900)>>8) | 
+                    (isEdgeConstrained(mesh, tetToUndelete, 1 , 2)>>5) |
+                     isEdgeConstrained(mesh, tetToUndelete, 0 , 2), curNode[1], curNode[0], curNode[3], 4*tetToUndelete+2);
 
-      #pragma omp parallel for simd aligned(nodeInfo:SIMD_ALIGN)
-      for (uint32_t i=passStart; i<passEnd; i++) {
-        nodeInfo[i].hilbertDist = vertices[nodeInfo[i].node].padding.hilbertDist;
-      }
+  if(curNeigh[3]!=bndFaces[0] && curNeigh[3]!=bndFaces[1] && curNeigh[3]!=bndFaces[2] && curNeigh[3]!=bndFaces[3])
+    bndPush(local, (isFacetConstrained(mesh, 4*tetToUndelete+3)>>12) | 
+                   (isEdgeConstrained(mesh, tetToUndelete, 0 , 3)>>2) |
+                   (isEdgeConstrained(mesh, tetToUndelete, 1 , 3)>>5) |
+                   (isEdgeConstrained(mesh, tetToUndelete, 2 , 3)>>8), curNode[0], curNode[1], curNode[2], 4*tetToUndelete+3);
 
-      if(n!=0){
-        HXT_CHECK( hxtNodeInfoSort(nodeInfo + passStart, passLength, nbits) );
-      }
+  return HXT_STATUS_OK;
+}
 
-      const uint32_t rem = passLength%nthreads;
-      const uint32_t step = passLength/nthreads;
 
-      uint32_t indexShift = MIN(step-1,(uint32_t) bboxShift[3]*step);
+static HXTStatus reshapeCavityIfNeeded(TetLocal* local, HXTMesh* mesh, const uint32_t vta, uint64_t prevDeleted) {
+  // we will remove the tetrahedra adjacent to the face that does not see the point, progressively, until the cavity is star shaped...
+  uint64_t blindFace = 0;
+  while(isStarShaped(local, mesh, vta, &blindFace)==HXT_STATUS_INTERNAL)
+  {
+    // printf("deleting %lu  cavity:%lu  ball:%lu\n",mesh->tetrahedra.neigh[local->ball.bnd[blindFace].neigh]/4, local->deleted.num-prevDeleted, local->ball.num );
+    HXT_CHECK( undeleteTetrahedron(local, mesh, vta, mesh->tetrahedra.neigh[local->ball.bnd[blindFace].neigh]/4) );
+  }
+  return HXT_STATUS_OK;
+}
 
-      int threadFinished = 0;
 
-      #pragma omp parallel num_threads(nthreads)
-      {
-        uint64_t curTet = 0; // we always begin with the first tet. (index 0)
-        const int threadID = omp_get_thread_num();
-        nthreads = omp_get_num_threads();
+static HXTStatus respectEdgeConstraint(TetLocal* local, HXTMesh* mesh, const uint32_t vta, const uint16_t color, const uint64_t prevDeleted) {
+  // HXT_WARNING("a constrained edge was inside the cavity, recovering it");
 
-        uint32_t localStart;
-        uint32_t localN;
-        int foundTet = 0;
+  // all the tetrahedron have the same color 'color', we will use that color to flag them
+  for (uint64_t i=prevDeleted; i<local->deleted.num; i++) {
+    uint64_t delTet = local->deleted.tetID[i];
+    mesh->tetrahedra.colors[delTet] = 0;
+  }
 
-        if(nthreads>1){
-          // if(threadID<nthreads){
+  for (uint64_t i=prevDeleted; i<local->deleted.num; i++) {
+    uint64_t delTet = local->deleted.tetID[i];
+    int exist = 1;
+    for (int j=0; exist && j<4; j++) {
+      for (int k=j+1; exist && k<4; k++) {
+        if(isEdgeConstrained(mesh, delTet, j, k) && (mesh->tetrahedra.colors[delTet] & (1U<<(j*4+k)))==0) {
+          // const int _UP_FACET[4][4] = {{-1, 2, 3, 1},
+          //                              { 3,-1, 0, 2},
+          //                              { 1, 3,-1, 0},
+          //                              { 2, 0, 1,-1}};
+
+          // HXT_WARNING("found constrained edge %u-%u in tetrahedron %lu (%d %d) inside the cavity\n",mesh->tetrahedra.node[4*delTet+ _UP_FACET[j][k]], mesh->tetrahedra.node[4*delTet+ _UP_FACET[k][j]], delTet, j, k);
+          // turn around the edge to check if a facet is on the boundary of the cavity. If yes, the edge is processed... if not, we delete one tet.
+          int in_facet = j;
+          int out_facet = k;
+
+          int edgeIsSafe = 0;
+          uint64_t curTet = delTet;
+          // first turn
+          do
+          {
+            uint32_t newV = mesh->tetrahedra.node[4*curTet + in_facet];
 
-          /******************************************************
-                          Making partitions
-          ******************************************************/
+            // go into the neighbor through out_facet
+            uint64_t neigh = mesh->tetrahedra.neigh[4*curTet + out_facet];
+            curTet = neigh/4;
+            in_facet = neigh%4;
 
-          // printf("bbox: %f %f %f shift = %u/%u\n", bboxShift[0], bboxShift[1], bboxShift[2], indexShift%step, step);
-          localStart = step*threadID + ((threadID>=rem)?rem:threadID) + indexShift;
-          uint64_t dist = nodeInfo[passStart + localStart].hilbertDist;
-          
-          uint32_t up = 1;
-          while(localStart+up<passLength && dist==nodeInfo[passStart + localStart + up].hilbertDist)
-            up++;
+            uint32_t* nodes = mesh->tetrahedra.node + 4*curTet;
+            for (out_facet=0; out_facet<3; out_facet++)
+              if(nodes[out_facet]==newV)
+                break;
 
-          uint32_t down = 0;
-          while(localStart>down && dist==nodeInfo[passStart + localStart-down-1].hilbertDist)
-            down--;
+            if(isTetDeleted(mesh, curTet)!=0) {
+              // mark that the edge as been treate
+              #ifdef DEBUG
+                if((mesh->tetrahedra.colors[curTet] & (1U<<(in_facet<out_facet?in_facet*4+out_facet:out_facet*4+in_facet)))!=0)
+                  return HXT_ERROR_MSG(HXT_STATUS_ERROR, "the flag says that the tet has already been processed for this edge...");
+              #endif
+              mesh->tetrahedra.colors[curTet] |= (1U<<(in_facet*4+out_facet)) | (1U<<(out_facet*4+in_facet));
+            }
+            else {
+              edgeIsSafe=1;
+            }
 
-          if(up>down && threadID!=0 &&
-                nodeInfo[passStart
-                 + step*(threadID-1) + (((threadID-1)>=rem)?rem:(threadID-1))
-                 + indexShift].hilbertDist
-                ==dist)
-              localStart -=down;
-          else
-            localStart += up;
-        
-          Locals[threadID].localStart = localStart;
-          // }
+            // printf("edge %u-%u, tet %lu (%d %d)  deleted:%u\n", mesh->tetrahedra.node[4*curTet+ _UP_FACET[in_facet][out_facet]], mesh->tetrahedra.node[4*curTet+ _UP_FACET[out_facet][in_facet]], curTet, in_facet, out_facet, isTetDeleted(mesh, curTet)!=0);
 
-          #pragma omp barrier
+          } while (curTet!=delTet);
 
-          // if(threadID<nthreads){
-          uint32_t localEnd = Locals[(threadID+1)%nthreads].localStart;
-          // if localStart = localEnd, the length is 0
-          localN = (localEnd + passLength - localStart)%passLength;
+          if(!edgeIsSafe) { // we must find a tetrahedron on the opposite side of vta and delete it.
+            in_facet = j;
+            out_facet = k;
+            curTet = delTet;
 
-          Locals[threadID].startDist = nodeInfo[passStart + localStart].hilbertDist;
-          Locals[threadID].endDist = nodeInfo[passStart + localEnd].hilbertDist;
+            uint64_t tetContainingVta = local->deleted.tetID[prevDeleted];
+            uint64_t tetToUndelete = HXT_NO_ADJACENT;
+            double distMax = 0.0;
+            double* vtaCoord = mesh->vertices.coord + 4*vta;
 
-          /******************************************************
-                          find starting tetrahedron
-          ******************************************************/
+          #ifdef DEBUG
+            double* a = mesh->vertices.coord + 4*mesh->tetrahedra.node[4*tetContainingVta];
+            double* b = mesh->vertices.coord + 4*mesh->tetrahedra.node[4*tetContainingVta+1];
+            double* c = mesh->vertices.coord + 4*mesh->tetrahedra.node[4*tetContainingVta+2];
+            double* d = mesh->vertices.coord + 4*mesh->tetrahedra.node[4*tetContainingVta+3];
 
-          for (uint64_t i=0; i<mesh->tetrahedra.num; i++)
-          {
-            curTet = i*4;
-            if(mesh->tetrahedra.colors[i]!=HXT_DELETED_COLOR &&
-               checkTetrahedron(vertices, &Locals[threadID], mesh->tetrahedra.node + curTet )==HXT_STATUS_OK)
-            {
-              foundTet = 1;
-              break;
+            if(orient3d(vtaCoord,b,c,d)<0.0 || orient3d(a,vtaCoord,c,d)<0.0 || orient3d(a,b,vtaCoord,d)<0.0 || orient3d(a,b,c,vtaCoord)<0.0) {
+              return HXT_ERROR_MSG(HXT_STATUS_ERROR, "an edge part of a ghost tetrahedron is constrained");
             }
-          }
-          // }
-
-          #pragma omp barrier
-        }
-        else
-          // if(threadID==0)
-        {
+          #endif
 
-          /******************************************************
-             single-thread partition and starting tetrahedron
-          ******************************************************/
-          localStart = 0;
-          localN = passLength;
-          Locals[0].startDist = 0;
-          Locals[0].endDist = UINT64_MAX;
-
-          for (uint64_t i=0; i<mesh->tetrahedra.num; i++)
-          { 
-            if(mesh->tetrahedra.colors[i]!=HXT_DELETED_COLOR){
-              curTet = i*4;
-              foundTet = 1;
-              break;
-            }
-          }
-        }
+            do
+            {
+              uint32_t newV = mesh->tetrahedra.node[4*curTet + in_facet];
 
-        if (foundTet == 0) {
-          HXT_INFO_COND(options->verbosity>1,
-                        "thread %d did not find any tetrahedron to begin with", threadID);
-        }
+              // go into the neighbor through out_facet
+              uint64_t neigh = mesh->tetrahedra.neigh[4*curTet + out_facet];
+              curTet = neigh/4;
+              in_facet = neigh%4;
 
+              uint32_t* nodes = mesh->tetrahedra.node + 4*curTet;
+              for (out_facet=0; out_facet<3; out_facet++)
+                if(nodes[out_facet]==newV)
+                  break;
 
-        // if(threadID<nthreads){
-        if(foundTet!=0){
+              double* coord1 = mesh->vertices.coord + newV;
+              double* coord2 = mesh->vertices.coord + nodes[in_facet];
 
-          /******************************************************
-                          vertices insertion
-          ******************************************************/
-          for (uint32_t i=0; i<localN; i++)
-          {
-            uint32_t passIndex = (localStart+i)%passLength;
-            uint32_t vta = nodeInfo[passStart + passIndex].node;
-            #ifdef HXT_DELAUNAY_MAX_MEMORY
-              HXTStatus status = doTheWork(mesh,
-                                           verticesID+((threadID*(mesh->vertices.num+SIMD_ALIGN/4)) & ~(SIMD_ALIGN/4 - 1)),
-                                           &Locals[threadID],
-                                           options->nodalSizes,
-                                           &curTet,
-                                           vta);
-            #else
-              HXTStatus status = doTheWork(mesh, verticesID, &Locals[threadID], options->nodalSizes, &curTet, vta);
-            #endif
-
-            switch(status){
-              case HXT_STATUS_TRYAGAIN:
-                // ;
-                if(nthreads==1){
-                  double* vtaCoord = vertices[vta].coord;
-                  HXT_WARNING("skipping supposedly duplicate vertex (%f %f %f)", vtaCoord[0], vtaCoord[1], vtaCoord[2]);
-                  nodeInfo[passStart + passIndex].status = HXT_STATUS_FALSE;
-                  break;
+              if(curTet!=tetContainingVta) {
+                double dist = 0.0;
+                for (int l=0; l<3; l++) {
+                  double meanCoord = (coord1[l]+coord2[l])*0.5;
+                  double diff = meanCoord-vtaCoord[l];
+                  dist += diff*diff;
                 }
-              case HXT_STATUS_FALSE:
-              case HXT_STATUS_TRUE:
-                nodeInfo[passStart + passIndex].status = status;
-                break;
-              default: // error other than HXT_STATUS_TRYAGAIN cause the program to return
-                nodeInfo[passStart + passIndex].status = HXT_STATUS_TRYAGAIN;
-                HXT_OMP_CHECK( status );
-                break;
-            }
-          }  
-        }
-        // }
-
-        #pragma omp atomic update
-        threadFinished++;
 
-        int val = 0;
-        do{
-          // threads are waiting here for a reallocation
-          synchronizeReallocation(mesh, &threadFinished, &val);
-        }while(val<nthreads);
-        // }while(val<maxThreads);
+                if(dist>distMax) {
+                  dist = distMax;
+                  tetToUndelete = curTet;
+                }
+              }
+            } while (curTet!=delTet);
 
-        // HXT_INFO("thread %d terminated", threadID, threadFinished, nthreads);
-      }
+            if(tetToUndelete==delTet)
+              exist = 0;
 
-      /******************************************************
-      vertices that have to be tried again are put at the end
-      ******************************************************/
-      // everything above i+shift is HXT_STATUS_TRYAGAIN
-      uint32_t shift = 0;
-      unsigned numSkipped = 0;
-      for (uint32_t i=passEnd; i>passStart;)
-      {
-        i--;
-        if(nodeInfo[i].status!=HXT_STATUS_TRYAGAIN){
-          if(nodeInfo[i].status==HXT_STATUS_FALSE)
-            numSkipped++;
-          shift++;
-        }
-        else if(shift!=0) {
-          hxtNodeInfo tmp = nodeInfo[i];
-          nodeInfo[i] = nodeInfo[i+shift];
-          nodeInfo[i+shift] = tmp;
+            // printf("undeleting tetrahedron %lu\n", tetToUndelete);
+            mesh->tetrahedra.colors[tetToUndelete] = color;
+            HXT_CHECK( undeleteTetrahedron(local, mesh, vta, tetToUndelete) );
+          }
         }
       }
-
-      options->numVerticesInMesh += shift - numSkipped;
-
-      percent = (shift-numSkipped)*100.0/(passes[p+1]-passes[p]-numSkipped);
-      totalNumSkipped += numSkipped;
-
-      HXT_INFO_COND(options->verbosity>1,
-                    "%3d thrd |%10u/%-10u-> %*.1f%-*c\t- mesh.nvert: %-10u",
-                    nthreads, shift, passLength, MIN(8,n/2)+5, percent, 8-MIN(8,n/2),'%', options->numVerticesInMesh);
-      
-      passes[p] += shift;
     }
   }
 
-  /******************************************************
-                  Cleaning
-  ******************************************************/
-
-  HXT_CHECK( hxtRemoveDeleted(mesh) );
-
-  for (int i=0; i<maxThreads; i++){
-    HXT_CHECK( hxtAlignedFree(&Locals[i].deleted) );
-    HXT_CHECK( hxtAlignedFree(&Locals[i].bnd) );
-    HXT_CHECK( hxtAlignedFree(&Locals[i].vertices) );
+  for (uint64_t i=prevDeleted; i<local->deleted.num; i++) {
+    uint64_t delTet = local->deleted.tetID[i];
+    mesh->tetrahedra.colors[delTet] = color;
   }
 
-  HXT_CHECK( hxtAlignedFree(&verticesID) );
-  HXT_CHECK( hxtFree(&Locals) );
-
-  /***************************************************************
-    if reordering allowed, remove vertices we could not insert
-  ***************************************************************/
-  if(!noReordering && totalNumSkipped!=0){
-    /* remove deleted vertices and change tetrahedra.node accordingly */
+  return HXT_STATUS_OK;
+}
 
-    uint32_t* numInserted;
-    HXT_CHECK( hxtAlignedMalloc(&numInserted, maxThreads*sizeof(uint32_t)) );
 
-    uint32_t firstShifted = mesh->vertices.num - nToInsert;
-    uint32_t n = nToInsert;
+/* this function does a Breadth-first search of the tetrahedra in the cavity
+ * it add those to local->deleted
+ * it also maintain a local->bnd array with all the information concerning the boundary of the cavity
+ */
+static inline HXTStatus diggingACavity(HXTMesh* mesh, TetLocal* local, uint64_t curTet, const uint32_t vta, int* edgeConstraint){
+  // add tetrahedra to cavity
+  local->deleted.tetID[local->deleted.num++] = curTet;
+  markTetAsDeleted(mesh, curTet);
+  local->ball.num = 0;
 
-    // when a vertex was skipped, nodeInfo[i].status = HXT_STATUS_FALSE
-    #pragma omp parallel
-    {
-      // uint32_t localFirstSkipped = UINT32_MAX;
+  
 
-      // 1st: mark vertices with their corresponding status
-      #pragma omp for schedule(static)
-      for (uint32_t i=0; i<nToInsert; i++) {
-        uint32_t index = nodeInfo[i].node;
-        HXTStatus status = nodeInfo[i].status;
-        vertices[index].padding.status = status;
-        // if(status!=HXT_STATUS_TRUE && localFirstSkipped>i)
-        //   localFirstSkipped = i;
-      }
+  for(uint64_t start=local->deleted.num-1; start < local->deleted.num; start++){
+    uint64_t curTet = local->deleted.tetID[start];
+    const uint64_t* __restrict__ curNeigh = mesh->tetrahedra.neigh + 4*curTet;
+    const uint32_t* __restrict__ curNode = mesh->tetrahedra.node + 4*curTet;
 
-      #pragma omp barrier
-      #pragma omp single
-      {
-        uint32_t i = 0;
-        while (vertices[firstShifted+i].padding.status==HXT_STATUS_TRUE) i++;
+    *edgeConstraint += isAnyEdgeConstrained(mesh, curTet)!=0;
 
-        firstShifted += i+1;
-        n -= i+1;
-      }
+    /* here we allocate enough space for the boundary (local->bnd), the cavity (local->deleted) and the vertices (local->vertices) */
+    HXT_CHECK( reserveNewDeleted(local, 4) );
+    HXT_CHECK( reserveNewBnd(local, 4) );
 
-      uint32_t start = 0;
-      int threadID = omp_get_thread_num();
-      numInserted[threadID] = 0;
+    // we unrolled the loop for speed (also because indices are not trivial, we would need a 4X4 array)
 
-      #pragma omp for schedule(static)
-      for (uint32_t i=0; i<n; i++) {
-        if(vertices[firstShifted+i].padding.status==HXT_STATUS_TRUE)
-          numInserted[threadID]++;
+    /* and here we push stuff to local->bnd or local->deleted, always keeping ghost tet at last place */
+    uint64_t neigh = curNeigh[0]/4;
+    if(curNeigh[0]!=HXT_NO_ADJACENT && isTetDeleted(mesh, neigh)==0){
+      if(isFacetConstrained(mesh, 4*curTet+0) || 
+        tetInsphere(mesh, neigh*4, vta)<=0.0){
+        bndPush(local, mesh->tetrahedra.flag[curTet] & 0xF,
+                       curNode[1], curNode[2], curNode[3], curNeigh[0]);
       }
-
-      for (int i=0; i<threadID; i++) {
-        start+=numInserted[i];
+      else{
+        HXT_CHECK( deletedPush(mesh, local, neigh) );
       }
-      start += firstShifted-1;
-
-      // 3rd: compute where each vertices will be
-      #pragma omp for schedule(static)
-      for (uint32_t i=0; i<n; i++) {
-        uint32_t oldStart = start;
-
-        if(vertices[firstShifted+i].padding.status==HXT_STATUS_TRUE)
-          start++;
+    }
 
-        vertices[firstShifted+i].padding.index = oldStart;
+    neigh = curNeigh[1]/4;
+    if(curNeigh[1]!=HXT_NO_ADJACENT && isTetDeleted(mesh, neigh)==0){
+      if(isFacetConstrained(mesh, 4*curTet+1) || 
+        tetInsphere(mesh, neigh*4, vta)<=0.0){
+        bndPush(local, (isFacetConstrained(mesh, 4*curTet+1)>>4) | 
+                       (isEdgeConstrained(mesh, curTet, 1 , 2)>>5) |
+                       (isEdgeConstrained(mesh, curTet, 0 , 1)<<1) |
+                       (isEdgeConstrained(mesh, curTet, 1 , 3)>>4),
+                       curNode[2], curNode[0], curNode[3], curNeigh[1]);
       }
-
-      // 4th: update tetrahedra.node accordingly
-      #pragma omp for
-      for (uint64_t i=0; i<4*mesh->tetrahedra.num; i++) {
-        uint32_t index = mesh->tetrahedra.node[i];
-        if(index>=firstShifted && index!=HXT_GHOST_VERTEX)
-          mesh->tetrahedra.node[i] = vertices[index].padding.index;
+      else{
+        HXT_CHECK( deletedPush(mesh, local, neigh) );
       }
     }
 
-HXT_ASSERT(totalNumSkipped==mesh->vertices.num - vertices[mesh->vertices.num-1].padding.index - 1);
-
-    HXT_CHECK( hxtAlignedFree(&numInserted) );
-
-    // 5th: put vertices at the right indices
-    for (uint32_t i=firstShifted; i<mesh->vertices.num; i++) {
-      vertices[vertices[i].padding.index] = vertices[i];
+    neigh = curNeigh[2]/4;
+    if(curNeigh[2]!=HXT_NO_ADJACENT && isTetDeleted(mesh, neigh)==0){
+      if(isFacetConstrained(mesh, 4*curTet+2)|| 
+        tetInsphere(mesh, neigh*4, vta)<=0.0){
+        bndPush(local, (isFacetConstrained(mesh, 4*curTet+2)>>8) | 
+                       (isEdgeConstrained(mesh, curTet, 0 , 2)>>1) |
+                       (isEdgeConstrained(mesh, curTet, 1 , 2)>>4) |
+                       (isEdgeConstrained(mesh, curTet, 2 , 3)>>8),
+                       curNode[0], curNode[1], curNode[3], curNeigh[2]);
+      }
+      else{
+        HXT_CHECK( deletedPush(mesh, local, neigh) );
+      }
     }
 
-    if(options->verbosity>1)
-      HXT_INFO("%u vertices removed (vertices not inserted in the mesh are removed when using hxtDelaunay)", totalNumSkipped);
-    else if(options->verbosity>0)
-      HXT_INFO("%u vertices removed", totalNumSkipped);
-
-
-    mesh->vertices.num = mesh->vertices.num - totalNumSkipped;
-  }
-
-  if(options->verbosity>0){
-    time = omp_get_wtime() - time;
-    long long int tetCreated = (long long int) mesh->tetrahedra.num - (long long int) numTetrahedra;
-    HXT_INFO("took %f second for %lld tetrahedra (%.3f Mtet./s)",
-                  time, tetCreated , tetCreated/time/1000000.0);
+    neigh = curNeigh[3]/4;
+    if(curNeigh[3]!=HXT_NO_ADJACENT && isTetDeleted(mesh, neigh)==0){
+      if(isFacetConstrained(mesh, 4*curTet+3) || 
+        tetInsphere(mesh, neigh*4, vta)<=0.0){
+        
+        bndPush(local, (isFacetConstrained(mesh, 4*curTet+3)>>12) | 
+                       (isEdgeConstrained(mesh, curTet, 1 , 3)>>6) |
+                       (isEdgeConstrained(mesh, curTet, 0 , 3)>>1) |
+                       (isEdgeConstrained(mesh, curTet, 2 , 3)>>8),
+                       // there are 2 valid order for nodes: 1,0,2,3 and 0,2,1,3
+                       curNode[1], curNode[0], curNode[2], curNeigh[3]);
+      }
+      else{
+        HXT_CHECK( deletedPush(mesh, local, neigh) );
+      }
+    }
   }
 
   return HXT_STATUS_OK;
 }
 
 
+/**************************************************************
+ * compute adjacencies with a matrix O(1) insertion and search
+ **************************************************************/
+#ifndef HXT_DELAUNAY_LOW_MEMORY
+static inline HXTStatus computeAdjacenciesFast(HXTMesh* mesh, TetLocal* local, uint32_t* __restrict__ verticesID, const uint64_t blength){
+  cavityBnd_t* __restrict__ bnd = local->ball.bnd;
 
-static HXTStatus DelaunayOptionsInit(HXTMesh* mesh,
-                                HXTDelaunayOptions* userOptions,
-                                HXTDelaunayOptions* options,
-                                hxtBbox* bbox){
-HXT_ASSERT(mesh!=NULL);
+#ifndef NDEBUG
+  int ghost_is_there = 0;
+#endif
 
-  if(userOptions!=NULL){
-    options->bbox = userOptions->bbox;
-    options->nodalSizes = userOptions->nodalSizes;
-    options->verbosity = userOptions->verbosity;
-    options->numVerticesInMesh = userOptions->numVerticesInMesh;
-  }
-  else{
-    // default parameters
-    options->bbox = NULL;
-    options->nodalSizes = NULL;
-    options->verbosity = 0;
+HXT_ASSERT(((size_t) bnd)%SIMD_ALIGN==0);
+HXT_ASSERT(((size_t) verticesID)%SIMD_ALIGN==0);
 
-    // count the number of vertices in the mesh
-    #pragma omp parallel for
-    for (uint32_t i=0; i<mesh->vertices.num; i++) {
-      mesh->vertices.coord[4*i+3] = 0.0;
+  #pragma omp simd aligned(verticesID,bnd:SIMD_ALIGN)
+  for (uint32_t i=0; i<blength; i++){
+    verticesID[bnd[i].node[0]] = UINT32_MAX;
+    verticesID[bnd[i].node[1]] = UINT32_MAX;
+    if(bnd[i].node[2]!=HXT_GHOST_VERTEX){
+      verticesID[bnd[i].node[2]] = UINT32_MAX;
     }
+  }
 
-    #pragma omp parallel for
-    for (uint64_t i=0; i<4*mesh->tetrahedra.num; i++) {
-      mesh->vertices.coord[4*mesh->tetrahedra.node[i]+3] = 1.0;
+  uint32_t npts = 1;
+  for (uint32_t i=0; i<blength; i++)
+  {
+    if(verticesID[bnd[i].node[0]]>npts){
+      verticesID[bnd[i].node[0]] = npts++;
+    }
+    bnd[i].node[0] = verticesID[bnd[i].node[0]];
+    if(verticesID[bnd[i].node[1]]>npts){
+      verticesID[bnd[i].node[1]] = npts++;
     }
+    bnd[i].node[1] = verticesID[bnd[i].node[1]];
 
-    uint32_t numVerticesInMesh = 0;
-    #pragma omp parallel for reduction(+:numVerticesInMesh)
-    for (uint32_t i=0; i<mesh->vertices.num; i++) {
-        numVerticesInMesh += mesh->vertices.coord[4*i+3];
+    if(bnd[i].node[2]==HXT_GHOST_VERTEX){
+      bnd[i].node[2] = 0;
+#ifndef NDEBUG
+      ghost_is_there = 1;
+#endif
+    }
+    else{
+      if(verticesID[bnd[i].node[2]]>npts){
+        verticesID[bnd[i].node[2]] = npts++;
+      }
+      bnd[i].node[2] = verticesID[bnd[i].node[2]];
     }
 
-    options->numVerticesInMesh = numVerticesInMesh;
   }
 
-HXT_ASSERT(options->numVerticesInMesh <= mesh->vertices.num);
+  HXT_ASSERT_MSG((npts-3+ghost_is_there)*2==blength, "Failed to compute adjacencies (f) %u (%u ghost) vertices and %u cavity boundaries", npts-1+ghost_is_there, ghost_is_there, blength); // symbol undefined
 
-  if(options->bbox==NULL){
-    options->bbox = bbox;
-    hxtBboxInit(bbox);
-    HXT_CHECK( hxtBboxAdd(bbox, mesh->vertices.coord, mesh->vertices.num) );
+  #pragma omp simd aligned(verticesID:SIMD_ALIGN)
+  for (uint32_t i=0; i<blength; i++)
+  {
+    local->Map[bnd[i].node[0]*32 + bnd[i].node[1]] = bnd[i].neigh + 3;
+    local->Map[bnd[i].node[1]*32 + bnd[i].node[2]] = bnd[i].neigh + 1;
+    local->Map[bnd[i].node[2]*32 + bnd[i].node[0]] = bnd[i].neigh + 2;
   }
 
-  // for the predicates to work
-  exactinit(options->bbox->max[0]-options->bbox->min[0],
-            options->bbox->max[1]-options->bbox->min[1],
-            options->bbox->max[2]-options->bbox->min[2]);
+  #pragma omp simd aligned(verticesID:SIMD_ALIGN)
+  for (uint32_t i=0; i<blength; i++)
+  {
+    mesh->tetrahedra.neigh[bnd[i].neigh + 1] = local->Map[bnd[i].node[2]*32 + bnd[i].node[1]];
+    mesh->tetrahedra.neigh[bnd[i].neigh + 2] = local->Map[bnd[i].node[0]*32 + bnd[i].node[2]];
+    mesh->tetrahedra.neigh[bnd[i].neigh + 3] = local->Map[bnd[i].node[1]*32 + bnd[i].node[0]];
+  }
 
   return HXT_STATUS_OK;
 }
+#endif
 
 
+/**************************************************************
+ * compute adjacencies with a matrix O(n) insertion and search
+ **************************************************************/
+static inline HXTStatus computeAdjacenciesSlow(HXTMesh* mesh, TetLocal* local, const uint64_t start, const uint64_t blength){
 
-HXTStatus hxtDelaunay(HXTMesh* mesh, HXTDelaunayOptions* userOptions){
-  HXTDelaunayOptions options;
-  hxtBbox bbox;
-  HXT_CHECK( DelaunayOptionsInit(mesh, userOptions, &options, &bbox) );
+  uint64_t tlength = 0;
+  const uint64_t middle = blength*3/2; // 3N
 
-  const uint64_t nToInsert = mesh->vertices.num - options.numVerticesInMesh;
+  // N+2 point on the surface of the cavity
+  // 2N triangle on the surface of the cavity, x3 (4*0.5+1) data = 6N+9 uint64_t
+  // => enough place for the 3N edge x2 data = 6N uint64_t
+  uint64_t* Tmp = (uint64_t*) local->ball.bnd;
+  const unsigned index[4] = {2,3,1,2};
 
-  hxtNodeInfo* nodeInfo;
-  HXT_CHECK( hxtAlignedMalloc(&nodeInfo, nToInsert*sizeof(hxtNodeInfo)) );
-  
-  // we fill nodeInfo with the indices of each vertices to insert...
-  #pragma omp parallel for simd
-  for (uint32_t i=0; i<nToInsert; i++) {
-    nodeInfo[i].node = options.numVerticesInMesh + i;
-    // nodeInfo[i].status = HXT_STATUS_TRYAGAIN;
-  }
+  for (uint64_t i=0; i<blength; i++)
+  {
+    uint64_t curTet = local->deleted.tetID[start+ i];
+    const uint32_t* __restrict__ Node = mesh->tetrahedra.node + 4*curTet;
 
-  hxtTetrahedraCompute(mesh, &options, nodeInfo, nToInsert, 0);
+    // pointer to the position of Node[0] in the Tmp array
+    for (unsigned j=0; j<3; j++)
+    {
+      // define the edge by the minimum vertex and the other
+      uint64_t key = ((uint64_t) Node[index[j]]<<32) + Node[index[j+1]];
 
-  HXT_CHECK( hxtAlignedFree(&nodeInfo) );
+      // linear searching/pushing into Tmp
+      uint64_t k;
+      for (k=0; k<tlength; k++) // this is the only nested loop... the one that cost it all
+      {
+        __assume_aligned(Tmp, SIMD_ALIGN);
+        if(Tmp[k]==key)
+          break;
+      }
+
+      uint64_t curFace = 4*curTet+j+1;
 
+      // we did not found it
+      if(k==tlength){
+        Tmp[tlength] = (key>>32) + (key<<32);
+        Tmp[middle + tlength] = curFace;
+        tlength++;
+      }
+      else{// we found the neighbour !
+        uint64_t pairValue = Tmp[middle+k];
+        mesh->tetrahedra.neigh[curFace] = pairValue;
+        mesh->tetrahedra.neigh[pairValue] = curFace;
+        tlength--;
+        if(k<tlength){// put the last entry in the one we just discovered
+          Tmp[k] = Tmp[tlength];
+          Tmp[middle+k] = Tmp[middle + tlength];
+        }
+      }
+    }
+  }
+  HXT_ASSERT_MSG(tlength==0, "Failed to compute adjacencies (s)"); // verify that all neighbor were found
   return HXT_STATUS_OK;
 }
 
 
+/****************************************
+ * filling back the cavity (DelaunayBall)
+ ****************************************/
+static inline HXTStatus fillingACavity(HXTMesh* mesh, TetLocal* local, uint32_t* __restrict__ verticesID, uint64_t* __restrict__ curTet, const uint32_t vta, const uint16_t color){
+  uint64_t clength = local->deleted.num;
+  uint64_t blength = local->ball.num;
 
-HXTStatus hxtDelaunaySteadyVertices(HXTMesh* mesh, HXTDelaunayOptions* userOptions, hxtNodeInfo* nodeInfo, uint64_t nToInsert){
-  HXT_ASSERT(nodeInfo!=NULL);
+  uint64_t start = clength - blength;
 
-  HXTDelaunayOptions options;
-  hxtBbox bbox;
-  HXT_CHECK( DelaunayOptionsInit(mesh, userOptions, &options, &bbox) );
+  // #pragma vector aligned
+  #pragma omp simd
+  for (uint64_t i=0; i<blength; i++)
+  {
 
-HXT_ASSERT(options.numVerticesInMesh+nToInsert <= mesh->vertices.num);
+    __assume_aligned(local->deleted.tetID, SIMD_ALIGN);
+    __assume_aligned(local->ball.bnd, SIMD_ALIGN);
+    __assume_aligned(mesh->tetrahedra.colors, SIMD_ALIGN);
+    __assume_aligned(mesh->tetrahedra.flag, SIMD_ALIGN);
+    __assume_aligned(mesh->tetrahedra.node, SIMD_ALIGN);
+    __assume_aligned(mesh->tetrahedra.neigh, SIMD_ALIGN);
+
+    const uint64_t newTet = local->deleted.tetID[i + start];
+    uint32_t* __restrict__ Node = mesh->tetrahedra.node + 4*newTet;
+    mesh->tetrahedra.colors[newTet] = color;
+    mesh->tetrahedra.flag[newTet] = 0;
+
+    /* we need to always put the ghost vertex at the fourth slot*/
+    Node[0] = vta;
+    Node[1] = local->ball.bnd[i].node[0];
+    Node[2] = local->ball.bnd[i].node[1];
+    Node[3] = local->ball.bnd[i].node[2];
+
+    const uint64_t neigh = local->ball.bnd[i].neigh;
+    mesh->tetrahedra.neigh[4*newTet] = neigh;
+
+    mesh->tetrahedra.flag[newTet] = local->ball.bnd[i].flag;
+
+    // update neighbor's neighbor
+    mesh->tetrahedra.neigh[neigh] = 4*newTet;
+
+    // we recycle neigh to contain newTet (used in computeAdjacencies)
+    local->ball.bnd[i].neigh = 4*newTet;
+  }
+#ifndef HXT_DELAUNAY_LOW_MEMORY
+  if(blength<=58){ // N+2<=31 => N<=29 => 2N<=58
+  #ifndef NDEBUG
+    HXT_CHECK( computeAdjacenciesFast(mesh, local, verticesID, blength) );
+  #else
+    computeAdjacenciesFast(mesh, local, verticesID, blength);
+  #endif
+  }
+  else
+#endif
+  {
+  #ifndef NDEBUG
+    HXT_CHECK(computeAdjacenciesSlow(mesh, local, start, blength) );
+  #else
+    computeAdjacenciesSlow(mesh, local, start, blength);
+  #endif
+  }
+
+
+
+  *curTet = local->deleted.tetID[start];
+  local->deleted.num = start;
 
-  hxtTetrahedraCompute(mesh, &options, nodeInfo, nToInsert, 1);
   return HXT_STATUS_OK;
 }
 
 
+/*************************************************************
+ * insert a single point
+ ************************************************************/
+static inline HXTStatus insertion(HXTMesh* mesh,
+                                  uint32_t* verticesID,
+                                  TetLocal* local,
+                                  const double* nodalSizes,
+                                  uint64_t* curTet,
+                                  const uint32_t vta,
+                                  int perfectlyDelaunay){
+  const uint64_t prevDeleted = local->deleted.num;
 
-/***********************************
- * insphere predicate & perturbation
- ***********************************/
-// see Perturbations and Vertex Removal in a 3D Delaunay Triangulation, O. Devillers & M. Teillaud
-static double symbolicPerturbation (uint32_t indices[5] ,  const double* __restrict__ i,
-                                                                  const double* __restrict__ j,
-                                                                  const double* __restrict__ k,
-                                                                  const double* __restrict__ l,
-                                                                  const double* __restrict__ m){
-  
-  double const* pt[5] = {i,j,k,l,m};
+  HXT_CHECK( walking2Cavity(mesh, local, curTet, vta) );
 
-  // Sort the five points such that their indices are in the increasing
-  //   order. An optimized bubble sort algorithm is used, i.e., it has
-  //   the worst case O(n^2) runtime, but it is usually much faster.
-  int swaps = 0; // Record the total number of swaps.
-  int n = 5;
-  int count;
-  do {
-    count = 0;
-    n = n - 1;
-    for (int iter = 0; iter < n; iter++) {
-      if (indices[iter] > indices[iter+1]) {
+  if(nodalSizes!=NULL && filterTet(mesh, nodalSizes, *curTet, vta)){
+    return HXT_STATUS_FALSE;
+  }
 
-        const double *swappt = pt[iter];
-        pt[iter] = pt[iter+1];
-        pt[iter+1] = swappt;
+  const uint16_t color = mesh->tetrahedra.colors[*curTet];
+  int edgeConstraint = 0;
+  HXTStatus status = diggingACavity(mesh, local, *curTet, vta, &edgeConstraint);
 
-        uint32_t sw = indices [iter];
-        indices[iter] = indices[iter+1];
-        indices[iter+1] = sw;
-        count++;
-      }
-    }
-    swaps += count;
-  } while (count > 0); // Continue if some points are swapped.
-  
-  double oriA = orient3d(pt[1], pt[2], pt[3], pt[4]);
-  if (oriA != 0.0) {
-    // Flip the sign if there are odd number of swaps.
-    if ((swaps % 2) != 0) oriA = -oriA;
-    return oriA;
+  if(status==HXT_STATUS_INTERNAL){
+    restoreDeleted(mesh, local, prevDeleted, color);
+    return HXT_STATUS_TRYAGAIN;
+  }
+  else{
+    HXT_CHECK(status);
   }
-  
-  double oriB = -orient3d(pt[0], pt[2], pt[3], pt[4]);
-  if (oriB == 0.0) HXT_WARNING("Symbolic perturbation failed (2 superposed vertices ?)");
 
-  // Flip the sign if there are odd number of swaps.
-  if ((swaps % 2) != 0) oriB = -oriB;
-  return oriB;
-}
+  if(edgeConstraint) {
+    HXT_CHECK( respectEdgeConstraint(local, mesh, vta, color, prevDeleted) );
+  }
 
+  // uint64_t face = 0;
+  // if(!perfectlyDelaunay && isStarShaped(local, mesh, vta, &face)==HXT_STATUS_INTERNAL) {
+  //   restoreDeleted(mesh, local, prevDeleted, color);
+  //   return HXT_STATUS_FALSE;
+  // }
 
-// there could be multiple stuff to accelerate the predicate. first see if orient3D to 2 face is negative
-// orient3D + orient3D with perpendicular at the vertex
-// see if the distance is more than what the radius could be... visually, you can know
-double tetInsphere(HXTMesh* mesh, const uint64_t curTet, const uint32_t vta){
-  uint32_t* Node = mesh->tetrahedra.node + curTet;
+  // reshape the cavity if it is not star shaped
+  if(!perfectlyDelaunay)
+    HXT_CHECK( reshapeCavityIfNeeded(local, mesh, vta, prevDeleted) );
 
-  const double* __restrict__ a = mesh->vertices.coord + 4*Node[0];
-  const double* __restrict__ b = mesh->vertices.coord + 4*Node[1];
-  const double* __restrict__ c = mesh->vertices.coord + 4*Node[2];
-  const double* __restrict__ e = mesh->vertices.coord + 4*vta;
+  if(nodalSizes!=NULL && filterCavity(local, mesh, nodalSizes, vta)) {
+    restoreDeleted(mesh, local, prevDeleted, color);
+    return HXT_STATUS_FALSE;
+  }
 
-  if(Node[3]==HXT_GHOST_VERTEX){ 
-    double det = orient3d(a,b,c,e);
 
-    if(det!=0.0){
-      return det;
-    }
+  if(local->ball.num > local->deleted.num){
+    uint64_t needed = MAX(DELETED_BUFFER_SIZE,local->ball.num)-local->deleted.num;
 
-    // we never go here, except when point are aligned on boundary
-    // HXT_INFO("insphere using opposite vertex");
-    uint32_t oppositeNode = mesh->tetrahedra.node[mesh->tetrahedra.neigh[curTet+3]];
-    double* const __restrict__ oppositeVertex = mesh->vertices.coord + 4*oppositeNode;
-    det = insphere(a,b,c,oppositeVertex,e);
+    uint64_t ntet;
 
-    if (det == 0.0) {
-      uint32_t nn[5] = {Node[0],Node[1],Node[2],oppositeNode,vta};
-      // HXT_INFO("symbolic perturbation on boundary");
-      det = symbolicPerturbation (nn, a,b,c,oppositeVertex,e);
-      
+    #pragma omp atomic capture
+    { ntet = mesh->tetrahedra.num; mesh->tetrahedra.num+=needed;}
+
+    reserveNewTet(mesh);
+    reserveNewDeleted(local, needed);
+
+    #pragma omp simd
+    for (uint64_t i=0; i<needed; i++){
+      local->deleted.tetID[local->deleted.num+i] = ntet+i;
+      mesh->tetrahedra.flag[ntet+i] = 0;
+      markTetAsDeleted(mesh, ntet+i);
     }
-    return -det;
+
+    local->deleted.num+=needed;
   }
 
-  double* const __restrict__ d = mesh->vertices.coord + 4*Node[3];
+  HXT_CHECK( fillingACavity(mesh, local, verticesID, curTet, vta, color) );
 
-  double det = insphere(a,b,c,d,e);
-  if (det == 0.0) {
-    uint32_t nn[5] = {Node[0],Node[1],Node[2],Node[3],vta};
-    // HXT_INFO("symbolic perturbation");
-    det = symbolicPerturbation (nn, a,b,c,d,e);
-  }
-  return det;
+  return HXT_STATUS_TRUE;
 }
 
 
+/*************************************************************
+ * Delaunay triangulation of a set of points
+ ************************************************************/
+static HXTStatus parallelDelaunay3D(HXTMesh* mesh,
+                                    HXTDelaunayOptions* options,
+                                    hxtNodeInfo* nodeInfo,
+                                    const uint32_t nToInsert,
+                                    int noReordering)
+{
+  uint32_t totalNumSkipped = 0;
+  uint32_t seed = 1;
 
+  // third, divide indices in different passes
+  const int maxThreads = options->delaunayThreads;
+  const int perfectlyDelaunay = mesh->tetrahedra.num<=5;
 
+  uint32_t passes[12];
+  unsigned npasses = computePasses(passes, options->numVerticesInMesh, nToInsert);
 
+  // that ugly cast because people want an array of double into the mesh structure
+  HXTVertex* vertices = (HXTVertex*) mesh->vertices.coord;
+  
 
-/***********************************
- * walk to cavity
- ***********************************/
+  /******************************************************
+          shuffle (and optimize cache locality)
+  ******************************************************/
+  if(noReordering){
+    // shuffle nodeInfo
+    HXT_CHECK( hxtNodeInfoShuffle(nodeInfo, nToInsert) );
+  }
+  else {
+    HXT_INFO_COND(options->verbosity>1, "Reordering vertices from %u to %u", mesh->vertices.num - nToInsert, mesh->vertices.num);
+    HXTVertex* verticesToInsert = vertices + mesh->vertices.num - nToInsert;
 
-static inline HXTStatus walking2Cavity(HXTMesh* mesh, TetLocal* local, uint64_t* __restrict__ curTet, const uint32_t vta){
-  uint64_t nextTet = *curTet;
+    if(options->nodalSizes==NULL){
+      // shuffle the vertices to insert, then sort each pass except the first according to the hilbert curve...
+      HXT_CHECK( hxtVerticesShuffle(verticesToInsert, nToInsert) );
+    }
+    else{
+      HXT_CHECK( hxtNodeInfoShuffle(nodeInfo, nToInsert) );
+    }
 
-  /* if nextTet is a ghost triangle, go to the neighbor that is not a ghost triangle */
-  if(mesh->tetrahedra.node[nextTet+3]==HXT_GHOST_VERTEX)
-    nextTet = mesh->tetrahedra.neigh[nextTet+3]&NEIGH_H;
+    uint32_t nbits = hxtAdvancedHilbertBits(options->bbox, options->minSizeStart, options->minSizeEnd,
+                                            options->numVerticesInMesh,
+                                            options->numVerticesInMesh + nToInsert,
+                                            options->numVerticesInMesh + nToInsert/4,
+                                            nToInsert/2,
+                                            maxThreads);
+    
+    HXT_CHECK( hxtVerticesHilbertDist(options->bbox, verticesToInsert, nToInsert, &nbits, NULL) );
 
-  double* const vtaCoord = mesh->vertices.coord + 4*vta;
-  unsigned enteringFace = 4;
+    if(options->nodalSizes==NULL){
+      for (unsigned i=options->numVerticesInMesh < SMALLEST_ROUND; i<npasses; i++) {
+        HXT_CHECK( hxtVerticesSort(verticesToInsert+passes[i], passes[i+1]-passes[i], nbits) );
+      }
+    }
+    else{
+      #pragma omp parallel for
+      for (int i=0; i<nToInsert; i++) {
+        nodeInfo[i].hilbertDist = verticesToInsert[i].padding.hilbertDist;
+      }
 
-#ifdef DEBUG
-  uint64_t TotalCount = 0;
-#endif
-  
+      for (unsigned i=options->numVerticesInMesh < SMALLEST_ROUND; i<npasses; i++) {
+        HXT_CHECK( hxtNodeInfoSort(nodeInfo+passes[i], passes[i+1]-passes[i], nbits) );
+      }
 
-  while(1){
-    const uint32_t* __restrict__ curNode = mesh->tetrahedra.node + nextTet;
-    const uint64_t* __restrict__ curNeigh = mesh->tetrahedra.neigh + nextTet;
+      const uint32_t nodalMin = mesh->vertices.num - nToInsert;
+      double* sizesToInsert = options->nodalSizes + nodalMin;
 
-    if (tetInsphere(mesh, nextTet, vta) > 0){
-      *curTet = nextTet;
-      return HXT_STATUS_OK;
+      size_t vertSize = nToInsert*sizeof(HXTVertex);
+      size_t sizeSize = nToInsert*sizeof(double);
+      HXTVertex* vertCopy;
+      double* sizeCopy;
+      HXT_CHECK( hxtAlignedMalloc(&vertCopy, vertSize) );
+      HXT_CHECK( hxtAlignedMalloc(&sizeCopy, sizeSize) );
+      
+      #pragma omp parallel for
+      for (uint32_t i=0; i<nToInsert; i++) {
+        vertCopy[i] = verticesToInsert[nodeInfo[i].node-nodalMin];
+        sizeCopy[i] = sizesToInsert[nodeInfo[i].node-nodalMin];
+        nodeInfo[i].node = nodalMin + i;
+      }
+
+      memcpy(verticesToInsert, vertCopy, vertSize);
+      memcpy(sizesToInsert, sizeCopy, sizeSize);
+
+      HXT_CHECK( hxtAlignedFree(&vertCopy) );
+      HXT_CHECK( hxtAlignedFree(&sizeCopy) );
     }
-  #ifdef DEBUG
-    else if(curNode[3]==HXT_GHOST_VERTEX){
-      return HXT_ERROR_MSG(HXT_STATUS_FAILED, "walked outside of the domain");
+  }
+
+  /******************************************************
+        Initializations and allocations
+  ******************************************************/
+  if(mesh->tetrahedra.num<5){
+    HXT_INFO_COND(options->verbosity>0,
+                  "Initialization of tet. mesh");
+    HXT_CHECK( hxtTetrahedraInit(mesh, nodeInfo, nToInsert, options->verbosity) );
+    options->numVerticesInMesh = 4; // not counting the ghost vertex
+    passes[0] = 4;
+  }
+
+
+  uint32_t*  verticesID;
+#ifdef HXT_DELAUNAY_LOW_MEMORY
+  verticesID = NULL; // we do not need it
+#else
+  HXT_CHECK( hxtAlignedMalloc(&verticesID, mesh->vertices.num*sizeof(uint32_t)) );
+#endif
+
+  TetLocal* Locals;
+  HXT_CHECK( hxtMalloc(&Locals, maxThreads*sizeof(TetLocal)) );
+  // HXT_CHECK( hxtMalloc())
+
+  for (int i=0; i<maxThreads; i++)
+    localInit(&Locals[i]);
+
+
+  HXT_INFO_COND(options->verbosity>0,
+                "Delaunay of %10u vertices on %3d threads\t- mesh.nvert: %-10u",
+                passes[npasses] - passes[0], maxThreads, options->numVerticesInMesh);
+
+  for (uint32_t p=0; p<npasses; p++)
+  {
+
+    double percent = 200;
+    int nthreads = 1;
+    uint32_t tmp = (passes[p+1]-passes[p])/SMALLEST_ROUND;
+    while(tmp>0 && nthreads<maxThreads){
+      tmp = tmp/2;
+      nthreads*=2;
     }
-  #endif
+    nthreads = MIN(nthreads, maxThreads);
 
+    // const uint32_t initialPassLength = passes[p+1] - passes[p];
 
-    unsigned i;
-    unsigned neigh=4;
-    double Min = 0.0;
-    for (i=0; i<4; i++)
+    for(uint32_t n=0; passes[p+1]-passes[p]; n++)
     {
-      // we walk where the volume is minimum
-      const double* __restrict__ a = mesh->vertices.coord +4*curNode[(i+1)&3];
-      const double* __restrict__ b = mesh->vertices.coord +4*curNode[(i&2)^3];
-      const double* __restrict__ c = mesh->vertices.coord +4*curNode[(i+3)&2];
+      const uint32_t passStart = passes[p];
+      const uint32_t passEnd = passes[p+1];
+      const uint32_t passLength = passEnd - passStart;
+
+      /******************************************************
+                      choosing number of threads
+      ******************************************************/
+      if(percent<140/nthreads || passLength<SMALLEST_ROUND){
+        nthreads=1;
+      }
+      else if(percent<20){
+        nthreads=(nthreads+1)/2;
+      }
+      else if(passLength < nthreads*SMALLEST_ROUND)
+        nthreads=(nthreads+1)/2;
+
+
+      /******************************************************
+                      Sorting vertices
+      ******************************************************/
+      double hxtDeclareAligned bboxShift[4]={0.5,0.5,0.5,0};
+
+      if(percent<100 && nthreads>1)
+      {
+        bboxShift[0] = (double) hxtReproducibleLCG(&seed)/RAND_MAX;
+        bboxShift[1] = (double) hxtReproducibleLCG(&seed)/RAND_MAX;
+        bboxShift[2] = (double) hxtReproducibleLCG(&seed)/RAND_MAX;
+        bboxShift[3] = (double) hxtReproducibleLCG(&seed)/RAND_MAX; // this is not a bbox deformation, it's an index shift
+      }
+
+      uint32_t nbits = hxtAdvancedHilbertBits(options->bbox, options->minSizeStart, options->minSizeEnd,
+                                                             options->numVerticesInMesh - passStart,
+                                                             options->numVerticesInMesh - passStart + nToInsert,
+                                                             options->numVerticesInMesh,
+                                                             passLength,
+                                                             nthreads);
+      if(noReordering){
+        HXT_CHECK( hxtVerticesHilbertDist(options->bbox, vertices, mesh->vertices.num, &nbits, bboxShift) );
+      }
+      else{
+        // TODO: do not recompute hilber dist of the whole mesh on 1 thread
+        HXT_CHECK( hxtVerticesHilbertDist(options->bbox, vertices, mesh->vertices.num - nToInsert + passEnd, &nbits, bboxShift) );
+      }
       
-      if (i!=enteringFace) {
-        double val = orient3d(a,b,c, vtaCoord);
-        const uint32_t* __restrict__ neighNodes = mesh->tetrahedra.node + (curNeigh[i]&NEIGH_H);
-
-        if (val < Min && checkTetrahedron((hxtVertex*) mesh->vertices.coord, local, neighNodes)==HXT_STATUS_OK){
-          neigh = i;
-          Min = val;
-          //    break;
+
+      #pragma omp parallel for simd aligned(nodeInfo:SIMD_ALIGN)
+      for (uint32_t i=passStart; i<passEnd; i++) {
+        nodeInfo[i].hilbertDist = vertices[nodeInfo[i].node].padding.hilbertDist;
+      }
+
+      if(p!=0 || n!=0 || nthreads>1 || options->numVerticesInMesh >= SMALLEST_ROUND){
+        HXT_CHECK( hxtNodeInfoSort(nodeInfo + passStart, passLength, nbits) );
+      }
+
+      const uint32_t step = passLength/nthreads;
+
+      uint32_t indexShift = MIN(step-1,(uint32_t) bboxShift[3]*step);
+
+      int threadFinished = 0;
+
+      #pragma omp parallel num_threads(nthreads)
+      {
+      #ifdef _MSC_VER
+        #pragma omp single
+        nthreads = omp_get_num_threads();
+      #endif
+
+        uint64_t curTet = 0; // we always begin with the first tet. (index 0)
+        const int threadID = omp_get_thread_num();
+
+        uint32_t localStart;
+        uint32_t localN;
+        int foundTet = 0;
+
+        if(nthreads>1){
+          // if(threadID<nthreads){
+
+          /******************************************************
+                          Making partitions
+          ******************************************************/
+          localStart = step*threadID + indexShift;
+          uint64_t dist = nodeInfo[passStart + localStart].hilbertDist;
+          
+          uint32_t up = 1;
+          while(localStart+up<passLength && dist==nodeInfo[passStart + localStart + up].hilbertDist)
+            up++;
+
+          localStart = localStart+up==passLength?0:localStart+up;
+          if(localStart > 0)
+            Locals[threadID].partition.startDist = (nodeInfo[passStart + localStart].hilbertDist
+                                                  + nodeInfo[passStart + localStart - 1].hilbertDist + 1)/2;
+          else
+            Locals[threadID].partition.startDist = nodeInfo[passStart + passLength-1].hilbertDist + (nodeInfo[passStart + localStart].hilbertDist - nodeInfo[passStart + passLength - 1].hilbertDist)/2;
+          Locals[threadID].partition.first = localStart;
+          // }
+
+          #pragma omp barrier
+
+          // if(threadID<nthreads){
+          uint32_t localEnd = Locals[(threadID+1)%nthreads].partition.first;
+          localN = (localEnd + passLength - localStart)%passLength;
+
+          Locals[threadID].partition.endDist = Locals[(threadID+1)%nthreads].partition.startDist;
+
+          // printf("%d) first dist: %lu, last dist: %lu startDist: %lu endDist: %lu\n", threadID, nodeInfo[passStart + localStart].hilbertDist, nodeInfo[(passStart + localStart + localN-1)%passLength].hilbertDist, Locals[threadID].partition.startDist, Locals[threadID].partition.endDist);
+
+
+          /******************************************************
+                          find starting tetrahedron
+          ******************************************************/
+
+          for (uint64_t i=0; i<mesh->tetrahedra.num; i++)
+          {
+            curTet = i;
+            if(isTetDeleted(mesh, i)==0 &&
+               checkTetrahedron(vertices, &Locals[threadID], mesh->tetrahedra.node + curTet*4 )==HXT_STATUS_OK)
+            {
+              foundTet = 1;
+              break;
+            }
+          }
+
+          if(options->reproducible){
+            Locals[threadID].partition.startDist = 0;
+            Locals[threadID].partition.endDist = UINT64_MAX;
+
+            // walk in total liberty toward the first point
+            HXTStatus status = walking2Cavity(mesh, &Locals[threadID], &curTet, nodeInfo[passStart + (localStart+localN/2)%passLength].node);
+
+            if(status!=HXT_STATUS_OK && status!=HXT_STATUS_TRYAGAIN){
+              HXT_OMP_CHECK( status );
+            }
+
+            Locals[threadID].partition.startDist = nodeInfo[passStart + localStart].hilbertDist;
+            Locals[threadID].partition.endDist = nodeInfo[passStart + localEnd].hilbertDist;
+
+            if(checkTetrahedron(vertices, &Locals[threadID], mesh->tetrahedra.node + curTet*4 )!=HXT_STATUS_OK){
+              foundTet = 0;
+              // check the neighbors
+              for (unsigned i=0; i<4; i++) {
+                uint64_t tet = mesh->tetrahedra.neigh[4*curTet+i]/4;
+                if(checkTetrahedron(vertices, &Locals[threadID], mesh->tetrahedra.node + tet*4 )==HXT_STATUS_OK){
+                  foundTet = 1;
+                  curTet = tet;
+                  break;
+                }
+              }
+            }
+          }
+
+          // }
+
+          #pragma omp barrier
         }
+        else
+          // if(threadID==0)
+        {
+
+          /******************************************************
+             single-thread partition and starting tetrahedron
+          ******************************************************/
+          localStart = 0;
+          localN = passLength;
+          Locals[0].partition.startDist = 0;
+          Locals[0].partition.endDist = UINT64_MAX;
+
+          for (uint64_t i=0; i<mesh->tetrahedra.num; i++)
+          { 
+            if(isTetDeleted(mesh, i)==0){
+              curTet = i;
+              foundTet = 1;
+              break;
+            }
+          }
+        }
+
+        if (foundTet == 0) {
+          HXT_INFO_COND(options->verbosity>1,
+                        "thread %d did not find any tetrahedron to begin with", threadID);
+        }
+
+        // filtering vertices on the Moore curve
+        if(options->nodalSizes!=NULL)
+        {
+          double* p1 = NULL;
+          double p1Size = 0;
+
+          for (uint32_t i=0; i<localN; i++)
+          {
+            uint32_t passIndex = (localStart+i)%passLength;
+            uint32_t lastNode = nodeInfo[passStart + passIndex].node;
+            if(nodeInfo[passStart + passIndex].status==HXT_STATUS_TRYAGAIN){
+              double* p2 = vertices[lastNode].coord;
+              double p2Size = options->nodalSizes[lastNode];
+              if(p1!=NULL && pointIsTooClose(p1, p2, 0.5*(p1Size+p2Size))!=HXT_STATUS_OK){
+                nodeInfo[passStart + passIndex].status=HXT_STATUS_FALSE;
+              }
+              else{
+                p1 = p2;
+                p1Size = p2Size;
+              }
+            }
+          }
+        }
+
+
+        // if(threadID<nthreads){
+        if(foundTet!=0){
+
+          /******************************************************
+                          vertices insertion
+          ******************************************************/
+          for (uint32_t i=0; i<localN; i++)
+          {
+            uint32_t passIndex = (localStart+i)%passLength;
+            uint32_t vta = nodeInfo[passStart + passIndex].node;
+            if(nodeInfo[passStart + passIndex].status==HXT_STATUS_TRYAGAIN){
+              HXTStatus status = insertion(mesh, verticesID, &Locals[threadID], options->nodalSizes, &curTet, vta, perfectlyDelaunay);
+
+              switch(status){
+                case HXT_STATUS_TRYAGAIN:
+                  // ;
+                  if(nthreads==1){
+                    double* vtaCoord = vertices[vta].coord;
+                    HXT_WARNING("skipping supposedly duplicate vertex (%f %f %f)", vtaCoord[0], vtaCoord[1], vtaCoord[2]);
+                    nodeInfo[passStart + passIndex].status = HXT_STATUS_FALSE;
+                    break;
+                  }
+                case HXT_STATUS_FALSE:
+                case HXT_STATUS_TRUE:
+                  nodeInfo[passStart + passIndex].status = status;
+                  break;
+                default: // error other than HXT_STATUS_TRYAGAIN cause the program to return
+                  nodeInfo[passStart + passIndex].status = HXT_STATUS_TRYAGAIN;
+                  HXT_OMP_CHECK( status );
+                  break;
+              }
+            }
+            else{
+              nodeInfo[passStart + passIndex].status = HXT_STATUS_FALSE;
+            }
+          }  
+        }
+        // }
+
+        #pragma omp atomic update
+        threadFinished++;
+
+        int val = 0;
+        do{
+          // threads are waiting here for a reallocation
+          HXT_OMP_CHECK( synchronizeReallocation(mesh, &threadFinished, &val) );
+        }while(val<nthreads);
+        // }while(val<maxThreads);
       }
-    }
 
-    if(neigh==4){
-      // if(tetInsphere(mesh, nextTet, vta) > 0){
-      //   *curTet = nextTet;
-      //   return HXT_STATUS_OK;
-      // }
-      return HXT_STATUS_TRYAGAIN;
+      /******************************************************
+      vertices that have to be tried again are put at the end
+      ******************************************************/
+      // everything above i+shift is HXT_STATUS_TRYAGAIN
+      uint32_t shift = 0;
+      unsigned numSkipped = 0;
+      for (uint32_t i=passEnd; i>passStart;)
+      {
+        i--;
+        if(nodeInfo[i].status!=HXT_STATUS_TRYAGAIN){
+          if(nodeInfo[i].status==HXT_STATUS_FALSE)
+            numSkipped++;
+          shift++;
+        }
+        else if(shift!=0) {
+          hxtNodeInfo tmp = nodeInfo[i];
+          nodeInfo[i] = nodeInfo[i+shift];
+          nodeInfo[i+shift] = tmp;
+        }
+      }
+
+      options->numVerticesInMesh += shift - numSkipped;
+
+      percent = (shift-numSkipped)*100.0/MAX(1,passLength-numSkipped);
+      totalNumSkipped += numSkipped;
+
+      HXT_INFO_COND(options->verbosity>1,
+                    "%3d thrd |%10u/%-10u-> %*.1f%-*c\t- mesh.nvert: %-10u",
+                    nthreads, shift-numSkipped, passLength-numSkipped, MIN(8,n/2)+5, percent, 8-MIN(8,n/2),'%', options->numVerticesInMesh);
+      
+      passes[p] += shift;
     }
+  }
 
-    //    printf("nextTet %u %g %u %u\n",nextTet,Min, count, neigh);
-    nextTet = curNeigh[neigh]&NEIGH_H;
-    enteringFace = curNeigh[neigh]&3;
-
-    #ifdef DEBUG
-    if(TotalCount>mesh->tetrahedra.num){
-      return HXT_ERROR_MSG(HXT_STATUS_FAILED, "infinite walk to find the cavity");
+  /******************************************************
+                  Cleaning
+  ******************************************************/
+  #pragma omp parallel num_threads(maxThreads)
+  {
+    const int threadID = omp_get_thread_num();
+    for (uint64_t i=0; i<Locals[threadID].deleted.num; i++) {
+      for (int j=0; j<4; j++) {
+        mesh->tetrahedra.neigh[4*Locals[threadID].deleted.tetID[i]+j] = HXT_NO_ADJACENT;
+      }
     }
-    // printf("%lu\n",TotalCount);
-    TotalCount++;
-    #endif
   }
-}
+  HXT_CHECK( hxtRemoveDeleted(mesh) );
 
+  for (int i=0; i<maxThreads; i++){
+    HXT_CHECK( hxtAlignedFree(&Locals[i].deleted.tetID) );
+    HXT_CHECK( hxtAlignedFree(&Locals[i].ball.bnd) );
+  }
 
+  HXT_CHECK( hxtAlignedFree(&verticesID) );
+  HXT_CHECK( hxtFree(&Locals) );
 
-/***********************************
- * digging cavity
- ***********************************/
+  /***************************************************************
+    if reordering allowed, remove vertices we could not insert
+  ***************************************************************/
+  if(!noReordering && totalNumSkipped!=0){
+    /* remove deleted vertices and change tetrahedra.node accordingly */
 
-/* pushing boundary information to local->bnd */
-static inline void bndPush( TetLocal* local, const uint32_t vta, 
-              const uint32_t node1, const uint32_t node2,
-              const uint32_t node3, const uint64_t otherside){
-  uint64_t n = local->numBnd;
-  local->bnd[n].node[0] = vta;
-  local->bnd[n].node[1] = node1;
-  local->bnd[n].node[2] = node2;
-  local->bnd[n].node[3] = node3;
-  local->bnd[n].otherside = otherside;
-  local->numBnd++;
-}
+    uint32_t* numInserted;
+    HXT_CHECK( hxtAlignedMalloc(&numInserted, omp_get_max_threads()*sizeof(uint32_t)) );
 
-static inline HXTStatus deletedPush(HXTMesh* mesh, TetLocal* local, const uint64_t neigh){
-  // check if 3 points of the new tetrahedra are owned by this thread
-  HXT_CHECK( checkTetrahedron((hxtVertex*) mesh->vertices.coord, local, mesh->tetrahedra.node + (neigh&NEIGH_H)) );
+    uint32_t firstShifted = mesh->vertices.num - nToInsert;
+    uint32_t n = nToInsert;
 
-  local->deleted[local->numDeleted++] = neigh&NEIGH_H;
-  local->vertices[local->numVertices++] = mesh->tetrahedra.node[neigh];
+    // when a vertex was skipped, nodeInfo[i].status = HXT_STATUS_FALSE
+    #pragma omp parallel
+    {
+      // 1st: mark vertices with their corresponding status
+      #pragma omp for schedule(static)
+      for (uint32_t i=0; i<nToInsert; i++) {
+        uint32_t index = nodeInfo[i].node;
+        HXTStatus status = nodeInfo[i].status;
+        vertices[index].padding.status = status;
+      }// implicit barrier here
 
-  return HXT_STATUS_OK;
-}
+      #pragma omp single
+      {
+        uint32_t i = 0;
+        while (vertices[firstShifted+i].padding.status==HXT_STATUS_TRUE) i++;
 
+        firstShifted += i+1;
+        n -= i+1;
+      }// implicit barrier here
 
-//TODO: when applying colors, we need to verify that the cavity is convex... everytime !!
-/* this function does a Breadth-first search of the tetrahedra in the cavity
- * it add those to local->deleted
- * it also maintain a local->bnd array with all the information concerning the boundary of the cavity
- */
-static inline HXTStatus diggingACavity(HXTMesh* mesh, TetLocal* local, uint64_t curTet, const uint32_t vta, const uint16_t color){
-  // add triangle to cavity
-  uint64_t start;
-  uint16_t* __restrict__ allColors = mesh->tetrahedra.colors;
-
-  local->deleted[local->numDeleted++] = curTet;
-  allColors[curTet>>2]=HXT_DELETED_COLOR;
-
-  // add the vertices to the list
-  local->vertices[0] = mesh->tetrahedra.node[curTet + 0];
-  local->vertices[1] = mesh->tetrahedra.node[curTet + 1];
-  local->vertices[2] = mesh->tetrahedra.node[curTet + 2];
-  local->vertices[3] = mesh->tetrahedra.node[curTet + 3];
-  local->numVertices = 4;
-  local->numBnd = 0;
+      uint32_t start = 0;
+      int threadID = omp_get_thread_num();
+      numInserted[threadID] = 0;
 
-  
+      #pragma omp for schedule(static)
+      for (uint32_t i=0; i<n; i++) {
+        if(vertices[firstShifted+i].padding.status==HXT_STATUS_TRUE)
+          numInserted[threadID]++;
+      }// implicit barrier here
 
-  for(start=local->numDeleted-1; start < local->numDeleted; start++){
-    uint64_t curTet = local->deleted[start];
-    const uint64_t* __restrict__ curNeigh = mesh->tetrahedra.neigh + curTet;
-    const uint32_t* __restrict__ curNode = mesh->tetrahedra.node + curTet;
-    
+      for (int i=0; i<threadID; i++) {
+        start+=numInserted[i];
+      }
+      start += firstShifted-1;
 
-    /* here we allocate enough space for the boundary (local->bnd), the cavity (local->deleted) and the vertices (local->vertices) */
-    HXT_CHECK( reserveNewDeleted(local, local->numDeleted+4) );
-    HXT_CHECK( reserveNewBnd(local, local->numBnd+4) );
-    HXT_CHECK( reserveNewVertices(local, local->numVertices+4) );
+      // 3rd: compute where each vertices will be
+      #pragma omp for schedule(static)
+      for (uint32_t i=0; i<n; i++) {
+        uint32_t oldStart = start;
 
-    // we unrolled the loop for speed (also because indices are not trivial, we would need a 4X4 array)
+        if(vertices[firstShifted+i].padding.status==HXT_STATUS_TRUE)
+          start++;
 
-    /* and here we push stuff to local->bnd or local->deleted, always keeping ghost tet at last place */
-    uint64_t neigh = curNeigh[0]>>2;
-    if(allColors[neigh]!=HXT_DELETED_COLOR){
-      if(
-        // allColors[neigh] != color || 
-        tetInsphere(mesh, neigh*4, vta)<=0.0){
-        bndPush(local, vta, curNode[1], curNode[2], curNode[3], curNeigh[0]);
-      }
-      else{
-        HXT_CHECK( deletedPush(mesh, local, curNeigh[0]) );
-        allColors[neigh] = HXT_DELETED_COLOR;
+        // index and status are at the same location (it's a union) we cannot put this above the "if" !
+        vertices[firstShifted+i].padding.index = oldStart;
       }
-    }
 
-    neigh = curNeigh[1]>>2;
-    if(allColors[neigh]!=HXT_DELETED_COLOR){
-      if(
-        // allColors[neigh] != color || 
-        tetInsphere(mesh, neigh*4, vta)<=0.0){
-        bndPush(local, vta, curNode[2], curNode[0], curNode[3], curNeigh[1]);
-      }
-      else{
-        HXT_CHECK( deletedPush(mesh, local, curNeigh[1]) );
-        allColors[neigh] = HXT_DELETED_COLOR;
+      // 4th: update tetrahedra.node accordingly
+      #pragma omp for
+      for (uint64_t i=0; i<4*mesh->tetrahedra.num; i++) {
+        uint32_t index = mesh->tetrahedra.node[i];
+        if(index>=firstShifted && index!=HXT_GHOST_VERTEX)
+          mesh->tetrahedra.node[i] = vertices[index].padding.index;
       }
     }
 
-    neigh = curNeigh[2]>>2;
-    if(allColors[neigh]!=HXT_DELETED_COLOR){
-      if(
-        // allColors[neigh] != color || 
-        tetInsphere(mesh, neigh*4, vta)<=0.0){
-        bndPush(local, vta, curNode[0], curNode[1], curNode[3], curNeigh[2]);
-      }
-      else{
-        HXT_CHECK( deletedPush(mesh, local, curNeigh[2]) );
-        allColors[neigh] = HXT_DELETED_COLOR;
-      }
-    }
+    HXT_CHECK( hxtAlignedFree(&numInserted) );
 
-    neigh = curNeigh[3]>>2;
-    if(allColors[neigh]!=HXT_DELETED_COLOR){
-      if(
-        // allColors[neigh] != color || 
-        tetInsphere(mesh, neigh*4, vta)<=0.0){
-        // curNode[0] is the biggest, the smallest can be curNode[1] or curNode[2]
-        // if(curNode[1]<curNode[2])
-        //   bndPush(local, vta, curNode[0], curNode[2], curNode[1], curNeigh[3]);
-        // else
-          bndPush(local, vta, curNode[1], curNode[0], curNode[2], curNeigh[3]);
-      }
-      else{
-        HXT_CHECK( deletedPush(mesh, local, curNeigh[3]) );
-        allColors[neigh] = HXT_DELETED_COLOR;
+    // 5th: put vertices at the right indices
+    for (uint32_t i=firstShifted; i<mesh->vertices.num; i++) {
+      if(options->nodalSizes!=NULL){
+        options->nodalSizes[vertices[i].padding.index] = options->nodalSizes[i];
       }
+      vertices[vertices[i].padding.index] = vertices[i];
     }
-  }
-
-  return HXT_STATUS_OK;
-}
-
-
-/***********************************
- * filling back the cavity
- ***********************************/
-// N+2 point on the surface of the cavity
-// 2N triangle on the surface of the cavity, x3 (4*0.5+1) data = 6N+9 uint64_t
-// => enough place for the 3N edge x2 data = 6N uint64_t
-static inline HXTStatus computeAdjacenciesFast(HXTMesh* mesh, TetLocal* local, uint32_t* __restrict__ verticesID, const uint64_t start, const uint64_t blength){
-  uint32_t* __restrict__ localVertices = local->vertices;
 
-HXT_ASSERT(((size_t) verticesID)%SIMD_ALIGN==0);
-
-  uint32_t i;
-  #pragma omp simd aligned(verticesID,localVertices:SIMD_ALIGN)
-  for (i=0; i<local->numVertices; i++){
-    verticesID[localVertices[i]+1] = UINT32_MAX;
-  }
+    if(options->verbosity>1)
+      HXT_INFO("%u vertices removed (vertices not inserted in the mesh are removed when using hxtDelaunay)\n", totalNumSkipped);
 
-  uint32_t npts = 0;
-  for (i=0; i<local->numVertices; i++)
-  {
-    if(verticesID[localVertices[i]+1]>npts){
-      // printf("vertice %u (located at %u in verticesID) has got number %u\n",localVertices[i],localVertices[i]+1, npts);
-      verticesID[localVertices[i]+1] = npts++;
-    }
+    mesh->vertices.num = mesh->vertices.num - totalNumSkipped;
   }
 
-  HXT_ASSERT_MSG((npts-2)*2==blength, "Failed to compute adjacencies (f)");
-
-  #pragma omp simd aligned(verticesID:SIMD_ALIGN)
-  for (i=0; i<blength; i++)
-  {
-    local->Map[verticesID[local->bnd[i].node[1]+1]*32 + verticesID[local->bnd[i].node[2]+1]] = local->bnd[i].otherside + 3;
-    local->Map[verticesID[local->bnd[i].node[2]+1]*32 + verticesID[local->bnd[i].node[3]+1]] = local->bnd[i].otherside + 1;
-    local->Map[verticesID[local->bnd[i].node[3]+1]*32 + verticesID[local->bnd[i].node[1]+1]] = local->bnd[i].otherside + 2;
-  }
+  HXT_INFO_COND(options->verbosity>0, "Delaunay done !%10u skipped", totalNumSkipped);
+  HXT_INFO_COND(options->verbosity>1, "mem. allocated:%5.2fGB - mesh.ntet: %-12lu - mesh.nvert: %-10lu",
+    ((50 + 2*(mesh->tetrahedra.flag!=NULL)) * mesh->tetrahedra.size +
+     (32 + 8*(options->nodalSizes!=NULL)) * mesh->vertices.size)/(1024.*1024.*1024.),
+    mesh->tetrahedra.num, mesh->vertices.num);
 
-  #pragma omp simd aligned(verticesID:SIMD_ALIGN)
-  for (i=0; i<blength; i++)
-  {
-    mesh->tetrahedra.neigh[local->bnd[i].otherside + 1] = local->Map[verticesID[local->bnd[i].node[3]+1]*32 + verticesID[local->bnd[i].node[2]+1]];
-    mesh->tetrahedra.neigh[local->bnd[i].otherside + 2] = local->Map[verticesID[local->bnd[i].node[1]+1]*32 + verticesID[local->bnd[i].node[3]+1]];
-    mesh->tetrahedra.neigh[local->bnd[i].otherside + 3] = local->Map[verticesID[local->bnd[i].node[2]+1]*32 + verticesID[local->bnd[i].node[1]+1]];
+  if(options->reproducible && maxThreads!=1){
+    HXT_INFO_COND(options->verbosity>1, "Reordering tetrahedra (reproducible==true)\n", mesh->vertices.num - nToInsert, mesh->vertices.num);
+    HXT_CHECK( hxtTetReorder(mesh) );
   }
 
   return HXT_STATUS_OK;
 }
 
 
-static inline HXTStatus computeAdjacenciesSlow(HXTMesh* mesh, TetLocal* local, const uint64_t start, const uint64_t blength){
-
-  uint64_t tlength = 0;
-  const uint64_t middle = blength*3/2; // 3N
-
-  uint64_t* Tmp = (uint64_t*) local->bnd; // we know there is enough space...
-  const unsigned index[4] = {2,3,1,2};
+/*****************************************
+ * complete the HXTDelaunayOptions struct
+ * when there are missing fields.
+ ****************************************/
+static HXTStatus DelaunayOptionsInit(HXTMesh* mesh,
+                                HXTDelaunayOptions* userOptions,
+                                HXTDelaunayOptions* options,
+                                HXTBbox* bbox){
+HXT_ASSERT(mesh!=NULL);
 
-  uint64_t i;
-  for (i=0; i<blength; i++)
-  {
-    uint64_t curTet = local->deleted[start+ i];
-    const uint32_t* __restrict__ Node = mesh->tetrahedra.node + curTet;
+  if(userOptions!=NULL){
+    options->bbox = userOptions->bbox;
+    options->nodalSizes = userOptions->nodalSizes;
+    options->verbosity = userOptions->verbosity;
+    options->minSizeStart = MAX(0.0, userOptions->minSizeStart);
+    options->minSizeEnd = MAX(options->minSizeStart, userOptions->minSizeEnd);
+    options->numVerticesInMesh = userOptions->numVerticesInMesh;
+    options->delaunayThreads = userOptions->delaunayThreads;
+    options->reproducible = userOptions->reproducible;
+  }
+  else{
+    HXTVertex* vertices = (HXTVertex*) mesh->vertices.coord;
 
-    // printf("===Node: %u %u %u %u\n",Node[0],Node[1],Node[2],Node[3]);
+    // default parameters
+    options->bbox = NULL;
+    options->nodalSizes = NULL;
+    options->minSizeStart = 0.0;
+    options->minSizeEnd = 0.0;
+    options->verbosity = 1;
+    options->delaunayThreads = 0;
+    options->reproducible = 0;
 
-    // pointer to the position of Node[0] in the Tmp array
-    uint64_t j;
-    for (j=0; j<3; j++)
-    {
-      // define the edge by the minimum vertex and the other
-      uint64_t key = ((uint64_t) Node[index[j]]<<32) + Node[index[j+1]];
-      curTet++;
+    // count the number of vertices in the mesh
+    #pragma omp parallel for
+    for (uint32_t i=0; i<mesh->vertices.num; i++) {
+      vertices[i].padding.index = 0;
+    }
 
-      // linear searching/pushing into Tmp
-      uint64_t k;
-      for (k=0; k<tlength; k++) // this is the only nested loop... the one that cost it all
-      {
-        __assume_aligned(Tmp, SIMD_ALIGN);
-        if(Tmp[k]==key)
-          break;
-      }
+    #pragma omp parallel for
+    for (uint64_t i=0; i<mesh->tetrahedra.num; i++) {
+      vertices[mesh->tetrahedra.node[4*i+0]].padding.index = 1;
+      vertices[mesh->tetrahedra.node[4*i+1]].padding.index = 1;
+      vertices[mesh->tetrahedra.node[4*i+2]].padding.index = 1;
+      if(mesh->tetrahedra.node[4*i+3]!=HXT_GHOST_VERTEX)
+        vertices[mesh->tetrahedra.node[4*i+3]].padding.index = 1;
+    }
 
-      // we did not found it
-      if(k==tlength){
-        // printf("pushing key %lu=(%u<<32) + %u   (tet %lu, %lu = %lu)\n",key,Node[index[j]],Node[node[j+1]], curTet&NEIGH_H, j+1, curTet);
-        Tmp[tlength] = (key>>32) + (key<<32);
-        Tmp[middle + tlength] = curTet;
-        tlength++;
-      }
-      else{// we found the neighbour !
-        uint64_t pairValue = Tmp[middle+k];
-        // printf("found key %lu (tet %lu, %lu = %lu) neigh:%lu\n",key, curTet&NEIGH_H, j+1, curTet, pairValue);
-        mesh->tetrahedra.neigh[curTet] = pairValue;
-        mesh->tetrahedra.neigh[pairValue] = curTet;
-        tlength--;
-        if(k<tlength){// put the last entry in the one we just discovered
-          Tmp[k] = Tmp[tlength];
-          Tmp[middle+k] = Tmp[middle + tlength];
-        }
-      }
+    uint32_t numVerticesInMesh = 0;
+    #pragma omp parallel for reduction(+:numVerticesInMesh)
+    for (uint32_t i=0; i<mesh->vertices.num; i++) {
+        numVerticesInMesh += vertices[i].padding.index;
     }
+
+    options->numVerticesInMesh = numVerticesInMesh;
   }
-  HXT_ASSERT_MSG(tlength==0, "Failed to compute adjacencies (s)"); // verify that all neighbor were found
-  return HXT_STATUS_OK;
-}
 
+HXT_ASSERT(options->numVerticesInMesh <= mesh->vertices.num);
 
-static inline HXTStatus fillingACavity(HXTMesh* mesh, TetLocal* local, uint32_t* __restrict__ verticesID, uint64_t* __restrict__ curTet, const uint16_t color){
-  uint64_t clength = local->numDeleted;
-  uint64_t blength = local->numBnd;
+  if(options->bbox==NULL){
+    options->bbox = bbox;
+    hxtBboxInit(bbox);
+    HXT_CHECK( hxtBboxAdd(bbox, mesh->vertices.coord, mesh->vertices.num) );
+  }
 
-  uint64_t start = clength - blength;
+  if(options->delaunayThreads==0)
+    options->delaunayThreads = omp_get_max_threads();
+  else if(options->delaunayThreads<0)
+    options->delaunayThreads = omp_get_num_procs();
+  
+  if(options->delaunayThreads>omp_get_thread_limit())
+    options->delaunayThreads = omp_get_thread_limit();
 
-  // #pragma vector aligned
-  #pragma omp simd
-  for (uint64_t i=0; i<blength; i++)
-  {
+  // for the predicates to work
+  exactinit(options->bbox->max[0]-options->bbox->min[0],
+            options->bbox->max[1]-options->bbox->min[1],
+            options->bbox->max[2]-options->bbox->min[2]);
 
-    __assume_aligned(local->deleted, SIMD_ALIGN);
-    __assume_aligned(local->bnd, SIMD_ALIGN);
-    __assume_aligned(mesh->tetrahedra.colors, SIMD_ALIGN);
-    __assume_aligned(mesh->tetrahedra.node, SIMD_ALIGN);
-    __assume_aligned(mesh->tetrahedra.neigh, SIMD_ALIGN);
+  return HXT_STATUS_OK;
+}
 
-    const uint64_t newTet = local->deleted[i + start];
-    uint32_t* __restrict__ Node = mesh->tetrahedra.node + newTet;
-    mesh->tetrahedra.colors[newTet>>2] = color;
 
-    /* we need to always put the ghost vertex at the fourth slot*/
-    Node[0] = local->bnd[i].node[0];
-    Node[1] = local->bnd[i].node[1];
-    Node[2] = local->bnd[i].node[2];
-    Node[3] = local->bnd[i].node[3];
+/*****************************************
+ * parallel Delaunay
+ * see header for a complete description
+ ****************************************/
+HXTStatus hxtDelaunay(HXTMesh* mesh, HXTDelaunayOptions* userOptions){
+  HXTDelaunayOptions options;
+  HXTBbox bbox;
+  HXT_CHECK( DelaunayOptionsInit(mesh, userOptions, &options, &bbox) );
 
-    const uint64_t otherside = local->bnd[i].otherside;
-    mesh->tetrahedra.neigh[newTet] = otherside;
-    // update neighbor's neighbor
-    mesh->tetrahedra.neigh[otherside] = newTet;
+  const uint32_t nToInsert = mesh->vertices.num - options.numVerticesInMesh;
 
-    // we recycle otherside to contain newTet (used in computeAdjacencies)
-    local->bnd[i].otherside = newTet;
-  }
-#ifndef HXT_DELAUNAY_LOW_MEMORY
-  if(blength<=60){ // N+2<=32 => N<=30 => 2N<=60
-  #ifdef DEBUG
-    HXT_CHECK( computeAdjacenciesFast(mesh, local, verticesID, start, blength) );
-  #else
-    computeAdjacenciesFast(mesh, local, verticesID, start, blength);
-  #endif
-  }
-  else
-#endif
-  {
-  #ifdef DEBUG
-    HXT_CHECK(computeAdjacenciesSlow(mesh, local, start, blength) );
-  #else
-    computeAdjacenciesSlow(mesh, local, start, blength);
-  #endif
+  if(options.reproducible && nToInsert<2048) // not worth launching threads and having to reorder tets after...
+    options.delaunayThreads = 1;
+
+  hxtNodeInfo* nodeInfo;
+  HXT_CHECK( hxtAlignedMalloc(&nodeInfo, nToInsert*sizeof(hxtNodeInfo)) );
+  
+  // we fill nodeInfo with the indices of each vertices to insert...
+  #pragma omp parallel for simd
+  for (uint32_t i=0; i<nToInsert; i++) {
+    nodeInfo[i].node = options.numVerticesInMesh + i;
+    nodeInfo[i].status = HXT_STATUS_TRYAGAIN; // necessary for when foundTet = 0;
   }
 
-  *curTet = local->deleted[start];
-  local->numDeleted = start;
+  HXT_CHECK( parallelDelaunay3D(mesh, &options, nodeInfo, nToInsert, 0) );
+
+  HXT_CHECK( hxtAlignedFree(&nodeInfo) );
 
   return HXT_STATUS_OK;
 }
 
 
-/**************************************************************************
- *                  VERIFYING A MESH                                      *
- **************************************************************************/
-// static HXTStatus hxtTetrahedraVerifyInternal(HXTMesh* mesh, TetLocal* Locals, int nthreads)
-// {
-//   unsigned numErrors = 0;
-//   if(mesh->tetrahedra.num > mesh->tetrahedra.size){
-//     HXT_WARNING("number of tetrahedra exceed the size of the array");
-//     numErrors++;
-//   }
-//   uint64_t i;
-//   // #pragma omp parallel for
-//   for (i=0; i<mesh->tetrahedra.num; i++)
-//   {
-
-//     uint32_t* Node = mesh->tetrahedra.node + i*4;
-//     uint64_t* Neigh = mesh->tetrahedra.neigh + i*4;
-
-
-//     int deleted = 0;
-//     for (int threadID=0; threadID<nthreads; threadID++)
-//     {
-//       int thisDeleted = 0;
-//       for (uint64_t j=0; j<Locals[threadID].numDeleted; j++)
-//       {
-//         if(i*4==Locals[threadID].deleted[j]){
-//           thisDeleted++;
-//         }
-//       }
-
-//       if(thisDeleted>0){
-//         if(thisDeleted>1){
-//           HXT_WARNING("tet. %lu is deleted multiple time in thread %d", i*4, threadID);
-//           numErrors++;
-//         }
-//         deleted++;
-//       }
-//     }
-
-//     if(deleted>0){
-//       if(deleted>1){
-//         HXT_WARNING("tet. %lu is deleted in mutliple threads",i*4);
-//         numErrors++;
-//       }
-
-//       int flag = mesh->tetrahedra.colors[i] == HXT_DELETED_COLOR;
-//       // verify that the flag is set according to the deleted stuff
-//       if(flag!=deleted){
-//         HXT_WARNING("tet. %lu 's flag say it is %sdeleted but it is %sin deleted list", i*4, (flag)?"":"not ", (deleted)?"":"not ");
-//         numErrors++;
-//       }
-
-//       continue;
-//     }
-
-//     if(Node[0]>mesh->vertices.num || Node[1]>mesh->vertices.num || Node[2]>mesh->vertices.num
-//       || (Node[3]!=HXT_GHOST_VERTEX && Node[3]>mesh->vertices.num)){
-//       HXT_WARNING("ghost vertex at wrong place in tet. %lu (%u %u %u %u)", i*4, Node[0], Node[1], Node[2], Node[3]);
-//       numErrors++;
-//     }
-
-
-//     double* a = mesh->vertices.coord + 4*Node[0];
-//     double* b = mesh->vertices.coord + 4*Node[1];
-//     double* c = mesh->vertices.coord + 4*Node[2];
-//     double* d;
-//     if(Node[3]!=HXT_GHOST_VERTEX)
-//       d = mesh->vertices.coord + 4*Node[3];
-
-//     if(Node[3]!=HXT_GHOST_VERTEX && orient3d(a,b,c,d)<0.0){
-//       HXT_WARNING("orientation of tet %lu is wrong",i*4);
-//       numErrors++;
-//     }
-
-//     // check the neighbors
-//     unsigned j;
-//     for (j=0; j<4; j++)
-//     {
-//       uint64_t neigh = Neigh[j];
-
-//       // verify that neigh is not deleted
-//       if(mesh->tetrahedra.colors[neigh/4] == HXT_DELETED_COLOR){
-//         HXT_WARNING("tet %lu has got a deleted tet as its %uth neighbor", i*4, j);
-//         numErrors++;
-//       }
-
-//       // uint64_t* NeighNeigh = mesh->tetrahedra.neigh + neigh;
-//       uint32_t* NeighNode = mesh->tetrahedra.node + (neigh&NEIGH_H);
-
-//       if(mesh->tetrahedra.neigh[neigh]!=i*4+j){
-//         HXT_WARNING("tet %lu is not the neighbor of its %uth neighbor", i*4,j);
-//         numErrors++;
-//       }
-
-//       uint32_t V[3] = { Node[((j+1)&3)], Node[((j&2)^3)], Node[((j+3)&2)]};
-//       unsigned l;
-//       for (l=0; l<3; l++)
-//       {
-//         if(NeighNode[(((neigh&3)+1)&3)]==V[l] && NeighNode[(((neigh&3)&2)^3)]==V[(l+2)%3] && NeighNode[(((neigh&3)+3)&2)]==V[(l+1)%3])
-//            break;
-//       }
-
-//       if(l==3){
-//         HXT_WARNING("neighbor %u of tet. %lu doesn't contain 3 common vertices",j,i*4);
-//         numErrors++;
-//       }
-
-//       if(Node[j]==NeighNode[neigh&3]){
-//         HXT_WARNING("neighbor %u of tet. %lu contain the same non-common vertex... (WTF?)");
-//         numErrors++;
-//       }
-
-//       if(Node[3]!=HXT_GHOST_VERTEX && NeighNode[neigh&3]!=HXT_GHOST_VERTEX && insphere(mesh->vertices.coord + 4*Node[0],
-//                   mesh->vertices.coord + 4*Node[1],
-//                   mesh->vertices.coord + 4*Node[2],
-//                   mesh->vertices.coord + 4*Node[3],
-//                   mesh->vertices.coord + 4*NeighNode[neigh&3]].coord)>0.0){
-//         HXT_WARNING("neighbor %u (%lu) of tet %lu has it's non-common node in the sphere (insphere(%u %u %u %u %u)=%.12g>0)",j, neigh, i*4, Node[0], Node[1], Node[2], Node[3], NeighNode[neigh&3],insphere(mesh->vertices.coord + 4*Node[0],
-//                   mesh->vertices.coord + 4*Node[1],
-//                   mesh->vertices.coord + 4*Node[2],
-//                   mesh->vertices.coord + 4*Node[3],
-//                   mesh->vertices.coord + 4*NeighNode[neigh&3]));
-//         numErrors++;
-//       }
-//     }
-//   }
-
-//   if(numErrors){
-//     return HXT_ERROR_MSG(HXT_STATUS_ERROR, "hxtTetrahedraVerifyInternal detected %d errors", numErrors);
-//   }
-
-//   return HXT_STATUS_OK;
-// }
-
-// HXTStatus hxtTetrahedraVerify(HXTMesh* mesh, TetLocal* local, int nthreads)
-HXTStatus hxtTetrahedraVerify(HXTMesh* mesh)
-{
-  volatile int errorOccured = 0;
-
-  #pragma omp parallel for
-  for (uint64_t i=0; i<mesh->tetrahedra.num; i++)
-  {
-
-    if(errorOccured)
-      continue;
-
-    uint32_t* Node = mesh->tetrahedra.node + i*4;
-    uint64_t* Neigh = mesh->tetrahedra.neigh + i*4;
-
-    if(mesh->tetrahedra.colors[i] == HXT_DELETED_COLOR)
-      continue;
-
-    // check for good placement of ghost vertex
-    if(Node[0]==HXT_GHOST_VERTEX || Node[1]==HXT_GHOST_VERTEX || Node[2]==HXT_GHOST_VERTEX){
-      HXT_ERROR_MSG(HXT_STATUS_ERROR, "ghost vertex at wrong place in tet. %lu", i*4);
-      errorOccured=1;
-      continue;
-    }
-
-    double* a = mesh->vertices.coord + 4*Node[0];
-    double* b = mesh->vertices.coord + 4*Node[1];
-    double* c = mesh->vertices.coord + 4*Node[2];
-
-    // check for the orientation
-    // if(Node[3]==0){
-    //   HXT_WARNING("ghost tet. %lu remains in the array (did you clean the mesh?)",i*4);
-    // }
-    // else
-    if(Node[3]!=HXT_GHOST_VERTEX && orient3d(a,b,c,mesh->vertices.coord + 4*Node[3])<0.0){
-      HXT_ERROR_MSG(HXT_STATUS_ERROR, "orientation of tet %lu is wrong",i*4);
-      errorOccured=1;
-      continue;
-    }
+/************************************************
+ * parallel Delaunay without moving the vertices
+ * see header for a complete description
+ ***********************************************/
+HXTStatus hxtDelaunaySteadyVertices(HXTMesh* mesh, HXTDelaunayOptions* userOptions, hxtNodeInfo* nodeInfo, uint64_t nToInsert){
+HXT_ASSERT(nodeInfo!=NULL);
 
-    // check the neighbors
-    unsigned j;
-    for (j=0; j<4; j++)
-    {
-      uint64_t neigh = Neigh[j];
-      // uint64_t* NeighNeigh = mesh->tetrahedra.neigh + neigh;
-      uint32_t* NeighNode = mesh->tetrahedra.node + (neigh&NEIGH_H);
-
-      if(mesh->tetrahedra.neigh[neigh]!=i*4+j){
-        HXT_ERROR_MSG(HXT_STATUS_ERROR, "tet %lu is not the neighbor of its %uth neighbor", i*4,j);
-        errorOccured=1;
-        continue;
-      }
+  HXTDelaunayOptions options;
+  HXTBbox bbox;
+  HXT_CHECK( DelaunayOptionsInit(mesh, userOptions, &options, &bbox) );
 
-      uint32_t V[3] = { Node[((j+1)&3)], Node[((j&2)^3)], Node[((j+3)&2)]};
-      unsigned l;
-      for (l=0; l<3; l++)
-      {
-        if(NeighNode[(((neigh&3)+1)&3)]==V[l] && NeighNode[(((neigh&3)&2)^3)]==V[(l+2)%3] && NeighNode[(((neigh&3)+3)&2)]==V[(l+1)%3])
-           break;
-      }
+  if(options.reproducible && nToInsert<2048) // not worth launching threads and having to reorder tets after...
+    options.delaunayThreads = 1;
 
-      if(l==3){
-        HXT_ERROR_MSG(HXT_STATUS_ERROR, "neighbor %u of tet. %lu doesn't contain 3 common vertices",j,i*4);
-        errorOccured=1;
-        continue;
-      }
+HXT_ASSERT(options.numVerticesInMesh+nToInsert <= mesh->vertices.num);
 
-      if(Node[3]!=HXT_GHOST_VERTEX && NeighNode[neigh&3]!=HXT_GHOST_VERTEX && insphere(mesh->vertices.coord + 4*Node[0],
-                  mesh->vertices.coord + 4*Node[1],
-                  mesh->vertices.coord + 4*Node[2],
-                  mesh->vertices.coord + 4*Node[3],
-                  mesh->vertices.coord + 4*NeighNode[neigh&3])>0.0){
-        HXT_ERROR_MSG(HXT_STATUS_ERROR, "neighbor %u of tet %lu has it's non-common node in the sphere (insphere(%u %u %u %u %u)>0)",j,i*4, Node[0], Node[1], Node[2], Node[3], NeighNode[neigh&3]);
-        errorOccured=1;
-        continue;
-      }
-    }
-  }
+  HXT_CHECK( parallelDelaunay3D(mesh, &options, nodeInfo, nToInsert, 1) );
 
-  if(errorOccured)
-    return HXT_STATUS_ERROR;
   return HXT_STATUS_OK;
 }
+
diff --git a/contrib/hxt/hxt_tetrahedra.h b/contrib/hxt/hxt_tetrahedra.h
index c4769f625c6837b4d667c304a824d32a565ffd74..5511dd5b9359c3d760330006935156493e4e888c 100644
--- a/contrib/hxt/hxt_tetrahedra.h
+++ b/contrib/hxt/hxt_tetrahedra.h
@@ -8,68 +8,103 @@ extern "C" {
 #include "hxt_mesh.h"
 #include "hxt_vertices.h"
 
-/* reserve place for tetrahedra assuming ntet tetrahedra will be added to the triangulation
- * it is efficient to give ntet = ~10 times the number of vertices to be added
- * as in average, the number of tetrahedra is ~6.5x greater than the number of vertices
+/**
+* \file tetrahedra.h Delaunay tetrahedrization
+* \author Célestin Marot
+*/
+
+/**
+ * \struct HXTDelaunayOptions
+ * 
+ * Options for the Delaunay functions hxtDelaunay() and hxtDelaunaySteadyVertices()
+ * 
+ *
  */
-HXTStatus hxtTetrahedraReserve(HXTMesh* mesh, uint64_t ntet);
-
-/* verify the consistency of a tetrahedral mesh */
-HXTStatus hxtTetrahedraVerify(HXTMesh* mesh);
-
-
-
-typedef struct HXTDelaunayOptions_struct{
-  hxtBbox* bbox;              // if NULL, the bbox is recomputed internally
-                              // else it mush contain the bounding box of all vertices
-
-  double* nodalSizes;         // if NULL, doesn't restrict nodalSize
-                              // else contains the minimum mesh size at each vertex
-                              // if the insertion of a vertex create an edge smaller than
-                              // the average nodalSize of its endpoints, the vertex is not inserted
-
-  uint32_t numVerticesInMesh; // the number of vertices in the mesh
-
-  int verbosity;              // verbosity=0: don't print information
-                              // verbosity=1: print basic information on each pass
-                              // verbosity=2: print everything
+typedef struct {
+  HXTBbox* bbox;              /**< The bounding box for all vertices.
+                               *  - if bbox==NULL, the bbox is recomputed internally;
+                               *  - if bbox!=NULL, bbox must contain all vertices */
+
+  double* nodalSizes;         /**<
+                               *  - if nodalSize==NULL, doesn't restrict nodalSize;
+                               *  - if nodalSize!=NULL, nodalSize contains the minimum
+                               *  mesh size at each vertex.\n
+                               *  If the insertion of a vertex create an edge smaller than
+                               * the average nodalSize of its endpoints, the vertex is
+                               * not inserted
+                               *  \warning a segmentation fault will occur if a vertex
+                               * doesn't have a corresponding mesh size */
+
+  double minSizeStart;         /**< estimate of the minimum mesh size at the moment of the call. 
+                                * 0 if the mesh is empty or if the distribution is uniform
+                                * (the mesh size is then guessed with the number of point) */
+  double minSizeEnd;           /**< estimate of the minimum mesh size when all points are inserted in the Delaunay. 
+                                * 0 if the distribution is uniform
+                                * (the mesh size is then guessed with the number of point) */
+
+  uint32_t numVerticesInMesh; /**< The number of vertices in the mesh */
+
+  int verbosity;              /**<
+                               *  - if verbosity<=0: don't print information.
+                               *  - if verbosity==1: print basic information on each pass
+                               *  - if verbosity>=2: print everything */
+
+  int reproducible;           /**< If reproducible!=0, the Delaunay use a reproducible tetrahedra order
+                               * in order to be totally deterministic.
+                               * \warning this takes time !
+                               * It requires a total reordering of tetrahedra at the end to get a reproducible order\n
+                               * except if `delaunayThreads==1 || (delaunayThreads==0 && omp_get_max_threads()==1)`\n
+                               * in which case it is reproducible anyway */
+
+  int delaunayThreads;        /**< number of threads for the delaunay insertion
+                               *  - if delaunayThreads==0, it will use omp_get_max_threads()
+                               *  - if delaunayThreads<0, it will uses omp_get_num_procs() */
 } HXTDelaunayOptions;
 
 
-/* insert only vertices of index nodeInfo[i].nodes (does not change ordering of vertices !)
+/**
+ * \brief Delaunay of a set of vertices that does not modify their order
+ * \details This perform the insertion of the vertices whose indices are
+ * given in nodeInfo (in the \ref hxtNodeInfo.node wtructure member)\n
+ * This function does not change the order of vertices in the mesh.\n
+ * \ref hxtNodeInfo.status will be modified by the function to tell
+ * if the vertex was successfully inserted or not.
+ *  - nodeInfo[i].status==HXT_STATUS_TRUE  if the vertex was successfully inserted.
+ *  - nodeInfo[i].status==HXT_STATUS_FALSE  if the vertex was not inserted.
+ *  - nodeInfo[i].status==HXT_STATUS_TRYAGAIN  if an error occured before the vertex could be inserted
+ *
+ * \warning
+ *  - the order of nodeInfo will change
+ *  - hxtNodeInfo[i].hilbertDist will change
+ *  - mesh->tetrahedra.* will change
+ *  - mesh->vertices.coord[4*i+3] will change
  *
- * this function modifies
- *     mesh->tetrahedra.*
- *     mesh->vertices[*].dist
- *     hxtNodeInfo ordering will change
- *     hxtNodeInfo[*].index will change
- *     hxtNodeInfo[*].status = HXT_STATUS_TRUE  if the vertex was successfully inserted
- *     hxtNodeInfo[*].status = HXT_STATUS_FALSE if the vertex was not inserted
-                              (insertion would break the nodalSize or the vertex is already in the mesh)
- *     hxtNodeInfo[*].status = HXT_STATUS_TRYAGAIN if an error occured before the vertex could be inserted
+ * \param mesh: a valid Delaunay mesh
+ * \param options: options to give to the Delaunay algorithm \ref HXTDelaunayOptions
+ * \param[in, out] nodeInfo: the indices of the vertices to insert in the tetrahedral mesh.
+ * \param nToInset: the number of element in nodeInfo, hence the number of vertices to insert.
  */
 HXTStatus hxtDelaunaySteadyVertices(HXTMesh* mesh, HXTDelaunayOptions* options, hxtNodeInfo* nodeInfo, uint64_t nToInsert);
 
 
-/* insert vertices from numVerticesInMesh to mesh->vertices.num
+/**
+ * \brief Delaunay of a set of vertices
+ * \details This perform the insertion of the vertices
+ * from numVerticesInMesh to mesh->vertices.num\n
  *
- * this function modifies
- *     mesh->tetrahedra.*
- *     mesh->vertices[*].dist
- *     mesh->vertices can be reordered
- *     vertices that could not be inserted are deleted from mesh->vertices !
- *              (their insertion would break the nodalSize or the vertex is a duplicate)
+ * \warning
+ *  - the order of mesh->vertices will change
+ *  - hxtNodeInfo[i].hilbertDist will change
+ *  - mesh->tetrahedra.* will change
+ *  - mesh->vertices.coord[4*i+3] will change
+ *  - vertices that could not be inserted are deleted from mesh->vertices !
  *
- * DO NOT USE THIS FUNCTION IF OTHER STUFF DEPENDS ON THE VERTICES TO INSERT
- *
- * However, this version is a little bit faster...
+ * \param mesh: a valid Delaunay mesh
+ * \param options: options to give to the Delaunay algorithm \ref HXTDelaunayOptions
  */
 HXTStatus hxtDelaunay(HXTMesh* mesh, HXTDelaunayOptions* options);
 
 
-/* remove tetrahedra whose color are HXT_DELETED_COLOR */
-HXTStatus hxtRemoveDeleted(HXTMesh* mesh);
-
 #ifdef __cplusplus
 }
 #endif
diff --git a/contrib/hxt/hxt_tools.h b/contrib/hxt/hxt_tools.h
index 11421d57af31c4855666b84a7db4778a074b10b8..f9fcab01d11697ac56198b5295aea839c8ea2c22 100644
--- a/contrib/hxt/hxt_tools.h
+++ b/contrib/hxt/hxt_tools.h
@@ -14,13 +14,7 @@ extern "C" {
 
 /* define SIMD ALIGNMENT */
 #ifndef SIMD_ALIGN
-#ifdef __AVX512F__
 #define SIMD_ALIGN 64
-#elif defined(__AVX2__)
-#define SIMD_ALIGN 32
-#else
-#define SIMD_ALIGN 16 /* we align to 16 anyway even if no sse */
-#endif
 #endif
 
 // declare alignement of pointer allocated on the stack or in a struct
@@ -52,7 +46,7 @@ ___
 |  int arrayLength = ...;
 |  int *array;
 |  HXT_CHECK( hxtMalloc(&array, sizeof(int)*arrayLength) );
-|
+|  
 |  array[0] = ...;
 |  [...]
 |  array[arrayLength-1] = ...;
@@ -104,15 +98,6 @@ static inline HXTStatus hxtRealloc(void* ptrToPtr, size_t size)
 }
 
 
-// FIXME Gmsh: aligned routines do not seem to work on 32 bit machines
-#include <stdint.h>
-#if UINTPTR_MAX == 0xffffffff
-static inline HXTStatus hxtGetAlignedBlockSize(void* ptrToPtr, size_t* size){ *size = 0; return HXT_STATUS_OK; }
-static inline HXTStatus hxtAlignedMalloc(void* ptrToPtr, size_t size){ return hxtMalloc(ptrToPtr, size); }
-static inline HXTStatus hxtAlignedFree(void* ptrToPtr){ return hxtFree(ptrToPtr); }
-static inline HXTStatus hxtAlignedRealloc(void* ptrToPtr, size_t size){ return hxtRealloc(ptrToPtr, size); }
-#else
-
 /*********************************************************
  * Hextreme aligned malloc implementation
  *********************************************************/
@@ -152,7 +137,7 @@ static inline HXTStatus hxtAlignedMalloc(void* ptrToPtr, size_t size)
   p2 = (char**)(((size_t)(pstart) + startOffset) & ~(SIMD_ALIGN - 1)); // only keep bits ge SIMD_ALIGN
   p2[-1] = pstart;
   p2[-2] = pstart + size;
-  p2[-3] = pstart + (size ^ 0xBF58476D1CE4E5B9ULL); // makes a verification possible
+  p2[-3] = (char*) ((size_t) pstart + (size ^ 0xBF58476D1CE4E5B9ULL)); // makes a verification possible
   *(void**)ptrToPtr = p2;
 
   return HXT_STATUS_OK;
@@ -187,15 +172,15 @@ static inline HXTStatus hxtAlignedRealloc(void* ptrToPtr, size_t size)
     HXT_CHECK(hxtAlignedFree(ptrToPtr));
     return HXT_STATUS_OK;
   }
-
+  
   size_t old_size;
   HXT_CHECK( hxtGetAlignedBlockSize(ptrToPtr, &old_size) );
 
   if(size>old_size || size+4096<old_size){ // we do not shrink block to gain less than 4096 bytes
     void* newptr = NULL;
     HXT_CHECK( hxtAlignedMalloc(&newptr, size));
-
-    memcpy(newptr, *(void**)ptrToPtr, size>old_size?old_size:size);
+    if(old_size)
+      memcpy(newptr, *(void**)ptrToPtr, size>old_size?old_size:size);
 
     HXT_CHECK( hxtAlignedFree(ptrToPtr) );
 
@@ -205,7 +190,22 @@ static inline HXTStatus hxtAlignedRealloc(void* ptrToPtr, size_t size)
   return HXT_STATUS_OK;
 }
 
-#endif // FIXME Gmsh
+/*********************************************************
+  A way to call rand with a seed to get a reproducible
+  result.
+  For example, we do not call srand() each time we
+  call a reproducible Delaunay, else if someone was calling
+  rand(); Delaunay(); rand(); ...
+  he would always get the same result. We use 
+  hxtReproducibleRand() instead
+
+  !!!! 1st seed must absolutely be 1 !!!!
+**********************************************************/
+static inline uint32_t hxtReproducibleLCG(uint32_t *seed)
+{
+  *seed = 69621*(*seed)%2147483647;
+  return *seed;
+}
 
 /*********************************************************
  * Matrix operations
diff --git a/contrib/hxt/hxt_vertices.c b/contrib/hxt/hxt_vertices.c
index c3b482dc41cb3fe6d8e37975e3885e9cf72b55b6..5e1a15770c824339d67a198efa43c45e3932662d 100644
--- a/contrib/hxt/hxt_vertices.c
+++ b/contrib/hxt/hxt_vertices.c
@@ -3,6 +3,11 @@
 #include "hxt_vertices.h"
 #include "hxt_sort.h"
 
+/**
+* \file hxt_vertices.c see header hxt_vertices.h.
+* \author Célestin Marot
+*/
+
 /* create a nextbefore macro
  * for a strictly positive value, round to previous double value */
 #if (defined (__STD_VERSION__) && (__STD_VERSION__ >= 199901L)) // nextafter only from C99
@@ -33,12 +38,10 @@
  *
  *                           multiple of 11: 0 11 22 33 44 55 66
  * from this, we get that it's better to use 0 9  21 33 42 54 63 bit
- * corresponding to a nbr of iteration of    0 3  7  11 14 18 21
- *
- * note that 2 */
+ * corresponding to a nbr of iteration of    0 3  7  11 14 18 21 */
 uint32_t hxtAdvisedHilbertBits(const uint32_t n)
 {
-  uint32_t nlog2 = u32_log2(n*n*n)/2; // if we have n points, we want approximately n^1.5 possible hilbert coordinates
+  uint32_t nlog2 = u32_log2(n)*3/2; // if we have n points, we want approximately n^1.5 possible hilbert coordinates
 
   if(n < HXT_SORT_SEQUENTIAL_LIMIT){
      return (nlog2+7)/8*8/3*3;
@@ -48,20 +51,39 @@ uint32_t hxtAdvisedHilbertBits(const uint32_t n)
   }
 }
 
-#define ALPHA_FACTOR
 
 // we want a minimum of 16 blocks per partitions^2
 // we also want to have at least 16 blocks per vertex to sort
-uint32_t hxtAdvancedHilbertBits(const uint32_t nInitial, const uint32_t nToSort, const uint32_t nPartitions, const uint32_t maxPartitions){
-  uint32_t nlog2 = u32_log2((nToSort+nInitial/64)*nPartitions*SIMD_ALIGN/64*maxPartitions/128);
+uint32_t hxtAdvancedHilbertBits(HXTBbox* bbox, double sizeStart, double sizeEnd, uint32_t numStart, uint32_t numEnd, uint32_t numInMesh, uint32_t numToSort, uint32_t nthreads){
+    uint32_t logNthreads = u32_log2(nthreads+1);
+    double k1 = 0.065*(logNthreads+2); // how much the number of iteration depends on the mesh size
+    const double k2 = 0.38;   // how much the number of iteration depends on the number of point to sort
+    uint32_t num = numInMesh + numToSort/2;
+
+    if(sizeEnd==0.0){    // we have to guess the mesh size
+      // k1 *= (2./3.);  // minimum mesh size in uniform distribution
+      k1 *= (1./3.);     // grid-like
+    }
+    else{
+      double meanBBoxSize = ((bbox->max[0] - bbox->min[0])
+                           + (bbox->max[1] - bbox->min[1])
+                           + (bbox->max[2] - bbox->min[2]))/3.0;
 
-  if(nToSort < HXT_SORT_SEQUENTIAL_LIMIT){
-     return ((nlog2+7)/8*8)/3*3;
-  }
-  else{
-     return MIN(63,((nlog2+10)/11*11)/3*3);
-  }
-  return nlog2;
+      double k1End = (double) u64_log2((uint64_t) (meanBBoxSize/sizeEnd))/u32_log2(numEnd);
+
+      if(sizeStart==0.0){
+        k1 *= k1End;
+      }
+      else{
+        double alpha = (double) (num - numStart)/(numEnd-numStart + .1);
+        double k1Start = (double) u64_log2((uint64_t) (meanBBoxSize/sizeStart))/u32_log2(numStart);
+
+        k1 *= (1-alpha)*k1Start + alpha*k1End;
+      }
+    }
+
+    num = u32_log2(num)*k1 + u32_log2(numToSort)*k2;
+    return MIN(21, num)*3;
 }
 
 /*================================= compute the hilberts distance ===========================================
@@ -69,29 +91,30 @@ uint32_t hxtAdvancedHilbertBits(const uint32_t nInitial, const uint32_t nToSort,
  * This part is for computing the hilbert coordinates of the vertices (X,Y,Z)
  * shift[i] must be between 0 and 1
  */
-HXTStatus hxtVerticesHilbertDist(hxtBbox* bbox, hxtVertex* vertices, const uint32_t n, uint32_t* nbits, const double* shift)
+HXTStatus hxtVerticesHilbertDist(HXTBbox* bbox, HXTVertex* vertices, const uint32_t n, uint32_t* nbits, const double* shift)
 {
 HXT_ASSERT(vertices!=NULL);
 HXT_ASSERT(bbox!=NULL);
+HXT_ASSERT(nbits!=NULL);
 HXT_ASSERT_MSG(bbox->min[0]<bbox->max[0] ||
         bbox->min[1]<bbox->max[1] ||
         bbox->min[2]<bbox->max[2],"wrong bounding box");
-
-  uint32_t level;
-  if(nbits==NULL || *nbits==0){
-    *nbits = hxtAdvisedHilbertBits(n);
-    level = (*nbits + 2)/3;
-    *nbits = level*3;
+  
+  if(*nbits>63){
+    *nbits = 63;
   }
-  else if(*nbits>63){
-    level = 21;
-    *nbits = level*3;
+  else if(*nbits==0) {
+    #pragma omp parallel for simd
+    for (uint32_t i=0; i<n; i++)
+      vertices[i].padding.hilbertDist = 0;
+
+    return HXT_STATUS_OK;
   }
   else{
-    level = (*nbits + 2)/3;
-    *nbits = level*3;
+    *nbits = (*nbits+2)/3*3;
   }
 
+  const uint32_t level = *nbits/3;
   const double defaultShift[3] = {0.5,0.5,0.5};
 
   if(shift==NULL)
@@ -99,6 +122,14 @@ HXT_ASSERT_MSG(bbox->min[0]<bbox->max[0] ||
 
 /* this was a beautifull check of the parameters... */
 
+  // if(level==0){
+  //   #pragma omp parallel for
+  //   for (uint32_t i=0; i<n; i++)
+  //     vertices[i].padding.hilbertDist = 0;
+
+  //   return HXT_STATUS_OK;
+  // }
+
   double hxtDeclareAligned div1[3];
   double hxtDeclareAligned div2[3];
   double hxtDeclareAligned mean[3];
@@ -120,8 +151,6 @@ HXT_ASSERT_MSG(bbox->min[0]<bbox->max[0] ||
   }
 
 
-
-
   // const uint32_t invGCTable[8] = {0,1,3,2,7,6,4,5};
 
 #pragma omp parallel for simd
@@ -246,30 +275,30 @@ HXT_ASSERT_MSG(bbox->min[0]<bbox->max[0] ||
 }
 
 
-static inline uint64_t getVertexDist64(hxtVertex* const __restrict__  v, const void* user_data)
+static inline uint64_t getVertexDist64(HXTVertex* const __restrict__  v, const void* user_data)
 {
   return v->padding.hilbertDist;
 }
 
-static HXTStatus hxtVerticesSort64(hxtVertex* const __restrict__  vertices, const uint32_t n, const uint64_t distMax)
+static HXTStatus hxtVerticesSort64(HXTVertex* const __restrict__  vertices, const uint32_t n, const uint64_t distMax)
 {
-  HXTSORT64_UNIFORM(hxtVertex, vertices, n, distMax, getVertexDist64, NULL);
+  HXTSORT64_UNIFORM(HXTVertex, vertices, n, distMax, getVertexDist64, NULL);
   return HXT_STATUS_OK;
 }
 
-static inline uint32_t getVertexDist32(hxtVertex* const __restrict__  v, const void* user_data)
+static inline uint32_t getVertexDist32(HXTVertex* const __restrict__  v, const void* user_data)
 {
   return v->padding.hilbertDist;
 }
 
 // TODO: test if it's really worth it to have a 32 bit version
-static HXTStatus hxtVerticesSort32(hxtVertex* const __restrict__  vertices, const uint32_t n, const uint32_t distMax){
-  HXTSORT32_UNIFORM(hxtVertex, vertices, n, distMax, getVertexDist32, NULL);
+static HXTStatus hxtVerticesSort32(HXTVertex* const __restrict__  vertices, const uint32_t n, const uint32_t distMax){
+  HXTSORT32_UNIFORM(HXTVertex, vertices, n, distMax, getVertexDist32, NULL);
   return HXT_STATUS_OK;
 }
 
 
-HXTStatus hxtVerticesSort(hxtVertex* const __restrict__  vertices, const uint32_t n, uint32_t nbits)
+HXTStatus hxtVerticesSort(HXTVertex* const __restrict__  vertices, const uint32_t n, uint32_t nbits)
 {
   HXT_ASSERT(vertices!=NULL);
   if(nbits>64){
@@ -347,7 +376,7 @@ static inline uint32_t fastHash(uint32_t x) {
 }
 
 /* for the non-static function, use a 22 bit key and a sort with two pass so we don't need to copy */
-HXTStatus hxtVerticesShuffle(hxtVertex* const __restrict__ vertices, const uint32_t n){
+HXTStatus hxtVerticesShuffle(HXTVertex* const __restrict__ vertices, const uint32_t n){
   #pragma omp parallel for simd
   for (uint32_t i=0; i<n; i++){
     vertices[i].padding.hilbertDist = fastHash(i);
diff --git a/contrib/hxt/hxt_vertices.h b/contrib/hxt/hxt_vertices.h
index 9fff40894e73a93562563d993c2698dcb183fd6b..2550e8034838290c128a21f333075cb113887a8c 100644
--- a/contrib/hxt/hxt_vertices.h
+++ b/contrib/hxt/hxt_vertices.h
@@ -4,42 +4,107 @@
 #include "hxt_mesh.h"
 #include "hxt_bbox.h"
 
-typedef struct hxtVertexStruct{
+/**
+* \file hxt_vertices.h
+* Compute Hilbert/Moore indices of an array of vertices and sort them.
+* \author Célestin Marot
+*/
+
+/**
+ * \struct HXTVertex
+ * \brief Contain coordinates and a padding that can be used as a temporary value.
+ *
+ * We can cast mesh->vertices.coord to a HXTVertex
+ * in order to use the 4th coordinate as a temporary value
+ * of different types. It is used for sorting and for the Delaunay partitions.
+ */
+typedef struct {
   double coord[3];
   union {
     uint64_t hilbertDist;
     uint32_t index;
     HXTStatus status;
   } padding;
-} hxtVertex;
+} HXTVertex;
 
-typedef struct hxtNodeInfoStruct{
+
+/**
+ * \struct hxtNodeInfo
+ * \brief Simple structure with a vertex index, a Hilbert/Moore coordinate and a status
+ *
+ * Simple structure with a vertex index, a Hilbert/Moore coordinate and a status
+ * For example, when vertices cannot be moved, we sort that structure instead.
+ */
+typedef struct {
   uint64_t hilbertDist;
   uint32_t node;
   HXTStatus status; // is the vertex inserted ? true, false or try_again
 } hxtNodeInfo;
 
+
+/**
+ * \brief Evaluate the number of bits for the Hilbert/Moore indices
+ * \param n: number of points
+ * \return number of bits of Hilbert/Moore indices for a good enough resolution of points coordinates
+ *
+ * \details This function is aimed to work with Delaunay insertion.
+ * Given a number of points from a supposedly almost uniform distribution,
+ * this function guess what is the level of the Hilbert/Moore curve
+ * needed such that the Delaunay insertion performs well.
+ */
 uint32_t hxtAdvisedHilbertBits(const uint32_t n);
-uint32_t hxtAdvancedHilbertBits(const uint32_t nInitial, const uint32_t nToSort, const uint32_t nPartitions, const uint32_t maxPartitions);
 
-/* compute the hilbert distance of each vertex in vertex.dist 
- * @in bbox: the bounding box countaining the min and max values
- * @in vertices: an array of vertices corresponding to the bbox
- * @in n: the number of vertices
- * @in nbits: hint on the number of bits on which the hilbert distance must be computed
- *            use 0 if you want the function to find a good number of bits for you
- * @out nbits: the real number of bits on which the hilbert distance was computed
+/**
+ * Same as hxtAdvisedHilbertBits() but with much more information
+ * \param bbox: the bounding box of the mesh
+ * \param sizeStart: estimated mesh size at a previous time Tstart
+ * \param sizeEnd: estimated mesh size in the future time Tend
+ * \param numStart: number of vertices in the mesh at Tstart
+ * \param numStart: number of vertices in the mesh at Tend
+ * \param numInMesh: number of vertices in the mesh now
+ * \param numToSort: number of point to insert/sort at this insertion step
  */
-HXTStatus hxtVerticesHilbertDist(hxtBbox* bbox, hxtVertex* vertices, const uint32_t n, uint32_t* nbits, const double shift[3]);
+uint32_t hxtAdvancedHilbertBits(HXTBbox* bbox, double sizeStart, double sizeEnd, uint32_t numStart, uint32_t numEnd, uint32_t numInMesh, uint32_t numToSort, uint32_t nthreads);
 
+/**
+ * \brief Compute the Hilbert/Moore index of each vertex in `vertices`
+ * \param bbox: a bounding box that contain all the given vertices
+ * \param vertices: the vertices coordinates
+ * \param n: the number of vertices
+ * \param[in,out] nbits: Sugested number of bits of the resulting Hilbert/Moore indices. The real number of bits used is returned.
+ * \param shift[3]: a value between 0 and 1 that commands how the Hilbert/Moore curve is deformed in each dimension.
+ *  {0.5, 0.5, 0.5} is the underformed Hilbert/Moore curve.
+ *
+ * \details Compute the Hilbert/Moore index of each vertex in its \ref HXTVertex.padding.hilbertDist structure member.
+ *          The size of the Hilbert/Moore index in bits can be suggested. Use nbits=0 if you don't want to suggest anything.
+ *          If the size of the resulting Hilbert/Moore indices differ, the value of nbits is set to the real size in bits.
+ *          A deformation of the Hilbert/Moore curve can be done with the shift parameter
+ */
+HXTStatus hxtVerticesHilbertDist(HXTBbox* bbox, HXTVertex* vertices, const uint32_t n, uint32_t* nbits, const double shift[3]);
 
-/* sort all vertex using their dist value using a parallel radix sort with multiple pass 
- * for the sort to be done properly, nbits>=(nbr. of bits in vertices[i].dist) for all i<n
+
+/**
+ * Sort all the vertices following their \ref HXTVertex.padding.hilbertDist structure member.
+ *
+ * \param vertices: pointer to an array of vertices
+ * \param n: number of vertices in the array
+ * \param nbits: the maximum number of bits set in the \ref HXTVertex.padding.hilbertDist structure member.
+ */
+HXTStatus hxtVerticesSort(HXTVertex* const vertices, const uint32_t n, uint32_t nbits);
+
+/**
+ * Same as hxtVerticesSort(), but sort \ref hxtNodeInfo following their hilbertDist structure member.
  */
-HXTStatus hxtVerticesSort(hxtVertex* const vertices, const uint32_t n, uint32_t nbits);
 HXTStatus hxtNodeInfoSort(hxtNodeInfo* const array, const uint32_t n, uint32_t nbits);
 
-HXTStatus hxtVerticesShuffle(hxtVertex* const vertices, const uint32_t n);
+/**
+ * Shuffle vertices given in an array of \ref HXTVertex in a pseudo-random fashion (always the same random sequence).
+ */
+HXTStatus hxtVerticesShuffle(HXTVertex* const vertices, const uint32_t n);
+
+/**
+ * Shuffle vertices given in an array of \ref hxtNodeInfo in a pseudo-random fashion (always the same random sequence).
+ */
 HXTStatus hxtNodeInfoShuffle(hxtNodeInfo* const array, const uint32_t n);
 
 #endif
diff --git a/contrib/hxt/mesh3d_main.c b/contrib/hxt/mesh3d_main.c
deleted file mode 100644
index b6dcd86bea7af0ba91d9724c5d0a7eaeddb1e404..0000000000000000000000000000000000000000
--- a/contrib/hxt/mesh3d_main.c
+++ /dev/null
@@ -1,62 +0,0 @@
-#include <math.h>
-#include "hxt_message.h"
-#include "hxt_api.h"
-#include "hxt_mesh3d.h"
-#include "hxt_tetrahedra.h"
-#include "hxt_mesh.h"
-#include "hxt_bbox.h"
-
-HXTStatus mySize (double x[3], void* data, double* s){
-  *s = .1;
-  return HXT_STATUS_OK;
-}
-
-int main(int argc, char **argv) {
-  if (argc != 3)
-    return HXT_ERROR_MSG(HXT_STATUS_FAILED,"usage: mesh3d input.msh output.msh");
-
-  HXTContext *context;
-  hxtContextCreate(&context);
-  HXTMesh *mesh;
-  HXT_CHECK(hxtMeshCreate(context, &mesh));
-  HXT_CHECK(hxtMeshReadGmsh(mesh, argv[1]));
-
-  // we must first update the bbox
-  hxtBbox bbox;
-  hxtBboxInit(&bbox);
-  HXT_CHECK( hxtBboxAdd(&bbox, mesh->vertices.coord, mesh->vertices.num) );
-  
-  // impossible to put 0 !!!
-  for (uint32_t i = 0;i< mesh->vertices.num;i++){
-    mesh->vertices.coord[4*i  ] += 1.e-8*drand48();
-    mesh->vertices.coord[4*i+1] += 1.e-8*drand48();
-    mesh->vertices.coord[4*i+2] += 1.e-8*drand48();
-  }
-  
-  printf("creating an empty mesh with %u vertices\n",mesh->vertices.num);
-  HXT_CHECK(hxtEmptyMesh(mesh));
-  puts("verifying consistency of the mesh");
-  HXT_CHECK( hxtTetrahedraVerify(mesh) );
-
-  uint32_t nbMissing;
-  printf("verifying the boundary\n");
-  HXT_CHECK(hxtVerifyBoundary(mesh,&nbMissing));
-  printf("%7d missing faces\n",nbMissing);
-  uint32_t nbColors;
-  if (nbMissing == 0){
-    double *sizes;
-    HXT_CHECK(hxtComputeMeshSizeFromMesh (mesh, &sizes));
-    HXT_CHECK(hxtColorMesh(mesh,&nbColors));
-    HXTMeshSize *meshSize;
-    HXT_CHECK(hxtMeshSizeCreate (context,&meshSize));
-    HXT_CHECK(hxtMeshSizeCompute (meshSize, bbox.min, bbox.max, mySize, NULL));
-    HXT_CHECK(hxtRefineTetrahedra(mesh, meshSize, &sizes));
-    HXT_CHECK(hxtMeshSizeDelete (&meshSize));
-    HXT_CHECK(hxtFree(&sizes));
-    puts("verifying consistency of the mesh");
-    HXT_CHECK( hxtTetrahedraVerify(mesh) );
-  }
-  
-  HXT_CHECK(hxtMeshWriteGmsh(mesh, argv[2]));
-  return HXT_STATUS_OK;
-}
diff --git a/contrib/hxt/parametrization_main.c b/contrib/hxt/parametrization_main.c
deleted file mode 100644
index 540aea32b190e7c699b65590f758ac9071428d9b..0000000000000000000000000000000000000000
--- a/contrib/hxt/parametrization_main.c
+++ /dev/null
@@ -1,87 +0,0 @@
-#include "hxt_option.h" // to be used
-#include "hxt_parametrization.h"
-#include "hxt_linear_system.h"
-
-int main(int argc, char **argv) {
-
-  if (argc < 2)
-    return HXT_ERROR_MSG(HXT_STATUS_FAILED,"usage: parametrization input.msh");
-
-  HXT_CHECK(hxtInitializeLinearSystems(&argc, &argv));
-  HXTContext *context;
-  HXT_CHECK(hxtContextCreate(&context));
-  HXTMesh *mesh;
-  HXT_CHECK(hxtMeshCreate(context, &mesh));
-  HXT_CHECK(hxtMeshReadGmsh(mesh, argv[1]));
-  
-  printf("%f\n",mesh->vertices[0].coord[1]);
-
-  HXTParametrization *param;
-  HXT_CHECK(hxtParametrizationCreate(mesh,&param));
-  
-  int *colors=NULL, *nNodes=NULL, *nodes=NULL, nc;
-  double *uv=NULL;
-  HXT_CHECK(hxtParametrizationCompute(param,&colors,&nNodes,&nodes,&uv,&nc));
-
-
-  /////////
-
-  FILE* file = fopen("checkmyjob.msh","w");
-  fprintf(file,"$MeshFormat\n"
-               "2.2 0 %u\n"
-               "$EndMeshFormat\n"
-               "$Nodes\n"
-               "%u\n",(unsigned) sizeof(double), mesh->numVertices);
-  
-
-  { /* print the nodes */
-    uint32_t i;
-    for (i=0; i<mesh->numVertices; i++)
-      fprintf(file,"%u %f %f %f\n",i+1, mesh->vertices[i].coord[0], mesh->vertices[i].coord[1], mesh->vertices[i].coord[2]);
-  }
-
-  fprintf(file,"$EndNodes\n"
-	  "$Elements\n"
-	  "%lu\n",mesh->triangles.num);
-
-  for (uint64_t i=0; i<mesh->triangles.num; i++){
-    fprintf(file,"%lu 2 2 0 %d %u %u %u \n",i,
-              colors[i],
-              mesh->triangles.node[i*3]+1,
-              mesh->triangles.node[i*3 + 1]+1,
-              mesh->triangles.node[i*3 + 2]+1);
-  }
-
-
-  fputs("$EndElements\n",file);
-  fclose(file);
-
-  for(int c=0; c<nc; c++){
-    
-    char uname[32], vname[32];
-    sprintf(uname,"nodedata_u_%d.msh",c);
-    sprintf(vname,"nodedata_v_%d.msh",c);
-
-    FILE  *uf = fopen(uname,"w");
-    FILE  *vf = fopen(vname,"w");    
-        
-    fprintf(uf,"$MeshFormat\n2.2 0 8\n$EndMeshFormat\n$NodeData\n1\n\"u\"\n1\n0\n3\n0\n1\n%u\n",nNodes[c+1]-nNodes[c]);
-    fprintf(vf,"$MeshFormat\n2.2 0 8\n$EndMeshFormat\n$NodeData\n1\n\"v\"\n1\n0\n3\n0\n1\n%u\n",nNodes[c+1]-nNodes[c]);
-	
-
-    
-    for(int i=nNodes[c]; i<nNodes[c+1]; i++){
-      fprintf(uf,"%u %f\n",nodes[i]+1,uv[2*i+0]);
-      fprintf(vf,"%u %f\n",nodes[i]+1,uv[2*i+1]);
-    }
-      
-
-    fprintf(uf,"$EndNodeData");
-    fprintf(vf,"$EndNodeData");
-    
-    fclose(uf);
-    fclose(vf);
-  }
-
-  
-}
diff --git a/contrib/hxt/predicates.c b/contrib/hxt/predicates.c
index b015cd6bb60f985d21bbfb190e332815140c0459..727a32c3cbd86a7ed9ffa5f37bd74a16ab1341d7 100644
--- a/contrib/hxt/predicates.c
+++ b/contrib/hxt/predicates.c
@@ -331,7 +331,9 @@ REAL splitter;
 static REAL epsilon;         /* = 2^(-p).  Used to estimate roundoff errors. */
 /* A set of coefficients used to calculate maximum roundoff errors.          */
 static REAL resulterrbound;
+static REAL ccwerrboundA, ccwerrboundB, ccwerrboundC;
 static REAL o3derrboundB, o3derrboundC;
+static REAL iccerrboundA, iccerrboundB, iccerrboundC;
 static REAL isperrboundB, isperrboundC;
 
 // Static filters for orient3d() and insphere().
@@ -409,14 +411,19 @@ void exactinit(REAL maxx, REAL maxy, REAL maxz)
 
   /* Error bounds for orientation and incircle tests. */
   resulterrbound = (3.0 + 8.0 * epsilon) * epsilon;
+  ccwerrboundA = (3.0 + 16.0 * epsilon) * epsilon;
+  ccwerrboundB = (2.0 + 12.0 * epsilon) * epsilon;
+  ccwerrboundC = (9.0 + 64.0 * epsilon) * epsilon * epsilon;
   o3derrboundA = (7.0 + 56.0 * epsilon) * epsilon;
   o3derrboundB = (3.0 + 28.0 * epsilon) * epsilon;
   o3derrboundC = (26.0 + 288.0 * epsilon) * epsilon * epsilon;
+  iccerrboundA = (10.0 + 96.0 * epsilon) * epsilon;
+  iccerrboundB = (4.0 + 48.0 * epsilon) * epsilon;
+  iccerrboundC = (44.0 + 576.0 * epsilon) * epsilon * epsilon;
   isperrboundA = (16.0 + 224.0 * epsilon) * epsilon;
   isperrboundB = (5.0 + 72.0 * epsilon) * epsilon;
   isperrboundC = (71.0 + 1408.0 * epsilon) * epsilon * epsilon;
 
-
 // Calculate the two static filters for orient3d() and insphere() tests.
 // Added by H. Si, 2012-08-23.
 
@@ -624,31 +631,35 @@ int fast_expansion_sum_zeroelim(const int elen, const REAL *e, const int flen, c
   eindex = findex = 0;
   if ((fnow > enow) == (fnow > -enow)) {
     Q = enow;
-    enow = e[++eindex];
+    ++eindex;
   } else {
     Q = fnow;
-    fnow = f[++findex];
+    ++findex;
   }
   hindex = 0;
   if ((eindex < elen) && (findex < flen)) {
+    enow = e[eindex];
+    fnow = f[findex];
     if ((fnow > enow) == (fnow > -enow)) {
       Fast_Two_Sum(enow, Q, Qnew, hh);
-      enow = e[++eindex];
+      ++eindex;
     } else {
       Fast_Two_Sum(fnow, Q, Qnew, hh);
-      fnow = f[++findex];
+      ++findex;
     }
     Q = Qnew;
     if (hh != 0.0) {
       h[hindex++] = hh;
     }
     while ((eindex < elen) && (findex < flen)) {
+      enow = e[eindex];
+      fnow = f[findex];
       if ((fnow > enow) == (fnow > -enow)) {
         Two_Sum(Q, enow, Qnew, hh);
-        enow = e[++eindex];
+        ++eindex;
       } else {
         Two_Sum(Q, fnow, Qnew, hh);
-        fnow = f[++findex];
+        ++findex;
       }
       Q = Qnew;
       if (hh != 0.0) {
@@ -657,16 +668,18 @@ int fast_expansion_sum_zeroelim(const int elen, const REAL *e, const int flen, c
     }
   }
   while (eindex < elen) {
+    enow = e[eindex];
     Two_Sum(Q, enow, Qnew, hh);
-    enow = e[++eindex];
+    ++eindex;
     Q = Qnew;
     if (hh != 0.0) {
       h[hindex++] = hh;
     }
   }
   while (findex < flen) {
+    fnow = f[findex];
     Two_Sum(Q, fnow, Qnew, hh);
-    fnow = f[++findex];
+    ++findex;
     Q = Qnew;
     if (hh != 0.0) {
       h[hindex++] = hh;
@@ -798,6 +811,155 @@ static REAL estimate(const int elen, const REAL *e)
   return Q;
 }
 
+/*****************************************************************************/
+/*                                                                           */
+/*  orient2dfast()   Approximate 2D orientation test.  Nonrobust.            */
+/*  orient2dexact()   Exact 2D orientation test.  Robust.                    */
+/*  orient2dslow()   Another exact 2D orientation test.  Robust.             */
+/*  orient2d()   Adaptive exact 2D orientation test.  Robust.                */
+/*                                                                           */
+/*               Return a positive value if the points pa, pb, and pc occur  */
+/*               in counterclockwise order; a negative value if they occur   */
+/*               in clockwise order; and zero if they are collinear.  The    */
+/*               result is also a rough approximation of twice the signed    */
+/*               area of the triangle defined by the three points.           */
+/*                                                                           */
+/*  Only the first and last routine should be used; the middle two are for   */
+/*  timings.                                                                 */
+/*                                                                           */
+/*  The last three use exact arithmetic to ensure a correct answer.  The     */
+/*  result returned is the determinant of a matrix.  In orient2d() only,     */
+/*  this determinant is computed adaptively, in the sense that exact         */
+/*  arithmetic is used only to the degree it is needed to ensure that the    */
+/*  returned value has the correct sign.  Hence, orient2d() is usually quite */
+/*  fast, but will run more slowly when the input points are collinear or    */
+/*  nearly so.                                                               */
+/*                                                                           */
+/*****************************************************************************/
+
+REAL orient2dfast(const REAL* __restrict__ pa, const REAL* __restrict__ pb, const REAL* __restrict__ pc)
+{
+  REAL acx, bcx, acy, bcy;
+
+  acx = pa[0] - pc[0];
+  bcx = pb[0] - pc[0];
+  acy = pa[1] - pc[1];
+  bcy = pb[1] - pc[1];
+  return acx * bcy - acy * bcx;
+}
+
+REAL orient2dadapt(const REAL* __restrict__ pa, const REAL* __restrict__ pb, const REAL* __restrict__ pc, const REAL detsum)
+{
+  INEXACT REAL acx, acy, bcx, bcy;
+  REAL acxtail, acytail, bcxtail, bcytail;
+  INEXACT REAL detleft, detright;
+  REAL detlefttail, detrighttail;
+  REAL det, errbound;
+  REAL B[4], C1[8], C2[12], D[16];
+  INEXACT REAL B3;
+  int C1length, C2length, Dlength;
+  REAL u[4];
+  INEXACT REAL u3;
+  INEXACT REAL s1, t1;
+  REAL s0, t0;
+
+  INEXACT REAL bvirt;
+  REAL avirt, bround, around;
+  INEXACT REAL c;
+  INEXACT REAL abig;
+  REAL ahi, alo, bhi, blo;
+  REAL err1, err2, err3;
+  INEXACT REAL _i, _j;
+  REAL _0;
+
+  acx = (REAL) (pa[0] - pc[0]);
+  bcx = (REAL) (pb[0] - pc[0]);
+  acy = (REAL) (pa[1] - pc[1]);
+  bcy = (REAL) (pb[1] - pc[1]);
+
+  Two_Product(acx, bcy, detleft, detlefttail);
+  Two_Product(acy, bcx, detright, detrighttail);
+
+  Two_Two_Diff(detleft, detlefttail, detright, detrighttail,
+               B3, B[2], B[1], B[0]);
+  B[3] = B3;
+
+  det = estimate(4, B);
+  errbound = ccwerrboundB * detsum;
+  if ((det >= errbound) || (-det >= errbound)) {
+    return det;
+  }
+
+  Two_Diff_Tail(pa[0], pc[0], acx, acxtail);
+  Two_Diff_Tail(pb[0], pc[0], bcx, bcxtail);
+  Two_Diff_Tail(pa[1], pc[1], acy, acytail);
+  Two_Diff_Tail(pb[1], pc[1], bcy, bcytail);
+
+  if ((acxtail == 0.0) && (acytail == 0.0)
+      && (bcxtail == 0.0) && (bcytail == 0.0)) {
+    return det;
+  }
+
+  errbound = ccwerrboundC * detsum + resulterrbound * Absolute(det);
+  det += (acx * bcytail + bcy * acxtail)
+       - (acy * bcxtail + bcx * acytail);
+  if ((det >= errbound) || (-det >= errbound)) {
+    return det;
+  }
+
+  Two_Product(acxtail, bcy, s1, s0);
+  Two_Product(acytail, bcx, t1, t0);
+  Two_Two_Diff(s1, s0, t1, t0, u3, u[2], u[1], u[0]);
+  u[3] = u3;
+  C1length = fast_expansion_sum_zeroelim(4, B, 4, u, C1);
+
+  Two_Product(acx, bcytail, s1, s0);
+  Two_Product(acy, bcxtail, t1, t0);
+  Two_Two_Diff(s1, s0, t1, t0, u3, u[2], u[1], u[0]);
+  u[3] = u3;
+  C2length = fast_expansion_sum_zeroelim(C1length, C1, 4, u, C2);
+
+  Two_Product(acxtail, bcytail, s1, s0);
+  Two_Product(acytail, bcxtail, t1, t0);
+  Two_Two_Diff(s1, s0, t1, t0, u3, u[2], u[1], u[0]);
+  u[3] = u3;
+  Dlength = fast_expansion_sum_zeroelim(C2length, C2, 4, u, D);
+
+  return(D[Dlength - 1]);
+}
+
+REAL orient2d(const REAL* __restrict__ pa, const REAL* __restrict__ pb, const REAL* __restrict__ pc)
+{
+  REAL detleft, detright, det;
+  REAL detsum, errbound;
+
+  detleft = (pa[0] - pc[0]) * (pb[1] - pc[1]);
+  detright = (pa[1] - pc[1]) * (pb[0] - pc[0]);
+  det = detleft - detright;
+
+  if (detleft > 0.0) {
+    if (detright <= 0.0) {
+      return det;
+    } else {
+      detsum = detleft + detright;
+    }
+  } else if (detleft < 0.0) {
+    if (detright >= 0.0) {
+      return det;
+    } else {
+      detsum = -detleft - detright;
+    }
+  } else {
+    return det;
+  }
+
+  errbound = ccwerrboundA * detsum;
+  if ((det >= errbound) || (-det >= errbound)) {
+    return det;
+  }
+
+  return orient2dadapt(pa, pb, pc, detsum);
+}
 
 /*****************************************************************************/
 /*                                                                           */
@@ -1291,6 +1453,668 @@ REAL orient3d(const REAL* const __restrict__ pa, const REAL* const __restrict__
   return orient3dadapt(pa, pb, pc, pd, permanent);
 }
 
+/*****************************************************************************/
+/*                                                                           */
+/*  incirclefast()   Approximate 2D incircle test.  Nonrobust.               */
+/*  incircleexact()   Exact 2D incircle test.  Robust.                       */
+/*  incircleslow()   Another exact 2D incircle test.  Robust.                */
+/*  incircle()   Adaptive exact 2D incircle test.  Robust.                   */
+/*                                                                           */
+/*               Return a positive value if the point pd lies inside the     */
+/*               circle passing through pa, pb, and pc; a negative value if  */
+/*               it lies outside; and zero if the four points are cocircular.*/
+/*               The points pa, pb, and pc must be in counterclockwise       */
+/*               order, or the sign of the result will be reversed.          */
+/*                                                                           */
+/*  Only the first and last routine should be used; the middle two are for   */
+/*  timings.                                                                 */
+/*                                                                           */
+/*  The last three use exact arithmetic to ensure a correct answer.  The     */
+/*  result returned is the determinant of a matrix.  In incircle() only,     */
+/*  this determinant is computed adaptively, in the sense that exact         */
+/*  arithmetic is used only to the degree it is needed to ensure that the    */
+/*  returned value has the correct sign.  Hence, incircle() is usually quite */
+/*  fast, but will run more slowly when the input points are cocircular or   */
+/*  nearly so.                                                               */
+/*                                                                           */
+/*****************************************************************************/
+
+REAL incirclefast(const REAL* __restrict__ pa, const REAL* __restrict__ pb, const REAL* __restrict__ pc, const REAL* __restrict__ pd)
+{
+  REAL adx, ady, bdx, bdy, cdx, cdy;
+  REAL abdet, bcdet, cadet;
+  REAL alift, blift, clift;
+
+  adx = pa[0] - pd[0];
+  ady = pa[1] - pd[1];
+  bdx = pb[0] - pd[0];
+  bdy = pb[1] - pd[1];
+  cdx = pc[0] - pd[0];
+  cdy = pc[1] - pd[1];
+
+  abdet = adx * bdy - bdx * ady;
+  bcdet = bdx * cdy - cdx * bdy;
+  cadet = cdx * ady - adx * cdy;
+  alift = adx * adx + ady * ady;
+  blift = bdx * bdx + bdy * bdy;
+  clift = cdx * cdx + cdy * cdy;
+
+  return alift * bcdet + blift * cadet + clift * abdet;
+}
+
+REAL incircleadapt(const REAL* __restrict__ pa, const REAL* __restrict__ pb, const REAL* __restrict__ pc, const REAL* __restrict__ pd, const REAL permanent)
+{
+  INEXACT REAL adx, bdx, cdx, ady, bdy, cdy;
+  REAL det, errbound;
+
+  INEXACT REAL bdxcdy1, cdxbdy1, cdxady1, adxcdy1, adxbdy1, bdxady1;
+  REAL bdxcdy0, cdxbdy0, cdxady0, adxcdy0, adxbdy0, bdxady0;
+  REAL bc[4], ca[4], ab[4];
+  INEXACT REAL bc3, ca3, ab3;
+  REAL axbc[8], axxbc[16], aybc[8], ayybc[16], adet[32];
+  int axbclen, axxbclen, aybclen, ayybclen, alen;
+  REAL bxca[8], bxxca[16], byca[8], byyca[16], bdet[32];
+  int bxcalen, bxxcalen, bycalen, byycalen, blen;
+  REAL cxab[8], cxxab[16], cyab[8], cyyab[16], cdet[32];
+  int cxablen, cxxablen, cyablen, cyyablen, clen;
+  REAL abdet[64];
+  int ablen;
+  REAL fin1[1152], fin2[1152];
+  REAL *finnow, *finother, *finswap;
+  int finlength;
+
+  REAL adxtail, bdxtail, cdxtail, adytail, bdytail, cdytail;
+  INEXACT REAL adxadx1, adyady1, bdxbdx1, bdybdy1, cdxcdx1, cdycdy1;
+  REAL adxadx0, adyady0, bdxbdx0, bdybdy0, cdxcdx0, cdycdy0;
+  REAL aa[4], bb[4], cc[4];
+  INEXACT REAL aa3, bb3, cc3;
+  INEXACT REAL ti1, tj1;
+  REAL ti0, tj0;
+  REAL u[4], v[4];
+  INEXACT REAL u3, v3;
+  REAL temp8[8], temp16a[16], temp16b[16], temp16c[16];
+  REAL temp32a[32], temp32b[32], temp48[48], temp64[64];
+  int temp8len, temp16alen, temp16blen, temp16clen;
+  int temp32alen, temp32blen, temp48len, temp64len;
+  REAL axtbb[8], axtcc[8], aytbb[8], aytcc[8];
+  int axtbblen, axtcclen, aytbblen, aytcclen;
+  REAL bxtaa[8], bxtcc[8], bytaa[8], bytcc[8];
+  int bxtaalen, bxtcclen, bytaalen, bytcclen;
+  REAL cxtaa[8], cxtbb[8], cytaa[8], cytbb[8];
+  int cxtaalen, cxtbblen, cytaalen, cytbblen;
+  REAL axtbc[8], aytbc[8], bxtca[8], bytca[8], cxtab[8], cytab[8];
+  int axtbclen, aytbclen, bxtcalen, bytcalen, cxtablen, cytablen;
+  REAL axtbct[16], aytbct[16], bxtcat[16], bytcat[16], cxtabt[16], cytabt[16];
+  int axtbctlen, aytbctlen, bxtcatlen, bytcatlen, cxtabtlen, cytabtlen;
+  REAL axtbctt[8], aytbctt[8], bxtcatt[8];
+  REAL bytcatt[8], cxtabtt[8], cytabtt[8];
+  int axtbcttlen, aytbcttlen, bxtcattlen, bytcattlen, cxtabttlen, cytabttlen;
+  REAL abt[8], bct[8], cat[8];
+  int abtlen, bctlen, catlen;
+  REAL abtt[4], bctt[4], catt[4];
+  int abttlen, bcttlen, cattlen;
+  INEXACT REAL abtt3, bctt3, catt3;
+  REAL negate;
+
+  INEXACT REAL bvirt;
+  REAL avirt, bround, around;
+  INEXACT REAL c;
+  INEXACT REAL abig;
+  REAL ahi, alo, bhi, blo;
+  REAL err1, err2, err3;
+  INEXACT REAL _i, _j;
+  REAL _0;
+
+  // Avoid compiler warnings. H. Si, 2012-02-16.
+  axtbclen = aytbclen = bxtcalen = bytcalen = cxtablen = cytablen = 0;
+
+  adx = (REAL) (pa[0] - pd[0]);
+  bdx = (REAL) (pb[0] - pd[0]);
+  cdx = (REAL) (pc[0] - pd[0]);
+  ady = (REAL) (pa[1] - pd[1]);
+  bdy = (REAL) (pb[1] - pd[1]);
+  cdy = (REAL) (pc[1] - pd[1]);
+
+  Two_Product(bdx, cdy, bdxcdy1, bdxcdy0);
+  Two_Product(cdx, bdy, cdxbdy1, cdxbdy0);
+  Two_Two_Diff(bdxcdy1, bdxcdy0, cdxbdy1, cdxbdy0, bc3, bc[2], bc[1], bc[0]);
+  bc[3] = bc3;
+  axbclen = scale_expansion_zeroelim(4, bc, adx, axbc);
+  axxbclen = scale_expansion_zeroelim(axbclen, axbc, adx, axxbc);
+  aybclen = scale_expansion_zeroelim(4, bc, ady, aybc);
+  ayybclen = scale_expansion_zeroelim(aybclen, aybc, ady, ayybc);
+  alen = fast_expansion_sum_zeroelim(axxbclen, axxbc, ayybclen, ayybc, adet);
+
+  Two_Product(cdx, ady, cdxady1, cdxady0);
+  Two_Product(adx, cdy, adxcdy1, adxcdy0);
+  Two_Two_Diff(cdxady1, cdxady0, adxcdy1, adxcdy0, ca3, ca[2], ca[1], ca[0]);
+  ca[3] = ca3;
+  bxcalen = scale_expansion_zeroelim(4, ca, bdx, bxca);
+  bxxcalen = scale_expansion_zeroelim(bxcalen, bxca, bdx, bxxca);
+  bycalen = scale_expansion_zeroelim(4, ca, bdy, byca);
+  byycalen = scale_expansion_zeroelim(bycalen, byca, bdy, byyca);
+  blen = fast_expansion_sum_zeroelim(bxxcalen, bxxca, byycalen, byyca, bdet);
+
+  Two_Product(adx, bdy, adxbdy1, adxbdy0);
+  Two_Product(bdx, ady, bdxady1, bdxady0);
+  Two_Two_Diff(adxbdy1, adxbdy0, bdxady1, bdxady0, ab3, ab[2], ab[1], ab[0]);
+  ab[3] = ab3;
+  cxablen = scale_expansion_zeroelim(4, ab, cdx, cxab);
+  cxxablen = scale_expansion_zeroelim(cxablen, cxab, cdx, cxxab);
+  cyablen = scale_expansion_zeroelim(4, ab, cdy, cyab);
+  cyyablen = scale_expansion_zeroelim(cyablen, cyab, cdy, cyyab);
+  clen = fast_expansion_sum_zeroelim(cxxablen, cxxab, cyyablen, cyyab, cdet);
+
+  ablen = fast_expansion_sum_zeroelim(alen, adet, blen, bdet, abdet);
+  finlength = fast_expansion_sum_zeroelim(ablen, abdet, clen, cdet, fin1);
+
+  det = estimate(finlength, fin1);
+  errbound = iccerrboundB * permanent;
+  if ((det >= errbound) || (-det >= errbound)) {
+    return det;
+  }
+
+  Two_Diff_Tail(pa[0], pd[0], adx, adxtail);
+  Two_Diff_Tail(pa[1], pd[1], ady, adytail);
+  Two_Diff_Tail(pb[0], pd[0], bdx, bdxtail);
+  Two_Diff_Tail(pb[1], pd[1], bdy, bdytail);
+  Two_Diff_Tail(pc[0], pd[0], cdx, cdxtail);
+  Two_Diff_Tail(pc[1], pd[1], cdy, cdytail);
+  if ((adxtail == 0.0) && (bdxtail == 0.0) && (cdxtail == 0.0)
+      && (adytail == 0.0) && (bdytail == 0.0) && (cdytail == 0.0)) {
+    return det;
+  }
+
+  errbound = iccerrboundC * permanent + resulterrbound * Absolute(det);
+  det += ((adx * adx + ady * ady) * ((bdx * cdytail + cdy * bdxtail)
+                                     - (bdy * cdxtail + cdx * bdytail))
+          + 2.0 * (adx * adxtail + ady * adytail) * (bdx * cdy - bdy * cdx))
+       + ((bdx * bdx + bdy * bdy) * ((cdx * adytail + ady * cdxtail)
+                                     - (cdy * adxtail + adx * cdytail))
+          + 2.0 * (bdx * bdxtail + bdy * bdytail) * (cdx * ady - cdy * adx))
+       + ((cdx * cdx + cdy * cdy) * ((adx * bdytail + bdy * adxtail)
+                                     - (ady * bdxtail + bdx * adytail))
+          + 2.0 * (cdx * cdxtail + cdy * cdytail) * (adx * bdy - ady * bdx));
+  if ((det >= errbound) || (-det >= errbound)) {
+    return det;
+  }
+
+  finnow = fin1;
+  finother = fin2;
+
+  if ((bdxtail != 0.0) || (bdytail != 0.0)
+      || (cdxtail != 0.0) || (cdytail != 0.0)) {
+    Square(adx, adxadx1, adxadx0);
+    Square(ady, adyady1, adyady0);
+    Two_Two_Sum(adxadx1, adxadx0, adyady1, adyady0, aa3, aa[2], aa[1], aa[0]);
+    aa[3] = aa3;
+  }
+  if ((cdxtail != 0.0) || (cdytail != 0.0)
+      || (adxtail != 0.0) || (adytail != 0.0)) {
+    Square(bdx, bdxbdx1, bdxbdx0);
+    Square(bdy, bdybdy1, bdybdy0);
+    Two_Two_Sum(bdxbdx1, bdxbdx0, bdybdy1, bdybdy0, bb3, bb[2], bb[1], bb[0]);
+    bb[3] = bb3;
+  }
+  if ((adxtail != 0.0) || (adytail != 0.0)
+      || (bdxtail != 0.0) || (bdytail != 0.0)) {
+    Square(cdx, cdxcdx1, cdxcdx0);
+    Square(cdy, cdycdy1, cdycdy0);
+    Two_Two_Sum(cdxcdx1, cdxcdx0, cdycdy1, cdycdy0, cc3, cc[2], cc[1], cc[0]);
+    cc[3] = cc3;
+  }
+
+  if (adxtail != 0.0) {
+    axtbclen = scale_expansion_zeroelim(4, bc, adxtail, axtbc);
+    temp16alen = scale_expansion_zeroelim(axtbclen, axtbc, 2.0 * adx,
+                                          temp16a);
+
+    axtcclen = scale_expansion_zeroelim(4, cc, adxtail, axtcc);
+    temp16blen = scale_expansion_zeroelim(axtcclen, axtcc, bdy, temp16b);
+
+    axtbblen = scale_expansion_zeroelim(4, bb, adxtail, axtbb);
+    temp16clen = scale_expansion_zeroelim(axtbblen, axtbb, -cdy, temp16c);
+
+    temp32alen = fast_expansion_sum_zeroelim(temp16alen, temp16a,
+                                            temp16blen, temp16b, temp32a);
+    temp48len = fast_expansion_sum_zeroelim(temp16clen, temp16c,
+                                            temp32alen, temp32a, temp48);
+    finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len,
+                                            temp48, finother);
+    finswap = finnow; finnow = finother; finother = finswap;
+  }
+  if (adytail != 0.0) {
+    aytbclen = scale_expansion_zeroelim(4, bc, adytail, aytbc);
+    temp16alen = scale_expansion_zeroelim(aytbclen, aytbc, 2.0 * ady,
+                                          temp16a);
+
+    aytbblen = scale_expansion_zeroelim(4, bb, adytail, aytbb);
+    temp16blen = scale_expansion_zeroelim(aytbblen, aytbb, cdx, temp16b);
+
+    aytcclen = scale_expansion_zeroelim(4, cc, adytail, aytcc);
+    temp16clen = scale_expansion_zeroelim(aytcclen, aytcc, -bdx, temp16c);
+
+    temp32alen = fast_expansion_sum_zeroelim(temp16alen, temp16a,
+                                            temp16blen, temp16b, temp32a);
+    temp48len = fast_expansion_sum_zeroelim(temp16clen, temp16c,
+                                            temp32alen, temp32a, temp48);
+    finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len,
+                                            temp48, finother);
+    finswap = finnow; finnow = finother; finother = finswap;
+  }
+  if (bdxtail != 0.0) {
+    bxtcalen = scale_expansion_zeroelim(4, ca, bdxtail, bxtca);
+    temp16alen = scale_expansion_zeroelim(bxtcalen, bxtca, 2.0 * bdx,
+                                          temp16a);
+
+    bxtaalen = scale_expansion_zeroelim(4, aa, bdxtail, bxtaa);
+    temp16blen = scale_expansion_zeroelim(bxtaalen, bxtaa, cdy, temp16b);
+
+    bxtcclen = scale_expansion_zeroelim(4, cc, bdxtail, bxtcc);
+    temp16clen = scale_expansion_zeroelim(bxtcclen, bxtcc, -ady, temp16c);
+
+    temp32alen = fast_expansion_sum_zeroelim(temp16alen, temp16a,
+                                            temp16blen, temp16b, temp32a);
+    temp48len = fast_expansion_sum_zeroelim(temp16clen, temp16c,
+                                            temp32alen, temp32a, temp48);
+    finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len,
+                                            temp48, finother);
+    finswap = finnow; finnow = finother; finother = finswap;
+  }
+  if (bdytail != 0.0) {
+    bytcalen = scale_expansion_zeroelim(4, ca, bdytail, bytca);
+    temp16alen = scale_expansion_zeroelim(bytcalen, bytca, 2.0 * bdy,
+                                          temp16a);
+
+    bytcclen = scale_expansion_zeroelim(4, cc, bdytail, bytcc);
+    temp16blen = scale_expansion_zeroelim(bytcclen, bytcc, adx, temp16b);
+
+    bytaalen = scale_expansion_zeroelim(4, aa, bdytail, bytaa);
+    temp16clen = scale_expansion_zeroelim(bytaalen, bytaa, -cdx, temp16c);
+
+    temp32alen = fast_expansion_sum_zeroelim(temp16alen, temp16a,
+                                            temp16blen, temp16b, temp32a);
+    temp48len = fast_expansion_sum_zeroelim(temp16clen, temp16c,
+                                            temp32alen, temp32a, temp48);
+    finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len,
+                                            temp48, finother);
+    finswap = finnow; finnow = finother; finother = finswap;
+  }
+  if (cdxtail != 0.0) {
+    cxtablen = scale_expansion_zeroelim(4, ab, cdxtail, cxtab);
+    temp16alen = scale_expansion_zeroelim(cxtablen, cxtab, 2.0 * cdx,
+                                          temp16a);
+
+    cxtbblen = scale_expansion_zeroelim(4, bb, cdxtail, cxtbb);
+    temp16blen = scale_expansion_zeroelim(cxtbblen, cxtbb, ady, temp16b);
+
+    cxtaalen = scale_expansion_zeroelim(4, aa, cdxtail, cxtaa);
+    temp16clen = scale_expansion_zeroelim(cxtaalen, cxtaa, -bdy, temp16c);
+
+    temp32alen = fast_expansion_sum_zeroelim(temp16alen, temp16a,
+                                            temp16blen, temp16b, temp32a);
+    temp48len = fast_expansion_sum_zeroelim(temp16clen, temp16c,
+                                            temp32alen, temp32a, temp48);
+    finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len,
+                                            temp48, finother);
+    finswap = finnow; finnow = finother; finother = finswap;
+  }
+  if (cdytail != 0.0) {
+    cytablen = scale_expansion_zeroelim(4, ab, cdytail, cytab);
+    temp16alen = scale_expansion_zeroelim(cytablen, cytab, 2.0 * cdy,
+                                          temp16a);
+
+    cytaalen = scale_expansion_zeroelim(4, aa, cdytail, cytaa);
+    temp16blen = scale_expansion_zeroelim(cytaalen, cytaa, bdx, temp16b);
+
+    cytbblen = scale_expansion_zeroelim(4, bb, cdytail, cytbb);
+    temp16clen = scale_expansion_zeroelim(cytbblen, cytbb, -adx, temp16c);
+
+    temp32alen = fast_expansion_sum_zeroelim(temp16alen, temp16a,
+                                            temp16blen, temp16b, temp32a);
+    temp48len = fast_expansion_sum_zeroelim(temp16clen, temp16c,
+                                            temp32alen, temp32a, temp48);
+    finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len,
+                                            temp48, finother);
+    finswap = finnow; finnow = finother; finother = finswap;
+  }
+
+  if ((adxtail != 0.0) || (adytail != 0.0)) {
+    if ((bdxtail != 0.0) || (bdytail != 0.0)
+        || (cdxtail != 0.0) || (cdytail != 0.0)) {
+      Two_Product(bdxtail, cdy, ti1, ti0);
+      Two_Product(bdx, cdytail, tj1, tj0);
+      Two_Two_Sum(ti1, ti0, tj1, tj0, u3, u[2], u[1], u[0]);
+      u[3] = u3;
+      negate = -bdy;
+      Two_Product(cdxtail, negate, ti1, ti0);
+      negate = -bdytail;
+      Two_Product(cdx, negate, tj1, tj0);
+      Two_Two_Sum(ti1, ti0, tj1, tj0, v3, v[2], v[1], v[0]);
+      v[3] = v3;
+      bctlen = fast_expansion_sum_zeroelim(4, u, 4, v, bct);
+
+      Two_Product(bdxtail, cdytail, ti1, ti0);
+      Two_Product(cdxtail, bdytail, tj1, tj0);
+      Two_Two_Diff(ti1, ti0, tj1, tj0, bctt3, bctt[2], bctt[1], bctt[0]);
+      bctt[3] = bctt3;
+      bcttlen = 4;
+    } else {
+      bct[0] = 0.0;
+      bctlen = 1;
+      bctt[0] = 0.0;
+      bcttlen = 1;
+    }
+
+    if (adxtail != 0.0) {
+      temp16alen = scale_expansion_zeroelim(axtbclen, axtbc, adxtail, temp16a);
+      axtbctlen = scale_expansion_zeroelim(bctlen, bct, adxtail, axtbct);
+      temp32alen = scale_expansion_zeroelim(axtbctlen, axtbct, 2.0 * adx,
+                                            temp32a);
+      temp48len = fast_expansion_sum_zeroelim(temp16alen, temp16a,
+                                              temp32alen, temp32a, temp48);
+      finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len,
+                                              temp48, finother);
+      finswap = finnow; finnow = finother; finother = finswap;
+      if (bdytail != 0.0) {
+        temp8len = scale_expansion_zeroelim(4, cc, adxtail, temp8);
+        temp16alen = scale_expansion_zeroelim(temp8len, temp8, bdytail,
+                                              temp16a);
+        finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp16alen,
+                                                temp16a, finother);
+        finswap = finnow; finnow = finother; finother = finswap;
+      }
+      if (cdytail != 0.0) {
+        temp8len = scale_expansion_zeroelim(4, bb, -adxtail, temp8);
+        temp16alen = scale_expansion_zeroelim(temp8len, temp8, cdytail,
+                                              temp16a);
+        finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp16alen,
+                                                temp16a, finother);
+        finswap = finnow; finnow = finother; finother = finswap;
+      }
+
+      temp32alen = scale_expansion_zeroelim(axtbctlen, axtbct, adxtail,
+                                            temp32a);
+      axtbcttlen = scale_expansion_zeroelim(bcttlen, bctt, adxtail, axtbctt);
+      temp16alen = scale_expansion_zeroelim(axtbcttlen, axtbctt, 2.0 * adx,
+                                            temp16a);
+      temp16blen = scale_expansion_zeroelim(axtbcttlen, axtbctt, adxtail,
+                                            temp16b);
+      temp32blen = fast_expansion_sum_zeroelim(temp16alen, temp16a,
+                                              temp16blen, temp16b, temp32b);
+      temp64len = fast_expansion_sum_zeroelim(temp32alen, temp32a,
+                                              temp32blen, temp32b, temp64);
+      finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp64len,
+                                              temp64, finother);
+      finswap = finnow; finnow = finother; finother = finswap;
+    }
+    if (adytail != 0.0) {
+      temp16alen = scale_expansion_zeroelim(aytbclen, aytbc, adytail, temp16a);
+      aytbctlen = scale_expansion_zeroelim(bctlen, bct, adytail, aytbct);
+      temp32alen = scale_expansion_zeroelim(aytbctlen, aytbct, 2.0 * ady,
+                                            temp32a);
+      temp48len = fast_expansion_sum_zeroelim(temp16alen, temp16a,
+                                              temp32alen, temp32a, temp48);
+      finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len,
+                                              temp48, finother);
+      finswap = finnow; finnow = finother; finother = finswap;
+
+
+      temp32alen = scale_expansion_zeroelim(aytbctlen, aytbct, adytail,
+                                            temp32a);
+      aytbcttlen = scale_expansion_zeroelim(bcttlen, bctt, adytail, aytbctt);
+      temp16alen = scale_expansion_zeroelim(aytbcttlen, aytbctt, 2.0 * ady,
+                                            temp16a);
+      temp16blen = scale_expansion_zeroelim(aytbcttlen, aytbctt, adytail,
+                                            temp16b);
+      temp32blen = fast_expansion_sum_zeroelim(temp16alen, temp16a,
+                                              temp16blen, temp16b, temp32b);
+      temp64len = fast_expansion_sum_zeroelim(temp32alen, temp32a,
+                                              temp32blen, temp32b, temp64);
+      finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp64len,
+                                              temp64, finother);
+      finswap = finnow; finnow = finother; finother = finswap;
+    }
+  }
+  if ((bdxtail != 0.0) || (bdytail != 0.0)) {
+    if ((cdxtail != 0.0) || (cdytail != 0.0)
+        || (adxtail != 0.0) || (adytail != 0.0)) {
+      Two_Product(cdxtail, ady, ti1, ti0);
+      Two_Product(cdx, adytail, tj1, tj0);
+      Two_Two_Sum(ti1, ti0, tj1, tj0, u3, u[2], u[1], u[0]);
+      u[3] = u3;
+      negate = -cdy;
+      Two_Product(adxtail, negate, ti1, ti0);
+      negate = -cdytail;
+      Two_Product(adx, negate, tj1, tj0);
+      Two_Two_Sum(ti1, ti0, tj1, tj0, v3, v[2], v[1], v[0]);
+      v[3] = v3;
+      catlen = fast_expansion_sum_zeroelim(4, u, 4, v, cat);
+
+      Two_Product(cdxtail, adytail, ti1, ti0);
+      Two_Product(adxtail, cdytail, tj1, tj0);
+      Two_Two_Diff(ti1, ti0, tj1, tj0, catt3, catt[2], catt[1], catt[0]);
+      catt[3] = catt3;
+      cattlen = 4;
+    } else {
+      cat[0] = 0.0;
+      catlen = 1;
+      catt[0] = 0.0;
+      cattlen = 1;
+    }
+
+    if (bdxtail != 0.0) {
+      temp16alen = scale_expansion_zeroelim(bxtcalen, bxtca, bdxtail, temp16a);
+      bxtcatlen = scale_expansion_zeroelim(catlen, cat, bdxtail, bxtcat);
+      temp32alen = scale_expansion_zeroelim(bxtcatlen, bxtcat, 2.0 * bdx,
+                                            temp32a);
+      temp48len = fast_expansion_sum_zeroelim(temp16alen, temp16a,
+                                              temp32alen, temp32a, temp48);
+      finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len,
+                                              temp48, finother);
+      finswap = finnow; finnow = finother; finother = finswap;
+      if (cdytail != 0.0) {
+        temp8len = scale_expansion_zeroelim(4, aa, bdxtail, temp8);
+        temp16alen = scale_expansion_zeroelim(temp8len, temp8, cdytail,
+                                              temp16a);
+        finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp16alen,
+                                                temp16a, finother);
+        finswap = finnow; finnow = finother; finother = finswap;
+      }
+      if (adytail != 0.0) {
+        temp8len = scale_expansion_zeroelim(4, cc, -bdxtail, temp8);
+        temp16alen = scale_expansion_zeroelim(temp8len, temp8, adytail,
+                                              temp16a);
+        finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp16alen,
+                                                temp16a, finother);
+        finswap = finnow; finnow = finother; finother = finswap;
+      }
+
+      temp32alen = scale_expansion_zeroelim(bxtcatlen, bxtcat, bdxtail,
+                                            temp32a);
+      bxtcattlen = scale_expansion_zeroelim(cattlen, catt, bdxtail, bxtcatt);
+      temp16alen = scale_expansion_zeroelim(bxtcattlen, bxtcatt, 2.0 * bdx,
+                                            temp16a);
+      temp16blen = scale_expansion_zeroelim(bxtcattlen, bxtcatt, bdxtail,
+                                            temp16b);
+      temp32blen = fast_expansion_sum_zeroelim(temp16alen, temp16a,
+                                              temp16blen, temp16b, temp32b);
+      temp64len = fast_expansion_sum_zeroelim(temp32alen, temp32a,
+                                              temp32blen, temp32b, temp64);
+      finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp64len,
+                                              temp64, finother);
+      finswap = finnow; finnow = finother; finother = finswap;
+    }
+    if (bdytail != 0.0) {
+      temp16alen = scale_expansion_zeroelim(bytcalen, bytca, bdytail, temp16a);
+      bytcatlen = scale_expansion_zeroelim(catlen, cat, bdytail, bytcat);
+      temp32alen = scale_expansion_zeroelim(bytcatlen, bytcat, 2.0 * bdy,
+                                            temp32a);
+      temp48len = fast_expansion_sum_zeroelim(temp16alen, temp16a,
+                                              temp32alen, temp32a, temp48);
+      finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len,
+                                              temp48, finother);
+      finswap = finnow; finnow = finother; finother = finswap;
+
+
+      temp32alen = scale_expansion_zeroelim(bytcatlen, bytcat, bdytail,
+                                            temp32a);
+      bytcattlen = scale_expansion_zeroelim(cattlen, catt, bdytail, bytcatt);
+      temp16alen = scale_expansion_zeroelim(bytcattlen, bytcatt, 2.0 * bdy,
+                                            temp16a);
+      temp16blen = scale_expansion_zeroelim(bytcattlen, bytcatt, bdytail,
+                                            temp16b);
+      temp32blen = fast_expansion_sum_zeroelim(temp16alen, temp16a,
+                                              temp16blen, temp16b, temp32b);
+      temp64len = fast_expansion_sum_zeroelim(temp32alen, temp32a,
+                                              temp32blen, temp32b, temp64);
+      finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp64len,
+                                              temp64, finother);
+      finswap = finnow; finnow = finother; finother = finswap;
+    }
+  }
+  if ((cdxtail != 0.0) || (cdytail != 0.0)) {
+    if ((adxtail != 0.0) || (adytail != 0.0)
+        || (bdxtail != 0.0) || (bdytail != 0.0)) {
+      Two_Product(adxtail, bdy, ti1, ti0);
+      Two_Product(adx, bdytail, tj1, tj0);
+      Two_Two_Sum(ti1, ti0, tj1, tj0, u3, u[2], u[1], u[0]);
+      u[3] = u3;
+      negate = -ady;
+      Two_Product(bdxtail, negate, ti1, ti0);
+      negate = -adytail;
+      Two_Product(bdx, negate, tj1, tj0);
+      Two_Two_Sum(ti1, ti0, tj1, tj0, v3, v[2], v[1], v[0]);
+      v[3] = v3;
+      abtlen = fast_expansion_sum_zeroelim(4, u, 4, v, abt);
+
+      Two_Product(adxtail, bdytail, ti1, ti0);
+      Two_Product(bdxtail, adytail, tj1, tj0);
+      Two_Two_Diff(ti1, ti0, tj1, tj0, abtt3, abtt[2], abtt[1], abtt[0]);
+      abtt[3] = abtt3;
+      abttlen = 4;
+    } else {
+      abt[0] = 0.0;
+      abtlen = 1;
+      abtt[0] = 0.0;
+      abttlen = 1;
+    }
+
+    if (cdxtail != 0.0) {
+      temp16alen = scale_expansion_zeroelim(cxtablen, cxtab, cdxtail, temp16a);
+      cxtabtlen = scale_expansion_zeroelim(abtlen, abt, cdxtail, cxtabt);
+      temp32alen = scale_expansion_zeroelim(cxtabtlen, cxtabt, 2.0 * cdx,
+                                            temp32a);
+      temp48len = fast_expansion_sum_zeroelim(temp16alen, temp16a,
+                                              temp32alen, temp32a, temp48);
+      finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len,
+                                              temp48, finother);
+      finswap = finnow; finnow = finother; finother = finswap;
+      if (adytail != 0.0) {
+        temp8len = scale_expansion_zeroelim(4, bb, cdxtail, temp8);
+        temp16alen = scale_expansion_zeroelim(temp8len, temp8, adytail,
+                                              temp16a);
+        finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp16alen,
+                                                temp16a, finother);
+        finswap = finnow; finnow = finother; finother = finswap;
+      }
+      if (bdytail != 0.0) {
+        temp8len = scale_expansion_zeroelim(4, aa, -cdxtail, temp8);
+        temp16alen = scale_expansion_zeroelim(temp8len, temp8, bdytail,
+                                              temp16a);
+        finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp16alen,
+                                                temp16a, finother);
+        finswap = finnow; finnow = finother; finother = finswap;
+      }
+
+      temp32alen = scale_expansion_zeroelim(cxtabtlen, cxtabt, cdxtail,
+                                            temp32a);
+      cxtabttlen = scale_expansion_zeroelim(abttlen, abtt, cdxtail, cxtabtt);
+      temp16alen = scale_expansion_zeroelim(cxtabttlen, cxtabtt, 2.0 * cdx,
+                                            temp16a);
+      temp16blen = scale_expansion_zeroelim(cxtabttlen, cxtabtt, cdxtail,
+                                            temp16b);
+      temp32blen = fast_expansion_sum_zeroelim(temp16alen, temp16a,
+                                              temp16blen, temp16b, temp32b);
+      temp64len = fast_expansion_sum_zeroelim(temp32alen, temp32a,
+                                              temp32blen, temp32b, temp64);
+      finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp64len,
+                                              temp64, finother);
+      finswap = finnow; finnow = finother; finother = finswap;
+    }
+    if (cdytail != 0.0) {
+      temp16alen = scale_expansion_zeroelim(cytablen, cytab, cdytail, temp16a);
+      cytabtlen = scale_expansion_zeroelim(abtlen, abt, cdytail, cytabt);
+      temp32alen = scale_expansion_zeroelim(cytabtlen, cytabt, 2.0 * cdy,
+                                            temp32a);
+      temp48len = fast_expansion_sum_zeroelim(temp16alen, temp16a,
+                                              temp32alen, temp32a, temp48);
+      finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len,
+                                              temp48, finother);
+      finswap = finnow; finnow = finother; finother = finswap;
+
+
+      temp32alen = scale_expansion_zeroelim(cytabtlen, cytabt, cdytail,
+                                            temp32a);
+      cytabttlen = scale_expansion_zeroelim(abttlen, abtt, cdytail, cytabtt);
+      temp16alen = scale_expansion_zeroelim(cytabttlen, cytabtt, 2.0 * cdy,
+                                            temp16a);
+      temp16blen = scale_expansion_zeroelim(cytabttlen, cytabtt, cdytail,
+                                            temp16b);
+      temp32blen = fast_expansion_sum_zeroelim(temp16alen, temp16a,
+                                              temp16blen, temp16b, temp32b);
+      temp64len = fast_expansion_sum_zeroelim(temp32alen, temp32a,
+                                              temp32blen, temp32b, temp64);
+      finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp64len,
+                                              temp64, finother);
+      finswap = finnow; finnow = finother; finother = finswap;
+    }
+  }
+
+  return finnow[finlength - 1];
+}
+
+REAL incircle(const REAL* __restrict__ pa, const REAL* __restrict__ pb, const REAL* __restrict__ pc, const REAL* __restrict__ pd)
+{
+  REAL adx, bdx, cdx, ady, bdy, cdy;
+  REAL bdxcdy, cdxbdy, cdxady, adxcdy, adxbdy, bdxady;
+  REAL alift, blift, clift;
+  REAL det;
+  REAL permanent, errbound;
+
+  adx = pa[0] - pd[0];
+  bdx = pb[0] - pd[0];
+  cdx = pc[0] - pd[0];
+  ady = pa[1] - pd[1];
+  bdy = pb[1] - pd[1];
+  cdy = pc[1] - pd[1];
+
+  bdxcdy = bdx * cdy;
+  cdxbdy = cdx * bdy;
+  alift = adx * adx + ady * ady;
+
+  cdxady = cdx * ady;
+  adxcdy = adx * cdy;
+  blift = bdx * bdx + bdy * bdy;
+
+  adxbdy = adx * bdy;
+  bdxady = bdx * ady;
+  clift = cdx * cdx + cdy * cdy;
+
+  det = alift * (bdxcdy - cdxbdy)
+      + blift * (cdxady - adxcdy)
+      + clift * (adxbdy - bdxady);
+
+  permanent = (Absolute(bdxcdy) + Absolute(cdxbdy)) * alift
+            + (Absolute(cdxady) + Absolute(adxcdy)) * blift
+            + (Absolute(adxbdy) + Absolute(bdxady)) * clift;
+  errbound = iccerrboundA * permanent;
+  if ((det > errbound) || (-det > errbound)) {
+    return det;
+  }
+
+  return incircleadapt(pa, pb, pc, pd, permanent);
+}
 
 /*****************************************************************************/
 /*                                                                           */
diff --git a/contrib/hxt/predicates.h b/contrib/hxt/predicates.h
index 46c92554dacfeb41fc773986e52c52a3980f9765..35d171f1a57f8499db4e0e3fe3eba11c4253f209 100644
--- a/contrib/hxt/predicates.h
+++ b/contrib/hxt/predicates.h
@@ -31,6 +31,17 @@ double orient3d(
   const double* const __restrict__ pc,
   const double* const __restrict__ pd);
 
+double incircle(
+  const double* const __restrict__ pa,
+  const double* const __restrict__ pb,
+  const double* const __restrict__ pc,
+  const double* const __restrict__ pd);
+
+double orient2d(
+  const double* const __restrict__ pa,
+  const double* const __restrict__ pb,
+  const double* const __restrict__ pc);
+
 int grow_expansion(
   int elen, const double *e, double b, double *h);
 
diff --git a/contrib/hxt/tetgenBR.cxx b/contrib/hxt/tetgenBR.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..e8eecc4aa6854d011de2a585ea5a2f3c37dbb432
--- /dev/null
+++ b/contrib/hxt/tetgenBR.cxx
@@ -0,0 +1,17343 @@
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// TetGen                                                                    //
+//                                                                           //
+// A Quality Tetrahedral Mesh Generator and A 3D Delaunay Triangulator       //
+//                                                                           //
+// Version 1.5                                                               //
+// May 31, 2014                                                              //
+//                                                                           //
+// Copyright (C) 2002--2016                                                  //
+//                                                                           //
+// TetGen is freely available through the website: http://www.tetgen.org.    //
+//   It may be copied, modified, and redistributed for non-commercial use.   //
+//   Please consult the file LICENSE for the detailed copyright notices.     //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+// The following code of this file was automatically generated.  Do not edit!
+// TetGenBR -- The Boundary Recovery code of TetGen.
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// parse_commandline()    Read the command line, identify switches, and set  //
+//                        up options and file names.                         //
+//                                                                           //
+// 'argc' and 'argv' are the same parameters passed to the function main()   //
+// of a C/C++ program. They together represent the command line user invoked //
+// from an environment in which TetGen is running.                           //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+bool tetgenbehavior::parse_commandline(int argc, char **argv)
+{
+  int startindex;
+  int increment;
+  int meshnumber;
+  int i, j, k;
+  char workstring[1024];
+
+  // First determine the input style of the switches.
+  if (argc == 0) {
+    startindex = 0;                    // Switches are given without a dash.
+    argc = 1;                    // For running the following for-loop once.
+    commandline[0] = '\0';
+  } else {
+    startindex = 1;
+    strcpy(commandline, argv[0]);
+    strcat(commandline, " ");
+  }
+
+  for (i = startindex; i < argc; i++) {
+    // Remember the command line for output.
+    strcat(commandline, argv[i]);
+    strcat(commandline, " ");
+    if (startindex == 1) {
+      // Is this string a filename?
+      if (argv[i][0] != '-') {
+        strncpy(infilename, argv[i], 1024 - 1);
+        infilename[1024 - 1] = '\0';
+        continue;                     
+      }
+    }
+    // Parse the individual switch from the string.
+    for (j = startindex; argv[i][j] != '\0'; j++) {
+      if (argv[i][j] == 'p') {
+        plc = 1;
+        if (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) ||
+            (argv[i][j + 1] == '.')) {
+          k = 0;
+          while (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) ||
+                 (argv[i][j + 1] == '.')) {
+            j++;
+            workstring[k] = argv[i][j];
+            k++;
+          }
+          workstring[k] = '\0';
+          facet_separate_ang_tol = (REAL) strtod(workstring, (char **) NULL);
+        }
+		if ((argv[i][j + 1] == '/') || (argv[i][j + 1] == ',')) {
+          j++;
+          if (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) ||
+              (argv[i][j + 1] == '.')) {
+            k = 0;
+            while (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) ||
+                 (argv[i][j + 1] == '.') || (argv[i][j + 1] == 'e') ||
+                 (argv[i][j + 1] == '-') || (argv[i][j + 1] == '+')) {
+              j++;
+              workstring[k] = argv[i][j];
+              k++;
+            }
+            workstring[k] = '\0';
+            facet_overlap_ang_tol = (REAL) strtod(workstring, (char **) NULL);
+          }
+        }
+        if ((argv[i][j + 1] == '/') || (argv[i][j + 1] == ',')) {
+          j++;
+          if (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) ||
+              (argv[i][j + 1] == '.')) {
+            k = 0;
+            while (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) ||
+                   (argv[i][j + 1] == '.')) {
+              j++;
+              workstring[k] = argv[i][j];
+              k++;
+            }
+            workstring[k] = '\0';
+            facet_small_ang_tol = (REAL) strtod(workstring, (char **) NULL);
+          }
+        }
+      } else if (argv[i][j] == 's') {
+        psc = 1;
+      } else if (argv[i][j] == 'Y') {
+        nobisect = 1;
+        if ((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) {
+          nobisect_nomerge = (argv[i][j + 1] - '0');
+          j++;
+        }
+        if ((argv[i][j + 1] == '/') || (argv[i][j + 1] == ',')) {
+          j++;
+          if ((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) {
+            supsteiner_level = (argv[i][j + 1] - '0');
+            j++;
+          }
+        }
+        if ((argv[i][j + 1] == '/') || (argv[i][j + 1] == ',')) {
+          j++;
+          if ((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) {
+            addsteiner_algo = (argv[i][j + 1] - '0');
+            j++;
+          }
+        }
+      } else if (argv[i][j] == 'r') {
+        refine = 1;
+      } else if (argv[i][j] == 'q') {
+        quality = 1;
+        if (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) ||
+            (argv[i][j + 1] == '.')) {
+          k = 0;
+          while (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) ||
+                 (argv[i][j + 1] == '.')) {
+            j++;
+            workstring[k] = argv[i][j];
+            k++;
+          }
+          workstring[k] = '\0';
+          minratio = (REAL) strtod(workstring, (char **) NULL);
+        }
+        if ((argv[i][j + 1] == '/') || (argv[i][j + 1] == ',')) {
+          j++;
+          if (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) ||
+              (argv[i][j + 1] == '.')) {
+            k = 0;
+            while (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) ||
+                   (argv[i][j + 1] == '.')) {
+              j++;
+              workstring[k] = argv[i][j];
+              k++;
+            }
+            workstring[k] = '\0';
+            mindihedral = (REAL) strtod(workstring, (char **) NULL);
+          }
+        }
+      } else if (argv[i][j] == 'R') {
+        coarsen = 1;
+        if ((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) {
+          coarsen_param = (argv[i][j + 1] - '0');
+          j++;
+        }
+        if ((argv[i][j + 1] == '/') || (argv[i][j + 1] == ',')) {
+          j++;
+          if (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) ||
+              (argv[i][j + 1] == '.')) {
+            k = 0;
+            while (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) ||
+                   (argv[i][j + 1] == '.')) {
+              j++;
+              workstring[k] = argv[i][j];
+              k++;
+            }
+            workstring[k] = '\0';
+            coarsen_percent = (REAL) strtod(workstring, (char **) NULL);
+          }
+        }
+      } else if (argv[i][j] == 'w') {
+        weighted = 1;
+        if ((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) {
+          weighted_param = (argv[i][j + 1] - '0');
+          j++;
+        }
+      } else if (argv[i][j] == 'b') {
+        // -b(brio_threshold/brio_ratio/hilbert_limit/hilbert_order)
+        brio_hilbert = 1;
+        if (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) ||
+            (argv[i][j + 1] == '.')) {
+          k = 0;
+          while (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) ||
+                 (argv[i][j + 1] == '.')) {
+            j++;
+            workstring[k] = argv[i][j];
+            k++;
+          }
+          workstring[k] = '\0';
+          brio_threshold = (int) strtol(workstring, (char **) &workstring, 0);
+        }
+        if ((argv[i][j + 1] == '/') || (argv[i][j + 1] == ',')) {
+          j++;
+          if (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) ||
+              (argv[i][j + 1] == '.')) {
+            k = 0;
+            while (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) ||
+                   (argv[i][j + 1] == '.')) {
+              j++;
+              workstring[k] = argv[i][j];
+              k++;
+            }
+            workstring[k] = '\0';
+            brio_ratio = (REAL) strtod(workstring, (char **) NULL);
+          }
+        }
+        if ((argv[i][j + 1] == '/') || (argv[i][j + 1] == ',')) {
+          j++;
+          if (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) ||
+              (argv[i][j + 1] == '.') || (argv[i][j + 1] == '-')) {
+            k = 0;
+            while (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) ||
+                   (argv[i][j + 1] == '.') || (argv[i][j + 1] == '-')) {
+              j++;
+              workstring[k] = argv[i][j];
+              k++;
+            }
+            workstring[k] = '\0';
+            hilbert_limit = (int) strtol(workstring, (char **) &workstring, 0);
+          }
+        }
+        if ((argv[i][j + 1] == '/') || (argv[i][j + 1] == ',')) {
+          j++;
+          if (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) ||
+              (argv[i][j + 1] == '.') || (argv[i][j + 1] == '-')) {
+            k = 0;
+            while (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) ||
+                   (argv[i][j + 1] == '.') || (argv[i][j + 1] == '-')) {
+              j++;
+              workstring[k] = argv[i][j];
+              k++;
+            }
+            workstring[k] = '\0';
+            hilbert_order = (REAL) strtod(workstring, (char **) NULL);
+          }
+        }
+        if (brio_threshold == 0) { // -b0
+          brio_hilbert = 0; // Turn off BRIO-Hilbert sorting. 
+        }
+        if (brio_ratio >= 1.0) { // -b/1
+          no_sort = 1;
+          brio_hilbert = 0; // Turn off BRIO-Hilbert sorting.
+        }
+      } else if (argv[i][j] == 'l') {
+        incrflip = 1;
+      } else if (argv[i][j] == 'L') {
+        flipinsert = 1;
+      } else if (argv[i][j] == 'm') {
+        metric = 1;
+      } else if (argv[i][j] == 'a') {
+        if (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) ||
+            (argv[i][j + 1] == '.')) {
+          fixedvolume = 1;
+          k = 0;
+          while (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) ||
+                 (argv[i][j + 1] == '.') || (argv[i][j + 1] == 'e') ||
+                 (argv[i][j + 1] == '-') || (argv[i][j + 1] == '+')) {
+            j++;
+            workstring[k] = argv[i][j];
+            k++;
+          }
+          workstring[k] = '\0';
+          maxvolume = (REAL) strtod(workstring, (char **) NULL);
+        } else {
+          varvolume = 1;
+        }
+      } else if (argv[i][j] == 'A') {
+        regionattrib = 1;
+      } else if (argv[i][j] == 'D') {
+        cdtrefine = 1;
+        if ((argv[i][j + 1] >= '1') && (argv[i][j + 1] <= '3')) {
+          reflevel = (argv[i][j + 1] - '1') + 1; 
+          j++;
+        }
+      } else if (argv[i][j] == 'i') {
+        insertaddpoints = 1;
+      } else if (argv[i][j] == 'd') {
+        diagnose = 1;
+      } else if (argv[i][j] == 'c') {
+        convex = 1;
+      } else if (argv[i][j] == 'M') {
+        nomergefacet = 1;
+        nomergevertex = 1;
+        if ((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '1')) {
+          nomergefacet = (argv[i][j + 1] - '0');
+          j++;
+        }
+        if ((argv[i][j + 1] == '/') || (argv[i][j + 1] == ',')) {
+          j++;
+          if ((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '1')) {
+            nomergevertex = (argv[i][j + 1] - '0');
+            j++;
+          }
+        }
+      } else if (argv[i][j] == 'X') {
+        if (argv[i][j + 1] == '1') {
+          nostaticfilter = 1;
+          j++;
+        } else {
+          noexact = 1;
+        }
+      } else if (argv[i][j] == 'z') {
+        if (argv[i][j + 1] == '1') {  // -z1
+          reversetetori = 1;
+          j++;
+        } else {
+          zeroindex = 1; // -z
+        }
+      } else if (argv[i][j] == 'f') {
+        facesout++;
+      } else if (argv[i][j] == 'e') {
+        edgesout++;
+      } else if (argv[i][j] == 'n') {
+        neighout++;
+      } else if (argv[i][j] == 'v') {
+        voroout = 1;
+      } else if (argv[i][j] == 'g') {
+        meditview = 1;
+      } else if (argv[i][j] == 'k') {
+        vtkview = 1;  
+      } else if (argv[i][j] == 'J') {
+        nojettison = 1;
+      } else if (argv[i][j] == 'B') {
+        nobound = 1;
+      } else if (argv[i][j] == 'N') {
+        nonodewritten = 1;
+      } else if (argv[i][j] == 'E') {
+        noelewritten = 1;
+      } else if (argv[i][j] == 'F') {
+        nofacewritten = 1;
+      } else if (argv[i][j] == 'I') {
+        noiterationnum = 1;
+      } else if (argv[i][j] == 'S') {
+        if (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) ||
+            (argv[i][j + 1] == '.')) {
+          k = 0;
+          while (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) ||
+                 (argv[i][j + 1] == '.') || (argv[i][j + 1] == 'e') ||
+                 (argv[i][j + 1] == '-') || (argv[i][j + 1] == '+')) {
+            j++;
+            workstring[k] = argv[i][j];
+            k++;
+          }
+          workstring[k] = '\0';
+          steinerleft = (int) strtol(workstring, (char **) NULL, 0);
+        }
+      } else if (argv[i][j] == 'o') {
+        if (argv[i][j + 1] == '2') {
+          order = 2;
+          j++;
+        }
+        if ((argv[i][j + 1] == '/') || (argv[i][j + 1] == ',')) {
+          j++;
+          if (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) ||
+              (argv[i][j + 1] == '.')) {
+            k = 0;
+            while (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) ||
+                   (argv[i][j + 1] == '.')) {
+              j++;
+              workstring[k] = argv[i][j];
+              k++;
+            }
+            workstring[k] = '\0';
+            optmaxdihedral = (REAL) strtod(workstring, (char **) NULL);
+          }
+        }
+      } else if (argv[i][j] == 'O') {
+        if ((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) {
+          optlevel = (argv[i][j + 1] - '0');
+          j++;
+        }
+        if ((argv[i][j + 1] == '/') || (argv[i][j + 1] == ',')) {
+          j++;
+          if ((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '7')) {
+            optscheme = (argv[i][j + 1] - '0');
+            j++;
+          }
+        }
+      } else if (argv[i][j] == 'T') {
+        if (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) ||
+            (argv[i][j + 1] == '.')) {
+          k = 0;
+          while (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) ||
+                 (argv[i][j + 1] == '.') || (argv[i][j + 1] == 'e') ||
+                 (argv[i][j + 1] == '-') || (argv[i][j + 1] == '+')) {
+            j++;
+            workstring[k] = argv[i][j];
+            k++;
+          }
+          workstring[k] = '\0';
+          epsilon = (REAL) strtod(workstring, (char **) NULL);
+        }
+      } else if (argv[i][j] == 'C') {
+        docheck++;
+      } else if (argv[i][j] == 'Q') {
+        quiet = 1;
+      } else if (argv[i][j] == 'V') {
+        verbose++;
+      } else if (argv[i][j] == 'x') {
+        if (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) ||
+            (argv[i][j + 1] == '.')) {
+          k = 0;
+          while (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) ||
+                 (argv[i][j + 1] == '.') || (argv[i][j + 1] == 'e') ||
+                 (argv[i][j + 1] == '-') || (argv[i][j + 1] == '+')) {
+            j++;
+            workstring[k] = argv[i][j];
+            k++;
+          }
+          workstring[k] = '\0';
+          tetrahedraperblock = (int) strtol(workstring, (char **) NULL, 0);
+          if (tetrahedraperblock > 8188) {
+            vertexperblock = tetrahedraperblock / 2;
+            shellfaceperblock = vertexperblock / 2;
+          } else {
+            tetrahedraperblock = 8188;
+          }
+        }
+      } else if ((argv[i][j] == 'h') || (argv[i][j] == 'H') ||
+                 (argv[i][j] == '?')) {
+      } else {
+        printf("Warning:  Unknown switch -%c.\n", argv[i][j]);
+      }
+    }
+  }
+
+  if (startindex == 0) {
+    // Set a temporary filename for debugging output.
+    strcpy(infilename, "tetgen-tmpfile");
+  } else {
+    if (infilename[0] == '\0') {
+    }
+    // Recognize the object from file extension if it is available.
+    if (!strcmp(&infilename[strlen(infilename) - 5], ".node")) {
+      infilename[strlen(infilename) - 5] = '\0';
+      object = NODES;
+    } else if (!strcmp(&infilename[strlen(infilename) - 5], ".poly")) {
+      infilename[strlen(infilename) - 5] = '\0';
+      object = POLY;
+      plc = 1;
+    } else if (!strcmp(&infilename[strlen(infilename) - 6], ".smesh")) {
+      infilename[strlen(infilename) - 6] = '\0';
+      object = POLY;
+      plc = 1;
+    } else if (!strcmp(&infilename[strlen(infilename) - 4], ".off")) {
+      infilename[strlen(infilename) - 4] = '\0';
+      object = OFF;
+      plc = 1;
+    } else if (!strcmp(&infilename[strlen(infilename) - 4], ".ply")) {
+      infilename[strlen(infilename) - 4] = '\0';
+      object = PLY;
+      plc = 1;
+    } else if (!strcmp(&infilename[strlen(infilename) - 4], ".stl")) {
+      infilename[strlen(infilename) - 4] = '\0';
+      object = STL;
+      plc = 1;
+    } else if (!strcmp(&infilename[strlen(infilename) - 5], ".mesh")) {
+      infilename[strlen(infilename) - 5] = '\0';
+      object = MEDIT;
+      if (!refine) plc = 1;
+    } else if (!strcmp(&infilename[strlen(infilename) - 4], ".vtk")) {
+      infilename[strlen(infilename) - 4] = '\0';
+      object = VTK;
+      plc = 1;
+    } else if (!strcmp(&infilename[strlen(infilename) - 4], ".ele")) {
+      infilename[strlen(infilename) - 4] = '\0';
+      object = MESH;
+      refine = 1;
+    }
+  }
+
+  if (nobisect && (!plc && !refine)) { // -Y
+    plc = 1; // Default -p option.
+  }
+  if (quality && (!plc && !refine)) { // -q
+    plc = 1; // Default -p option.
+  }
+  if (diagnose && !plc) { // -d
+    plc = 1;
+  }
+  if (refine && !quality) { // -r only
+    // Reconstruct a mesh, no mesh optimization.
+    optlevel = 0;
+  }
+  if (insertaddpoints && (optlevel == 0)) { // with -i option
+    optlevel = 2;
+  }
+  if (coarsen && (optlevel == 0)) { // with -R option
+    optlevel = 2;
+  }
+
+  // Detect improper combinations of switches.
+  if ((refine || plc) && weighted) {
+    printf("Error:  Switches -w cannot use together with -p or -r.\n");
+    return false;
+  }
+
+  if (convex) { // -c
+    if (plc && !regionattrib) {
+      // -A (region attribute) is needed for marking exterior tets (-1).
+      regionattrib = 1; 
+    }
+  }
+
+  // Note: -A must not used together with -r option. 
+  // Be careful not to add an extra attribute to each element unless the
+  //   input supports it (PLC in, but not refining a preexisting mesh).
+  if (refine || !plc) {
+    regionattrib = 0;
+  }
+  // Be careful not to allocate space for element area constraints that 
+  //   will never be assigned any value (other than the default -1.0).
+  if (!refine && !plc) {
+    varvolume = 0;
+  }
+  // If '-a' or '-aa' is in use, enable '-q' option too.
+  if (fixedvolume || varvolume) {
+    if (quality == 0) {
+      quality = 1;
+      if (!plc && !refine) {
+        plc = 1; // enable -p.
+      }
+    }
+  }
+  // No user-specified dihedral angle bound. Use default ones.
+  if (!quality) {
+    if (optmaxdihedral < 179.0) {
+      if (nobisect) {  // with -Y option
+        optmaxdihedral = 179.0;
+      } else { // -p only
+        optmaxdihedral = 179.999;
+      }
+    }
+    if (optminsmtdihed < 179.999) {
+      optminsmtdihed = 179.999;
+    }
+    if (optminslidihed < 179.999) {
+      optminslidihed = 179.999;
+    }
+  }
+
+  increment = 0;
+  strcpy(workstring, infilename);
+  j = 1;
+  while (workstring[j] != '\0') {
+    if ((workstring[j] == '.') && (workstring[j + 1] != '\0')) {
+      increment = j + 1;
+    }
+    j++;
+  }
+  meshnumber = 0;
+  if (increment > 0) {
+    j = increment;
+    do {
+      if ((workstring[j] >= '0') && (workstring[j] <= '9')) {
+        meshnumber = meshnumber * 10 + (int) (workstring[j] - '0');
+      } else {
+        increment = 0;
+      }
+      j++;
+    } while (workstring[j] != '\0');
+  }
+  if (noiterationnum) {
+    strcpy(outfilename, infilename);
+  } else if (increment == 0) {
+    strcpy(outfilename, infilename);
+    strcat(outfilename, ".1");
+  } else {
+    workstring[increment] = '%';
+    workstring[increment + 1] = 'd';
+    workstring[increment + 2] = '\0';
+    sprintf(outfilename, workstring, meshnumber + 1);
+  }
+  // Additional input file name has the end ".a".
+  strcpy(addinfilename, infilename);
+  strcat(addinfilename, ".a");
+  // Background filename has the form "*.b.ele", "*.b.node", ...
+  strcpy(bgmeshfilename, infilename);
+  strcat(bgmeshfilename, ".b");
+
+  return true;
+}
+
+////                                                                       ////
+////                                                                       ////
+//// behavior_cxx /////////////////////////////////////////////////////////////
+
+//// mempool_cxx //////////////////////////////////////////////////////////////
+////                                                                       ////
+////                                                                       ////
+
+// Initialize fast lookup tables for mesh maniplulation primitives.
+
+int tetgenmesh::bondtbl[12][12] = {{0,},};
+int tetgenmesh::enexttbl[12] = {0,};
+int tetgenmesh::eprevtbl[12] = {0,};
+int tetgenmesh::enextesymtbl[12] = {0,};
+int tetgenmesh::eprevesymtbl[12] = {0,};
+int tetgenmesh::eorgoppotbl[12] = {0,};
+int tetgenmesh::edestoppotbl[12] = {0,};
+int tetgenmesh::fsymtbl[12][12] = {{0,},};
+int tetgenmesh::facepivot1[12] = {0,};
+int tetgenmesh::facepivot2[12][12] = {{0,},};
+int tetgenmesh::tsbondtbl[12][6] = {{0,},};
+int tetgenmesh::stbondtbl[12][6] = {{0,},};
+int tetgenmesh::tspivottbl[12][6] = {{0,},};
+int tetgenmesh::stpivottbl[12][6] = {{0,},};
+
+// Table 'esymtbl' takes an directed edge (version) as input, returns the
+//   inversed edge (version) of it.
+
+int tetgenmesh::esymtbl[12] = {9, 6, 11, 4, 3, 7, 1, 5, 10, 0, 8, 2};
+
+// The following four tables give the 12 permutations of the set {0,1,2,3}.
+//   An offset 4 is added to each element for a direct access of the points
+//   in the tetrahedron data structure.
+
+int tetgenmesh:: orgpivot[12] = {7, 7, 5, 5, 6, 4, 4, 6, 5, 6, 7, 4};
+int tetgenmesh::destpivot[12] = {6, 4, 4, 6, 5, 6, 7, 4, 7, 7, 5, 5};
+int tetgenmesh::apexpivot[12] = {5, 6, 7, 4, 7, 7, 5, 5, 6, 4, 4, 6};
+int tetgenmesh::oppopivot[12] = {4, 5, 6, 7, 4, 5, 6, 7, 4, 5, 6, 7};
+
+// The twelve versions correspond to six undirected edges. The following two
+//   tables map a version to an undirected edge and vice versa.
+
+int tetgenmesh::ver2edge[12] = {0, 1, 2, 3, 3, 5, 1, 5, 4, 0, 4, 2};
+int tetgenmesh::edge2ver[ 6] = {0, 1, 2, 3, 8, 5};
+
+// Edge versions whose apex or opposite may be dummypoint.
+
+int tetgenmesh::epivot[12] = {4, 5, 2, 11, 4, 5, 2, 11, 4, 5, 2, 11};
+
+
+// Table 'snextpivot' takes an edge version as input, returns the next edge
+//   version in the same edge ring.
+
+int tetgenmesh::snextpivot[6] = {2, 5, 4, 1, 0, 3};
+
+// The following three tables give the 6 permutations of the set {0,1,2}.
+//   An offset 3 is added to each element for a direct access of the points
+//   in the triangle data structure.
+
+int tetgenmesh::sorgpivot [6] = {3, 4, 4, 5, 5, 3};
+int tetgenmesh::sdestpivot[6] = {4, 3, 5, 4, 3, 5};
+int tetgenmesh::sapexpivot[6] = {5, 5, 3, 3, 4, 4};
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// inittable()    Initialize the look-up tables.                             //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::inittables()
+{
+  int soffset, toffset;
+  int i, j;
+
+
+  // i = t1.ver; j = t2.ver;
+  for (i = 0; i < 12; i++) {
+    for (j = 0; j < 12; j++) {
+      bondtbl[i][j] = (j & 3) + (((i & 12) + (j & 12)) % 12);
+    }
+  }
+
+
+  // i = t1.ver; j = t2.ver
+  for (i = 0; i < 12; i++) {
+    for (j = 0; j < 12; j++) {
+      fsymtbl[i][j] = (j + 12 - (i & 12)) % 12;
+    }
+  }
+
+
+  for (i = 0; i < 12; i++) {
+    facepivot1[i] = (esymtbl[i] & 3);
+  }
+
+  for (i = 0; i < 12; i++) {
+    for (j = 0; j < 12; j++) {
+      facepivot2[i][j] = fsymtbl[esymtbl[i]][j];
+    }
+  }
+
+  for (i = 0; i < 12; i++) {
+    enexttbl[i] = (i + 4) % 12;
+    eprevtbl[i] = (i + 8) % 12;
+  }
+
+  for (i = 0; i < 12; i++) {
+    enextesymtbl[i] = esymtbl[enexttbl[i]];
+    eprevesymtbl[i] = esymtbl[eprevtbl[i]];
+  }
+
+  for (i = 0; i < 12; i++) {
+    eorgoppotbl [i] = eprevtbl[esymtbl[enexttbl[i]]];
+    edestoppotbl[i] = enexttbl[esymtbl[eprevtbl[i]]];
+  }
+
+  // i = t.ver, j = s.shver
+  for (i = 0; i < 12; i++) {
+    for (j = 0; j < 6; j++) {
+      if ((j & 1) == 0) {
+        soffset = (6 - ((i & 12) >> 1)) % 6;
+        toffset = (12 - ((j & 6) << 1)) % 12;
+      } else {
+        soffset = (i & 12) >> 1;
+        toffset = (j & 6) << 1;
+      }
+      tsbondtbl[i][j] = (j & 1) + (((j & 6) + soffset) % 6);
+      stbondtbl[i][j] = (i & 3) + (((i & 12) + toffset) % 12);
+    }
+  }
+
+
+  // i = t.ver, j = s.shver
+  for (i = 0; i < 12; i++) {
+    for (j = 0; j < 6; j++) {
+      if ((j & 1) == 0) {
+        soffset = (i & 12) >> 1;
+        toffset = (j & 6) << 1;
+      } else {
+        soffset = (6 - ((i & 12) >> 1)) % 6;
+        toffset = (12 - ((j & 6) << 1)) % 12;
+      }
+      tspivottbl[i][j] = (j & 1) + (((j & 6) + soffset) % 6);
+      stpivottbl[i][j] = (i & 3) + (((i & 12) + toffset) % 12);
+    }
+  }
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// restart()    Deallocate all objects in this pool.                         //
+//                                                                           //
+// The pool returns to a fresh state, like after it was initialized, except  //
+// that no memory is freed to the operating system.  Rather, the previously  //
+// allocated blocks are ready to be used.                                    //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::arraypool::restart()
+{
+  objects = 0l;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// poolinit()    Initialize an arraypool for allocation of objects.          //
+//                                                                           //
+// Before the pool may be used, it must be initialized by this procedure.    //
+// After initialization, memory can be allocated and freed in this pool.     //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::arraypool::poolinit(int sizeofobject, int log2objperblk)
+{
+  // Each object must be at least one byte long.
+  objectbytes = sizeofobject > 1 ? sizeofobject : 1;
+
+  log2objectsperblock = log2objperblk;
+  // Compute the number of objects in each block.
+  objectsperblock = ((int) 1) << log2objectsperblock;
+  objectsperblockmark = objectsperblock - 1;
+
+  // No memory has been allocated.
+  totalmemory = 0l;
+  // The top array has not been allocated yet.
+  toparray = (char **) NULL;
+  toparraylen = 0;
+
+  // Ready all indices to be allocated.
+  restart();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// arraypool()    The constructor and destructor.                            //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+tetgenmesh::arraypool::arraypool(int sizeofobject, int log2objperblk)
+{
+  poolinit(sizeofobject, log2objperblk);
+}
+
+tetgenmesh::arraypool::~arraypool()
+{
+  int i;
+
+  // Has anything been allocated at all?
+  if (toparray != (char **) NULL) {
+    // Walk through the top array.
+    for (i = 0; i < toparraylen; i++) {
+      // Check every pointer; NULLs may be scattered randomly.
+      if (toparray[i] != (char *) NULL) {
+        // Free an allocated block.
+        free((void *) toparray[i]);
+      }
+    }
+    // Free the top array.
+    free((void *) toparray);
+  }
+
+  // The top array is no longer allocated.
+  toparray = (char **) NULL;
+  toparraylen = 0;
+  objects = 0;
+  totalmemory = 0;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// getblock()    Return (and perhaps create) the block containing the object //
+//               with a given index.                                         //
+//                                                                           //
+// This function takes care of allocating or resizing the top array if nece- //
+// ssary, and of allocating the block if it hasn't yet been allocated.       //
+//                                                                           //
+// Return a pointer to the beginning of the block (NOT the object).          //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+char* tetgenmesh::arraypool::getblock(int objectindex)
+{
+  char **newarray;
+  char *block;
+  int newsize;
+  int topindex;
+  int i;
+
+  // Compute the index in the top array (upper bits).
+  topindex = objectindex >> log2objectsperblock;
+  // Does the top array need to be allocated or resized?
+  if (toparray == (char **) NULL) {
+    // Allocate the top array big enough to hold 'topindex', and NULL out
+    //   its contents.
+    newsize = topindex + 128;
+    toparray = (char **) malloc((size_t) (newsize * sizeof(char *)));
+    toparraylen = newsize;
+    for (i = 0; i < newsize; i++) {
+      toparray[i] = (char *) NULL;
+    }
+    // Account for the memory.
+    totalmemory = newsize * (uintptr_t) sizeof(char *);
+  } else if (topindex >= toparraylen) {
+    // Resize the top array, making sure it holds 'topindex'.
+    newsize = 3 * toparraylen;
+    if (topindex >= newsize) {
+      newsize = topindex + 128;
+    }
+    // Allocate the new array, copy the contents, NULL out the rest, and
+    //   free the old array.
+    newarray = (char **) malloc((size_t) (newsize * sizeof(char *)));
+    for (i = 0; i < toparraylen; i++) {
+      newarray[i] = toparray[i];
+    }
+    for (i = toparraylen; i < newsize; i++) {
+      newarray[i] = (char *) NULL;
+    }
+    free(toparray);
+    // Account for the memory.
+    totalmemory += (newsize - toparraylen) * sizeof(char *);
+    toparray = newarray;
+    toparraylen = newsize;
+  }
+
+  // Find the block, or learn that it hasn't been allocated yet.
+  block = toparray[topindex];
+  if (block == (char *) NULL) {
+    // Allocate a block at this index.
+    block = (char *) malloc((size_t) (objectsperblock * objectbytes));
+    toparray[topindex] = block;
+    // Account for the memory.
+    totalmemory += objectsperblock * objectbytes;
+  }
+
+  // Return a pointer to the block.
+  return block;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// lookup()    Return the pointer to the object with a given index, or NULL  //
+//             if the object's block doesn't exist yet.                      //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void* tetgenmesh::arraypool::lookup(int objectindex)
+{
+  char *block;
+  int topindex;
+
+  // Has the top array been allocated yet?
+  if (toparray == (char **) NULL) {
+    return (void *) NULL;
+  }
+
+  // Compute the index in the top array (upper bits).
+  topindex = objectindex >> log2objectsperblock;
+  // Does the top index fit in the top array?
+  if (topindex >= toparraylen) {
+    return (void *) NULL;
+  }
+
+  // Find the block, or learn that it hasn't been allocated yet.
+  block = toparray[topindex];
+  if (block == (char *) NULL) {
+    return (void *) NULL;
+  }
+
+  // Compute a pointer to the object with the given index.  Note that
+  //   'objectsperblock' is a power of two, so the & operation is a bit mask
+  //   that preserves the lower bits.
+  return (void *)(block + (objectindex & (objectsperblock - 1)) * objectbytes);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// newindex()    Allocate space for a fresh object from the pool.            //
+//                                                                           //
+// 'newptr' returns a pointer to the new object (it must not be a NULL).     //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+int tetgenmesh::arraypool::newindex(void **newptr)
+{
+  // Allocate an object at index 'firstvirgin'.
+  int newindex = objects;
+  *newptr = (void *) (getblock(objects) +
+    (objects & (objectsperblock - 1)) * objectbytes);
+  objects++;
+
+  return newindex;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// memorypool()   The constructors of memorypool.                            //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+tetgenmesh::memorypool::memorypool()
+{
+  firstblock = nowblock = (void **) NULL;
+  nextitem = (void *) NULL;
+  deaditemstack = (void *) NULL;
+  pathblock = (void **) NULL;
+  pathitem = (void *) NULL;
+  alignbytes = 0;
+  itembytes = itemwords = 0;
+  itemsperblock = 0;
+  items = maxitems = 0l;
+  unallocateditems = 0;
+  pathitemsleft = 0;
+}
+
+tetgenmesh::memorypool::memorypool(int bytecount, int itemcount, int wsize, 
+                                   int alignment)
+{
+  poolinit(bytecount, itemcount, wsize, alignment);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// ~memorypool()   Free to the operating system all memory taken by a pool.  //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+tetgenmesh::memorypool::~memorypool()
+{
+  while (firstblock != (void **) NULL) {
+    nowblock = (void **) *(firstblock);
+    free(firstblock);
+    firstblock = nowblock;
+  }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// poolinit()    Initialize a pool of memory for allocation of items.        //
+//                                                                           //
+// A `pool' is created whose records have size at least `bytecount'.  Items  //
+// will be allocated in `itemcount'-item blocks.  Each item is assumed to be //
+// a collection of words, and either pointers or floating-point values are   //
+// assumed to be the "primary" word type.  (The "primary" word type is used  //
+// to determine alignment of items.)  If `alignment' isn't zero, all items   //
+// will be `alignment'-byte aligned in memory.  `alignment' must be either a //
+// multiple or a factor of the primary word size;  powers of two are safe.   //
+// `alignment' is normally used to create a few unused bits at the bottom of //
+// each item's pointer, in which information may be stored.                  //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::memorypool::poolinit(int bytecount,int itemcount,int wordsize,
+                                      int alignment)
+{
+  // Find the proper alignment, which must be at least as large as:
+  //   - The parameter `alignment'.
+  //   - The primary word type, to avoid unaligned accesses.
+  //   - sizeof(void *), so the stack of dead items can be maintained
+  //       without unaligned accesses.
+  if (alignment > wordsize) {
+    alignbytes = alignment;
+  } else {
+    alignbytes = wordsize;
+  }
+  if ((int) sizeof(void *) > alignbytes) {
+    alignbytes = (int) sizeof(void *);
+  }
+  itemwords = ((bytecount + alignbytes - 1) /  alignbytes)
+            * (alignbytes / wordsize);
+  itembytes = itemwords * wordsize;
+  itemsperblock = itemcount;
+
+  // Allocate a block of items.  Space for `itemsperblock' items and one
+  //   pointer (to point to the next block) are allocated, as well as space
+  //   to ensure alignment of the items. 
+  firstblock = (void **) malloc(itemsperblock * itembytes + sizeof(void *)
+                                + alignbytes); 
+  if (firstblock == (void **) NULL) {
+    terminatetetgen(NULL, 1);
+  }
+  // Set the next block pointer to NULL.
+  *(firstblock) = (void *) NULL;
+  restart();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// restart()   Deallocate all items in this pool.                            //
+//                                                                           //
+// The pool is returned to its starting state, except that no memory is      //
+// freed to the operating system.  Rather, the previously allocated blocks   //
+// are ready to be reused.                                                   //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::memorypool::restart()
+{
+  uintptr_t alignptr;
+
+  items = 0;
+  maxitems = 0;
+
+  // Set the currently active block.
+  nowblock = firstblock;
+  // Find the first item in the pool.  Increment by the size of (void *).
+  alignptr = (uintptr_t) (nowblock + 1);
+  // Align the item on an `alignbytes'-byte boundary.
+  nextitem = (void *)
+    (alignptr + (uintptr_t) alignbytes -
+     (alignptr % (uintptr_t) alignbytes));
+  // There are lots of unallocated items left in this block.
+  unallocateditems = itemsperblock;
+  // The stack of deallocated items is empty.
+  deaditemstack = (void *) NULL;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// alloc()   Allocate space for an item.                                     //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void* tetgenmesh::memorypool::alloc()
+{
+  void *newitem;
+  void **newblock;
+  uintptr_t alignptr;
+
+  // First check the linked list of dead items.  If the list is not 
+  //   empty, allocate an item from the list rather than a fresh one.
+  if (deaditemstack != (void *) NULL) {
+    newitem = deaditemstack;                     // Take first item in list.
+    deaditemstack = * (void **) deaditemstack;
+  } else {
+    // Check if there are any free items left in the current block.
+    if (unallocateditems == 0) {
+      // Check if another block must be allocated.
+      if (*nowblock == (void *) NULL) {
+        // Allocate a new block of items, pointed to by the previous block.
+        newblock = (void **) malloc(itemsperblock * itembytes + sizeof(void *) 
+                                    + alignbytes);
+        if (newblock == (void **) NULL) {
+          terminatetetgen(NULL, 1);
+        }
+        *nowblock = (void *) newblock;
+        // The next block pointer is NULL.
+        *newblock = (void *) NULL;
+      }
+      // Move to the new block.
+      nowblock = (void **) *nowblock;
+      // Find the first item in the block.
+      //   Increment by the size of (void *).
+      alignptr = (uintptr_t) (nowblock + 1);
+      // Align the item on an `alignbytes'-byte boundary.
+      nextitem = (void *)
+        (alignptr + (uintptr_t) alignbytes -
+         (alignptr % (uintptr_t) alignbytes));
+      // There are lots of unallocated items left in this block.
+      unallocateditems = itemsperblock;
+    }
+    // Allocate a new item.
+    newitem = nextitem;
+    // Advance `nextitem' pointer to next free item in block.
+    nextitem = (void *) ((uintptr_t) nextitem + itembytes);
+    unallocateditems--;
+    maxitems++;
+  }
+  items++;
+  return newitem;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// dealloc()   Deallocate space for an item.                                 //
+//                                                                           //
+// The deallocated space is stored in a queue for later reuse.               //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::memorypool::dealloc(void *dyingitem)
+{
+  // Push freshly killed item onto stack.
+  *((void **) dyingitem) = deaditemstack;
+  deaditemstack = dyingitem;
+  items--;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// traversalinit()   Prepare to traverse the entire list of items.           //
+//                                                                           //
+// This routine is used in conjunction with traverse().                      //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::memorypool::traversalinit()
+{
+  uintptr_t alignptr;
+
+  // Begin the traversal in the first block.
+  pathblock = firstblock;
+  // Find the first item in the block.  Increment by the size of (void *).
+  alignptr = (uintptr_t) (pathblock + 1);
+  // Align with item on an `alignbytes'-byte boundary.
+  pathitem = (void *)
+    (alignptr + (uintptr_t) alignbytes -
+     (alignptr % (uintptr_t) alignbytes));
+  // Set the number of items left in the current block.
+  pathitemsleft = itemsperblock;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// traverse()   Find the next item in the list.                              //
+//                                                                           //
+// This routine is used in conjunction with traversalinit().  Be forewarned  //
+// that this routine successively returns all items in the list, including   //
+// deallocated ones on the deaditemqueue. It's up to you to figure out which //
+// ones are actually dead.  It can usually be done more space-efficiently by //
+// a routine that knows something about the structure of the item.           //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void* tetgenmesh::memorypool::traverse()
+{
+  void *newitem;
+  uintptr_t alignptr;
+
+  // Stop upon exhausting the list of items.
+  if (pathitem == nextitem) {
+    return (void *) NULL;
+  }
+  // Check whether any untraversed items remain in the current block.
+  if (pathitemsleft == 0) {
+    // Find the next block.
+    pathblock = (void **) *pathblock;
+    // Find the first item in the block.  Increment by the size of (void *).
+    alignptr = (uintptr_t) (pathblock + 1);
+    // Align with item on an `alignbytes'-byte boundary.
+    pathitem = (void *)
+      (alignptr + (uintptr_t) alignbytes -
+       (alignptr % (uintptr_t) alignbytes));
+    // Set the number of items left in the current block.
+    pathitemsleft = itemsperblock;
+  }
+  newitem = pathitem;
+  // Find the next item in the block.
+  pathitem = (void *) ((uintptr_t) pathitem + itembytes);
+  pathitemsleft--;
+  return newitem;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// makeindex2pointmap()    Create a map from index to vertices.              //
+//                                                                           //
+// 'idx2verlist' returns the created map.  Traverse all vertices, a pointer  //
+// to each vertex is set into the array.  The pointer to the first vertex is //
+// saved in 'idx2verlist[in->firstnumber]'.                                  //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::makeindex2pointmap(point*& idx2verlist)
+{
+  point pointloop;
+  int idx;
+
+  if (b->verbose > 1) {
+    printf("  Constructing mapping from indices to points.\n");
+  }
+
+  idx2verlist = new point[points->items + 1];
+
+  points->traversalinit();
+  pointloop = pointtraverse();
+  idx =  in->firstnumber;
+  while (pointloop != (point) NULL) {
+    idx2verlist[idx++] = pointloop;
+    pointloop = pointtraverse();
+  }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// makesubfacemap()    Create a map from vertex to subfaces incident at it.  //
+//                                                                           //
+// The map is returned in two arrays 'idx2faclist' and 'facperverlist'.  All //
+// subfaces incident at i-th vertex (i is counted from 0) are found in the   //
+// array facperverlist[j], where idx2faclist[i] <= j < idx2faclist[i + 1].   //
+// Each entry in facperverlist[j] is a subface whose origin is the vertex.   //
+//                                                                           //
+// NOTE: These two arrays will be created inside this routine, don't forget  //
+// to free them after using.                                                 //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::makepoint2submap(memorypool* pool, int*& idx2faclist,
+                                  face*& facperverlist)
+{
+  face shloop;
+  int i, j, k;
+
+  if (b->verbose > 1) {
+    printf("  Making a map from points to subfaces.\n");
+  }
+
+  // Initialize 'idx2faclist'.
+  idx2faclist = new int[points->items + 1];
+  for (i = 0; i < points->items + 1; i++) idx2faclist[i] = 0;
+
+  // Loop all subfaces, counter the number of subfaces incident at a vertex.
+  pool->traversalinit();
+  shloop.sh = shellfacetraverse(pool);
+  while (shloop.sh != (shellface *) NULL) {
+    // Increment the number of incident subfaces for each vertex.
+    j = pointmark((point) shloop.sh[3]) - in->firstnumber;
+    idx2faclist[j]++;
+    j = pointmark((point) shloop.sh[4]) - in->firstnumber;
+    idx2faclist[j]++;
+    // Skip the third corner if it is a segment.
+    if (shloop.sh[5] != NULL) {
+      j = pointmark((point) shloop.sh[5]) - in->firstnumber;
+      idx2faclist[j]++;
+    }
+    shloop.sh = shellfacetraverse(pool);
+  }
+
+  // Calculate the total length of array 'facperverlist'.
+  j = idx2faclist[0];
+  idx2faclist[0] = 0;  // Array starts from 0 element.
+  for (i = 0; i < points->items; i++) {
+    k = idx2faclist[i + 1];
+    idx2faclist[i + 1] = idx2faclist[i] + j;
+    j = k;
+  }
+
+  // The total length is in the last unit of idx2faclist.
+  facperverlist = new face[idx2faclist[i]];
+
+  // Loop all subfaces again, remember the subfaces at each vertex.
+  pool->traversalinit();
+  shloop.sh = shellfacetraverse(pool);
+  while (shloop.sh != (shellface *) NULL) {
+    j = pointmark((point) shloop.sh[3]) - in->firstnumber;
+    shloop.shver = 0; // save the origin.
+    facperverlist[idx2faclist[j]] = shloop;
+    idx2faclist[j]++;
+    // Is it a subface or a subsegment?
+    if (shloop.sh[5] != NULL) {
+      j = pointmark((point) shloop.sh[4]) - in->firstnumber;
+      shloop.shver = 2; // save the origin.
+      facperverlist[idx2faclist[j]] = shloop;
+      idx2faclist[j]++;
+      j = pointmark((point) shloop.sh[5]) - in->firstnumber;
+      shloop.shver = 4; // save the origin.
+      facperverlist[idx2faclist[j]] = shloop;
+      idx2faclist[j]++;
+    } else {
+      j = pointmark((point) shloop.sh[4]) - in->firstnumber;
+      shloop.shver = 1; // save the origin.
+      facperverlist[idx2faclist[j]] = shloop;
+      idx2faclist[j]++;
+    }
+    shloop.sh = shellfacetraverse(pool);
+  }
+
+  // Contents in 'idx2faclist' are shifted, now shift them back.
+  for (i = points->items - 1; i >= 0; i--) {
+    idx2faclist[i + 1] = idx2faclist[i];
+  }
+  idx2faclist[0] = 0;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// tetrahedrondealloc()    Deallocate space for a tet., marking it dead.     //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::tetrahedrondealloc(tetrahedron *dyingtetrahedron)
+{
+  // Set tetrahedron's vertices to NULL. This makes it possible to detect
+  //   dead tetrahedra when traversing the list of all tetrahedra.
+  dyingtetrahedron[4] = (tetrahedron) NULL;
+
+  // Dealloc the space to subfaces/subsegments.
+  if (dyingtetrahedron[8] != NULL) {
+    tet2segpool->dealloc((shellface *) dyingtetrahedron[8]);
+  }
+  if (dyingtetrahedron[9] != NULL) {
+    tet2subpool->dealloc((shellface *) dyingtetrahedron[9]);
+  }
+
+  tetrahedrons->dealloc((void *) dyingtetrahedron);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// tetrahedrontraverse()    Traverse the tetrahedra, skipping dead ones.     //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+tetgenmesh::tetrahedron* tetgenmesh::tetrahedrontraverse()
+{
+  tetrahedron *newtetrahedron;
+
+  do {
+    newtetrahedron = (tetrahedron *) tetrahedrons->traverse();
+    if (newtetrahedron == (tetrahedron *) NULL) {
+      return (tetrahedron *) NULL;
+    }
+  } while ((newtetrahedron[4] == (tetrahedron) NULL) ||
+           ((point) newtetrahedron[7] == dummypoint));
+  return newtetrahedron;
+}
+
+tetgenmesh::tetrahedron* tetgenmesh::alltetrahedrontraverse()
+{
+  tetrahedron *newtetrahedron;
+
+  do {
+    newtetrahedron = (tetrahedron *) tetrahedrons->traverse();
+    if (newtetrahedron == (tetrahedron *) NULL) {
+      return (tetrahedron *) NULL;
+    }
+  } while (newtetrahedron[4] == (tetrahedron) NULL); // Skip dead ones.
+  return newtetrahedron;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// shellfacedealloc()    Deallocate space for a shellface, marking it dead.  //
+//                       Used both for dealloc a subface and subsegment.     //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::shellfacedealloc(memorypool *pool, shellface *dyingsh)
+{
+  // Set shellface's vertices to NULL. This makes it possible to detect dead
+  //   shellfaces when traversing the list of all shellfaces.
+  dyingsh[3] = (shellface) NULL;
+  pool->dealloc((void *) dyingsh);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// shellfacetraverse()    Traverse the subfaces, skipping dead ones. Used    //
+//                        for both subfaces and subsegments pool traverse.   //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+tetgenmesh::shellface* tetgenmesh::shellfacetraverse(memorypool *pool)
+{
+  shellface *newshellface;
+
+  do {
+    newshellface = (shellface *) pool->traverse();
+    if (newshellface == (shellface *) NULL) {
+      return (shellface *) NULL;
+    }
+  } while (newshellface[3] == (shellface) NULL);          // Skip dead ones.
+  return newshellface;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// pointdealloc()    Deallocate space for a point, marking it dead.          //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::pointdealloc(point dyingpoint)
+{
+  // Mark the point as dead. This  makes it possible to detect dead points
+  //   when traversing the list of all points.
+  setpointtype(dyingpoint, DEADVERTEX);
+  points->dealloc((void *) dyingpoint);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// pointtraverse()    Traverse the points, skipping dead ones.               //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+tetgenmesh::point tetgenmesh::pointtraverse()
+{
+  point newpoint;
+
+  do {
+    newpoint = (point) points->traverse();
+    if (newpoint == (point) NULL) {
+      return (point) NULL;
+    }
+  } while (pointtype(newpoint) == DEADVERTEX);            // Skip dead ones.
+  return newpoint;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// maketetrahedron()    Create a new tetrahedron.                            //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::maketetrahedron(triface *newtet)
+{
+  newtet->tet = (tetrahedron *) tetrahedrons->alloc();
+
+  // Initialize the four adjoining tetrahedra to be "outer space".
+  newtet->tet[0] = NULL;
+  newtet->tet[1] = NULL;
+  newtet->tet[2] = NULL;
+  newtet->tet[3] = NULL;
+  // Four NULL vertices.
+  newtet->tet[4] = NULL;
+  newtet->tet[5] = NULL;
+  newtet->tet[6] = NULL;
+  newtet->tet[7] = NULL;
+  // No attached segments and subfaces yet.
+  newtet->tet[8] = NULL; 
+  newtet->tet[9] = NULL; 
+  // Initialize the marker (clear all flags).
+  setelemmarker(newtet->tet, 0);
+  for (int i = 0; i < numelemattrib; i++) {
+    setelemattribute(newtet->tet, i, 0.0);
+  }
+  if (b->varvolume) {
+    setvolumebound(newtet->tet, -1.0);
+  }
+
+  // Initialize the version to be Zero.
+  newtet->ver = 11;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// makeshellface()    Create a new shellface with version zero. Used for     //
+//                    both subfaces and subsegments.                         //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::makeshellface(memorypool *pool, face *newface)
+{
+  newface->sh = (shellface *) pool->alloc();
+
+  // No adjointing subfaces.
+  newface->sh[0] = NULL;
+  newface->sh[1] = NULL;
+  newface->sh[2] = NULL;
+  // Three NULL vertices.
+  newface->sh[3] = NULL;
+  newface->sh[4] = NULL;
+  newface->sh[5] = NULL;
+  // No adjoining subsegments.
+  newface->sh[6] = NULL;
+  newface->sh[7] = NULL;
+  newface->sh[8] = NULL;
+  // No adjoining tetrahedra.
+  newface->sh[9] = NULL;
+  newface->sh[10] = NULL;
+  if (checkconstraints) {
+    // Initialize the maximum area bound.
+    setareabound(*newface, 0.0);
+  }
+  // Set the boundary marker to zero.
+  setshellmark(*newface, 0);
+  // Clear the infection and marktest bits.
+  ((int *) (newface->sh))[shmarkindex + 1] = 0;
+  if (useinsertradius) {
+    setfacetindex(*newface, 0);
+  }
+
+  newface->shver = 0;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// makepoint()    Create a new point.                                        //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::makepoint(point* pnewpoint, enum verttype vtype)
+{
+  int i;
+
+  *pnewpoint = (point) points->alloc();
+
+  // Initialize the point attributes.
+  for (i = 0; i < numpointattrib; i++) {
+    (*pnewpoint)[3 + i] = 0.0;
+  }
+  // Initialize the metric tensor.
+  for (i = 0; i < sizeoftensor; i++) {
+    (*pnewpoint)[pointmtrindex + i] = 0.0;
+  }
+  setpoint2tet(*pnewpoint, NULL);
+  setpoint2ppt(*pnewpoint, NULL);
+  if (b->plc || b->refine) {
+    // Initialize the point-to-simplex field.
+    setpoint2sh(*pnewpoint, NULL);
+    if (b->metric && (bgm != NULL)) {
+      setpoint2bgmtet(*pnewpoint, NULL);
+    }
+  }
+  // Initialize the point marker (starting from in->firstnumber).
+  setpointmark(*pnewpoint, (int) (points->items) - (!in->firstnumber));
+  // Clear all flags.
+  ((int *) (*pnewpoint))[pointmarkindex + 1] = 0;
+  // Initialize (set) the point type. 
+  setpointtype(*pnewpoint, vtype);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// initializepools()    Calculate the sizes of the point, tetrahedron, and   //
+//                      subface. Initialize their memory pools.              //
+//                                                                           //
+// This routine also computes the indices 'pointmarkindex', 'point2simindex',//
+// 'point2pbcptindex', 'elemattribindex', and 'volumeboundindex'.  They are  //
+// used to find values within each point and tetrahedron, respectively.      //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::initializepools()
+{
+  int pointsize = 0, elesize = 0, shsize = 0;
+  int i;
+
+  if (b->verbose) {
+    printf("  Initializing memorypools.\n");
+    printf("  tetrahedron per block: %d.\n", b->tetrahedraperblock);
+  }
+
+  inittables();
+
+  // There are three input point lists available, which are in, addin,
+  //   and bgm->in. These point lists may have different number of 
+  //   attributes. Decide the maximum number.
+  numpointattrib = in->numberofpointattributes;
+  if (bgm != NULL) {
+    if (bgm->in->numberofpointattributes > numpointattrib) {
+      numpointattrib = bgm->in->numberofpointattributes;
+    }
+  }
+  if (addin != NULL) {
+    if (addin->numberofpointattributes > numpointattrib) {
+      numpointattrib = addin->numberofpointattributes;
+    }
+  }
+  if (b->weighted || b->flipinsert) { // -w or -L.
+    // The internal number of point attribute needs to be at least 1
+    //   (for storing point weights).
+    if (numpointattrib == 0) {    
+      numpointattrib = 1;
+    }
+  }
+
+  // Default varconstraint = 0;
+  if (in->segmentconstraintlist || in->facetconstraintlist) {
+    checkconstraints = 1;
+  }
+  if (b->plc || b->refine) {
+    // Save the insertion radius for Steiner points if boundaries
+    //   are allowed be split.
+    if (!b->nobisect || checkconstraints) {
+      useinsertradius = 1;
+    }
+  }
+
+  // The index within each point at which its metric tensor is found. 
+  // Each vertex has three coordinates.
+  if (b->psc) {
+    // '-s' option (PSC), the u,v coordinates are provided.
+    pointmtrindex = 5 + numpointattrib;
+    // The index within each point at which its u, v coordinates are found.
+    // Comment: They are saved after the list of point attributes.
+    pointparamindex = pointmtrindex - 2;
+  } else {
+    pointmtrindex = 3 + numpointattrib;
+  }
+  // For '-m' option. A tensor field is provided (*.mtr or *.b.mtr file).
+  if (b->metric) {
+    // Decide the size (1, 3, or 6) of the metric tensor.
+    if (bgm != (tetgenmesh *) NULL) {
+      // A background mesh is allocated. It may not exist though.
+      sizeoftensor = (bgm->in != (tetgenio *) NULL) ? 
+        bgm->in->numberofpointmtrs : in->numberofpointmtrs;
+    } else {
+      // No given background mesh - Itself is a background mesh.
+      sizeoftensor = in->numberofpointmtrs;
+    }
+    // Make sure sizeoftensor is at least 1.
+    sizeoftensor = (sizeoftensor > 0) ? sizeoftensor : 1;
+  } else {
+    // For '-q' option. Make sure to have space for saving a scalar value.
+    sizeoftensor = b->quality ? 1 : 0;
+  }
+  if (useinsertradius) {
+    // Increase a space (REAL) for saving point insertion radius, it is
+    //   saved directly after the metric. 
+    sizeoftensor++;
+  }
+  pointinsradiusindex = pointmtrindex + sizeoftensor - 1;
+  // The index within each point at which an element pointer is found, where
+  //   the index is measured in pointers. Ensure the index is aligned to a
+  //   sizeof(tetrahedron)-byte address.
+  point2simindex = ((pointmtrindex + sizeoftensor) * sizeof(REAL)
+                 + sizeof(tetrahedron) - 1) / sizeof(tetrahedron);
+  if (b->plc || b->refine || b->voroout) {
+    // Increase the point size by three pointers, which are:
+    //   - a pointer to a tet, read by point2tet();
+    //   - a pointer to a parent point, read by point2ppt()).
+    //   - a pointer to a subface or segment, read by point2sh();
+    if (b->metric && (bgm != (tetgenmesh *) NULL)) {
+      // Increase one pointer into the background mesh, point2bgmtet().
+      pointsize = (point2simindex + 4) * sizeof(tetrahedron);
+    } else {
+      pointsize = (point2simindex + 3) * sizeof(tetrahedron);
+    }
+  } else {
+    // Increase the point size by two pointer, which are:
+    //   - a pointer to a tet, read by point2tet();
+    //   - a pointer to a parent point, read by point2ppt()). -- Used by btree.
+    pointsize = (point2simindex + 2) * sizeof(tetrahedron);
+  }
+  // The index within each point at which the boundary marker is found,
+  //   Ensure the point marker is aligned to a sizeof(int)-byte address.
+  pointmarkindex = (pointsize + sizeof(int) - 1) / sizeof(int);
+  // Now point size is the ints (indicated by pointmarkindex) plus:
+  //   - an integer for boundary marker;
+  //   - an integer for vertex type;
+  //   - an integer for geometry tag (optional, -s option).
+  pointsize = (pointmarkindex + 2 + (b->psc ? 1 : 0)) * sizeof(tetrahedron);
+
+  // Initialize the pool of vertices.
+  points = new memorypool(pointsize, b->vertexperblock, sizeof(REAL), 0);
+
+  if (b->verbose) {
+    printf("  Size of a point: %d bytes.\n", points->itembytes);
+  }
+
+  // Initialize the infinite vertex.
+  dummypoint = (point) new char[pointsize];
+  // Initialize all fields of this point.
+  dummypoint[0] = 0.0;
+  dummypoint[1] = 0.0;
+  dummypoint[2] = 0.0;
+  for (i = 0; i < numpointattrib; i++) {
+    dummypoint[3 + i] = 0.0;
+  }
+  // Initialize the metric tensor.
+  for (i = 0; i < sizeoftensor; i++) {
+    dummypoint[pointmtrindex + i] = 0.0;
+  }
+  setpoint2tet(dummypoint, NULL);
+  setpoint2ppt(dummypoint, NULL);
+  if (b->plc || b->psc || b->refine) {
+    // Initialize the point-to-simplex field.
+    setpoint2sh(dummypoint, NULL);
+    if (b->metric && (bgm != NULL)) {
+      setpoint2bgmtet(dummypoint, NULL);
+    }
+  }
+  // Initialize the point marker (starting from in->firstnumber).
+  setpointmark(dummypoint, -1); // The unique marker for dummypoint.
+  // Clear all flags.
+  ((int *) (dummypoint))[pointmarkindex + 1] = 0;
+  // Initialize (set) the point type. 
+  setpointtype(dummypoint, UNUSEDVERTEX); // Does not matter.
+
+  // The number of bytes occupied by a tetrahedron is varying by the user-
+  //   specified options. The contents of the first 12 pointers are listed
+  //   in the following table:
+  //     [0]  |__ neighbor at f0 __|
+  //     [1]  |__ neighbor at f1 __|
+  //     [2]  |__ neighbor at f2 __|
+  //     [3]  |__ neighbor at f3 __|
+  //     [4]  |_____ vertex p0 ____|
+  //     [5]  |_____ vertex p1 ____|
+  //     [6]  |_____ vertex p2 ____|
+  //     [7]  |_____ vertex p3 ____|
+  //     [8]  |__ segments array __| (used by -p)
+  //     [9]  |__ subfaces array __| (used by -p)
+  //    [10]  |_____ reserved _____|
+  //    [11]  |___ elem marker ____| (used as an integer)
+
+  elesize = 12 * sizeof(tetrahedron); 
+
+  // The index to find the element markers. An integer containing varies
+  //   flags and element counter. 
+  if (!(sizeof(int) <= sizeof(tetrahedron)) ||
+      ((sizeof(tetrahedron) % sizeof(int)))) {
+    terminatetetgen(this, 2);
+  }
+  elemmarkerindex = (elesize - sizeof(tetrahedron)) / sizeof(int);
+
+  // The actual number of element attributes. Note that if the
+  //   `b->regionattrib' flag is set, an additional attribute will be added.
+  numelemattrib = in->numberoftetrahedronattributes + (b->regionattrib > 0);
+
+  // The index within each element at which its attributes are found, where
+  //   the index is measured in REALs. 
+  elemattribindex = (elesize + sizeof(REAL) - 1) / sizeof(REAL);
+  // The index within each element at which the maximum volume bound is
+  //   found, where the index is measured in REALs.
+  volumeboundindex = elemattribindex + numelemattrib;
+  // If element attributes or an constraint are needed, increase the number
+  //   of bytes occupied by an element.
+  if (b->varvolume) {
+    elesize = (volumeboundindex + 1) * sizeof(REAL);
+  } else if (numelemattrib > 0) {
+    elesize = volumeboundindex * sizeof(REAL);
+  }
+
+
+  // Having determined the memory size of an element, initialize the pool.
+  tetrahedrons = new memorypool(elesize, b->tetrahedraperblock, sizeof(void *),
+                                16);
+
+  if (b->verbose) {
+    printf("  Size of a tetrahedron: %d (%d) bytes.\n", elesize,
+           tetrahedrons->itembytes);
+  }
+
+  if (b->plc || b->refine) { // if (b->useshelles) {
+    // The number of bytes occupied by a subface.  The list of pointers
+    //   stored in a subface are: three to other subfaces, three to corners,
+    //   three to subsegments, two to tetrahedra.
+    shsize = 11 * sizeof(shellface);
+    // The index within each subface at which the maximum area bound is
+    //   found, where the index is measured in REALs.
+    areaboundindex = (shsize + sizeof(REAL) - 1) / sizeof(REAL);
+    // If -q switch is in use, increase the number of bytes occupied by
+    //   a subface for saving maximum area bound.
+    if (checkconstraints) { 
+      shsize = (areaboundindex + 1) * sizeof(REAL);
+    } else {
+      shsize = areaboundindex * sizeof(REAL);
+    }
+    // The index within subface at which the facet marker is found. Ensure
+    //   the marker is aligned to a sizeof(int)-byte address.
+    shmarkindex = (shsize + sizeof(int) - 1) / sizeof(int);
+    // Increase the number of bytes by two or three integers, one for facet
+    //   marker, one for shellface type and flags, and optionally one
+	//   for storing facet index (for mesh refinement).
+    shsize = (shmarkindex + 2 + useinsertradius) * sizeof(shellface);
+
+    // Initialize the pool of subfaces. Each subface record is eight-byte
+    //   aligned so it has room to store an edge version (from 0 to 5) in
+    //   the least three bits.
+    subfaces = new memorypool(shsize, b->shellfaceperblock, sizeof(void *), 8);
+
+    if (b->verbose) {
+      printf("  Size of a shellface: %d (%d) bytes.\n", shsize,
+             subfaces->itembytes);
+    }
+
+    // Initialize the pool of subsegments. The subsegment's record is same
+    //   with subface.
+    subsegs = new memorypool(shsize, b->shellfaceperblock, sizeof(void *), 8);
+
+    // Initialize the pool for tet-subseg connections.
+    tet2segpool = new memorypool(6 * sizeof(shellface), b->shellfaceperblock, 
+                                 sizeof(void *), 0);
+    // Initialize the pool for tet-subface connections.
+    tet2subpool = new memorypool(4 * sizeof(shellface), b->shellfaceperblock, 
+                                 sizeof(void *), 0);
+
+    // Initialize arraypools for segment & facet recovery.
+    subsegstack = new arraypool(sizeof(face), 10);
+    subfacstack = new arraypool(sizeof(face), 10);
+    subvertstack = new arraypool(sizeof(point), 8);
+
+    // Initialize arraypools for surface point insertion/deletion.
+    caveshlist = new arraypool(sizeof(face), 8);
+    caveshbdlist = new arraypool(sizeof(face), 8);
+    cavesegshlist = new arraypool(sizeof(face), 4);
+
+    cavetetshlist = new arraypool(sizeof(face), 8);
+    cavetetseglist = new arraypool(sizeof(face), 8);
+    caveencshlist = new arraypool(sizeof(face), 8);
+    caveencseglist = new arraypool(sizeof(face), 8);
+  }
+
+  // Initialize the pools for flips.
+  flippool = new memorypool(sizeof(badface), 1024, sizeof(void *), 0);
+  unflipqueue = new arraypool(sizeof(badface), 10);
+
+  // Initialize the arraypools for point insertion.
+  cavetetlist = new arraypool(sizeof(triface), 10);
+  cavebdrylist = new arraypool(sizeof(triface), 10);
+  caveoldtetlist = new arraypool(sizeof(triface), 10);
+  cavetetvertlist = new arraypool(sizeof(point), 10);
+}
+
+////                                                                       ////
+////                                                                       ////
+//// mempool_cxx //////////////////////////////////////////////////////////////
+
+//// geom_cxx /////////////////////////////////////////////////////////////////
+////                                                                       ////
+////                                                                       ////
+
+// PI is the ratio of a circle's circumference to its diameter.
+REAL tetgenmesh::PI = 3.14159265358979323846264338327950288419716939937510582;
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// insphere_s()    Insphere test with symbolic perturbation.                 //
+//                                                                           //
+// Given four points pa, pb, pc, and pd, test if the point pe lies inside or //
+// outside the circumscribed sphere of the four points.                      //
+//                                                                           //
+// Here we assume that the 3d orientation of the point sequence {pa, pb, pc, //
+// pd} is positive (NOT zero), i.e., pd lies above the plane passing through //
+// points pa, pb, and pc. Otherwise, the returned sign is flipped.           //
+//                                                                           //
+// Return a positive value (> 0) if pe lies inside, a negative value (< 0)   //
+// if pe lies outside the sphere, the returned value will not be zero.       //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+REAL tetgenmesh::insphere_s(REAL* pa, REAL* pb, REAL* pc, REAL* pd, REAL* pe)
+{
+  REAL sign;
+
+  sign = insphere(pa, pb, pc, pd, pe);
+  if (sign != 0.0) {
+    return sign;
+  }
+
+  // Symbolic perturbation.
+  point pt[5], swappt;
+  REAL oriA, oriB;
+  int swaps, count;
+  int n, i;
+
+  pt[0] = pa;
+  pt[1] = pb;
+  pt[2] = pc;
+  pt[3] = pd;
+  pt[4] = pe;
+  
+  // Sort the five points such that their indices are in the increasing
+  //   order. An optimized bubble sort algorithm is used, i.e., it has
+  //   the worst case O(n^2) runtime, but it is usually much faster.
+  swaps = 0; // Record the total number of swaps.
+  n = 5;
+  do {
+    count = 0;
+    n = n - 1;
+    for (i = 0; i < n; i++) {
+      if (pointmark(pt[i]) > pointmark(pt[i+1])) {
+        swappt = pt[i]; pt[i] = pt[i+1]; pt[i+1] = swappt;
+        count++;
+      }
+    }
+    swaps += count;
+  } while (count > 0); // Continue if some points are swapped.
+
+  oriA = orient3d(pt[1], pt[2], pt[3], pt[4]);
+  if (oriA != 0.0) {
+    // Flip the sign if there are odd number of swaps.
+    if ((swaps % 2) != 0) oriA = -oriA;
+    return oriA;
+  }
+  
+  oriB = -orient3d(pt[0], pt[2], pt[3], pt[4]);
+  if (oriB == 0.0) {
+    terminatetetgen(this, 2);
+  }
+  // Flip the sign if there are odd number of swaps.
+  if ((swaps % 2) != 0) oriB = -oriB;
+  return oriB;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// orient4d_s()    4d orientation test with symbolic perturbation.           //
+//                                                                           //
+// Given four lifted points pa', pb', pc', and pd' in R^4,test if the lifted //
+// point pe' in R^4 lies below or above the hyperplane passing through the   //
+// four points pa', pb', pc', and pd'.                                       //
+//                                                                           //
+// Here we assume that the 3d orientation of the point sequence {pa, pb, pc, //
+// pd} is positive (NOT zero), i.e., pd lies above the plane passing through //
+// the points pa, pb, and pc. Otherwise, the returned sign is flipped.       //
+//                                                                           //
+// Return a positive value (> 0) if pe' lies below, a negative value (< 0)   //
+// if pe' lies above the hyperplane, the returned value should not be zero.  //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+REAL tetgenmesh::orient4d_s(REAL* pa, REAL* pb, REAL* pc, REAL* pd, REAL* pe,
+                            REAL aheight, REAL bheight, REAL cheight, 
+                            REAL dheight, REAL eheight)
+{
+  REAL sign;
+
+  sign = orient4d(pa, pb, pc, pd, pe, 
+                  aheight, bheight, cheight, dheight, eheight);
+  if (sign != 0.0) {
+    return sign;
+  }
+
+  // Symbolic perturbation.
+  point pt[5], swappt;
+  REAL oriA, oriB;
+  int swaps, count;
+  int n, i;
+
+  pt[0] = pa;
+  pt[1] = pb;
+  pt[2] = pc;
+  pt[3] = pd;
+  pt[4] = pe;
+  
+  // Sort the five points such that their indices are in the increasing
+  //   order. An optimized bubble sort algorithm is used, i.e., it has
+  //   the worst case O(n^2) runtime, but it is usually much faster.
+  swaps = 0; // Record the total number of swaps.
+  n = 5;
+  do {
+    count = 0;
+    n = n - 1;
+    for (i = 0; i < n; i++) {
+      if (pointmark(pt[i]) > pointmark(pt[i+1])) {
+        swappt = pt[i]; pt[i] = pt[i+1]; pt[i+1] = swappt;
+        count++;
+      }
+    }
+    swaps += count;
+  } while (count > 0); // Continue if some points are swapped.
+
+  oriA = orient3d(pt[1], pt[2], pt[3], pt[4]);
+  if (oriA != 0.0) {
+    // Flip the sign if there are odd number of swaps.
+    if ((swaps % 2) != 0) oriA = -oriA;
+    return oriA;
+  }
+  
+  oriB = -orient3d(pt[0], pt[2], pt[3], pt[4]);
+  if (oriB == 0.0) {
+    terminatetetgen(this, 2);
+  }
+  // Flip the sign if there are odd number of swaps.
+  if ((swaps % 2) != 0) oriB = -oriB;
+  return oriB;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// tri_edge_test()    Triangle-edge intersection test.                       //
+//                                                                           //
+// This routine takes a triangle T (with vertices A, B, C) and an edge E (P, //
+// Q) in 3D, and tests if they intersect each other.                         //
+//                                                                           //
+// If the point 'R' is not NULL, it lies strictly above the plane defined by //
+// A, B, C. It is used in test when T and E are coplanar.                    //
+//                                                                           //
+// If T and E intersect each other, they may intersect in different ways. If //
+// 'level' > 0, their intersection type will be reported 'types' and 'pos'.  //
+//                                                                           //
+// The return value indicates one of the following cases:                    //
+//   - 0, T and E are disjoint.                                              //
+//   - 1, T and E intersect each other.                                      //
+//   - 2, T and E are not coplanar. They intersect at a single point.        //
+//   - 4, T and E are coplanar. They intersect at a single point or a line   //
+//        segment (if types[1] != DISJOINT).                                 //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+#define SETVECTOR3(V, a0, a1, a2) (V)[0] = (a0); (V)[1] = (a1); (V)[2] = (a2)
+
+#define SWAP2(a0, a1, tmp) (tmp) = (a0); (a0) = (a1); (a1) = (tmp)
+
+int tetgenmesh::tri_edge_2d(point A, point B, point C, point P, point Q, 
+                            point R, int level, int *types, int *pos)
+{
+  point U[3], V[3];  // The permuted vectors of points.
+  int pu[3], pv[3];  // The original positions of points.
+  REAL abovept[3];
+  REAL sA, sB, sC;
+  REAL s1, s2, s3, s4;
+  int z1;
+
+  if (R == NULL) {
+    // Calculate a lift point.
+    if (1) {
+      REAL n[3], len;
+      // Calculate a lift point, saved in dummypoint.
+      facenormal(A, B, C, n, 1, NULL);
+      len = sqrt(dot(n, n));
+      if (len != 0) {
+        n[0] /= len;
+        n[1] /= len;
+        n[2] /= len;
+        len = distance(A, B);
+        len += distance(B, C);
+        len += distance(C, A);
+        len /= 3.0;
+        R = abovept; //dummypoint;
+        R[0] = A[0] + len * n[0];
+        R[1] = A[1] + len * n[1];
+        R[2] = A[2] + len * n[2];
+      } else {
+        // The triangle [A,B,C] is (nearly) degenerate, i.e., it is (close)
+        //   to a line.  We need a line-line intersection test.
+        // !!! A non-save return value.!!!
+        return 0;  // DISJOINT
+      }
+    }
+  }
+
+  // Test A's, B's, and C's orientations wrt plane PQR. 
+  sA = orient3d(P, Q, R, A);
+  sB = orient3d(P, Q, R, B);
+  sC = orient3d(P, Q, R, C);
+
+
+  if (sA < 0) {
+    if (sB < 0) {
+      if (sC < 0) { // (---).
+        return 0; 
+      } else {
+        if (sC > 0) { // (--+).
+          // All points are in the right positions.
+          SETVECTOR3(U, A, B, C);  // I3
+          SETVECTOR3(V, P, Q, R);  // I2
+          SETVECTOR3(pu, 0, 1, 2);
+          SETVECTOR3(pv, 0, 1, 2);
+          z1 = 0;
+        } else { // (--0).
+          SETVECTOR3(U, A, B, C);  // I3
+          SETVECTOR3(V, P, Q, R);  // I2
+          SETVECTOR3(pu, 0, 1, 2);
+          SETVECTOR3(pv, 0, 1, 2);
+          z1 = 1;
+        }
+      }
+    } else { 
+      if (sB > 0) {
+        if (sC < 0) { // (-+-).
+          SETVECTOR3(U, C, A, B);  // PT = ST
+          SETVECTOR3(V, P, Q, R);  // I2
+          SETVECTOR3(pu, 2, 0, 1);
+          SETVECTOR3(pv, 0, 1, 2);
+          z1 = 0;
+        } else {
+          if (sC > 0) { // (-++).
+            SETVECTOR3(U, B, C, A);  // PT = ST x ST
+            SETVECTOR3(V, Q, P, R);  // PL = SL
+            SETVECTOR3(pu, 1, 2, 0);
+            SETVECTOR3(pv, 1, 0, 2);
+            z1 = 0;
+          } else { // (-+0).
+            SETVECTOR3(U, C, A, B);  // PT = ST
+            SETVECTOR3(V, P, Q, R);  // I2
+            SETVECTOR3(pu, 2, 0, 1);
+            SETVECTOR3(pv, 0, 1, 2);
+            z1 = 2;
+          }
+        }
+      } else {
+        if (sC < 0) { // (-0-).
+          SETVECTOR3(U, C, A, B);  // PT = ST
+          SETVECTOR3(V, P, Q, R);  // I2
+          SETVECTOR3(pu, 2, 0, 1);
+          SETVECTOR3(pv, 0, 1, 2);
+          z1 = 1;
+        } else {
+          if (sC > 0) { // (-0+).
+            SETVECTOR3(U, B, C, A);  // PT = ST x ST
+            SETVECTOR3(V, Q, P, R);  // PL = SL
+            SETVECTOR3(pu, 1, 2, 0);
+            SETVECTOR3(pv, 1, 0, 2);
+            z1 = 2;
+          } else { // (-00).
+            SETVECTOR3(U, B, C, A);  // PT = ST x ST
+            SETVECTOR3(V, Q, P, R);  // PL = SL
+            SETVECTOR3(pu, 1, 2, 0);
+            SETVECTOR3(pv, 1, 0, 2);
+            z1 = 3; 
+          }
+        }
+      }
+    }
+  } else {
+    if (sA > 0) {
+      if (sB < 0) {
+        if (sC < 0) { // (+--).
+          SETVECTOR3(U, B, C, A);  // PT = ST x ST
+          SETVECTOR3(V, P, Q, R);  // I2
+          SETVECTOR3(pu, 1, 2, 0);
+          SETVECTOR3(pv, 0, 1, 2);
+          z1 = 0;
+        } else {
+          if (sC > 0) { // (+-+).
+            SETVECTOR3(U, C, A, B);  // PT = ST
+            SETVECTOR3(V, Q, P, R);  // PL = SL
+            SETVECTOR3(pu, 2, 0, 1);
+            SETVECTOR3(pv, 1, 0, 2);
+            z1 = 0;
+          } else { // (+-0).
+            SETVECTOR3(U, C, A, B);  // PT = ST
+            SETVECTOR3(V, Q, P, R);  // PL = SL
+            SETVECTOR3(pu, 2, 0, 1);
+            SETVECTOR3(pv, 1, 0, 2);
+            z1 = 2;
+          }
+        }
+      } else { 
+        if (sB > 0) {
+          if (sC < 0) { // (++-).
+            SETVECTOR3(U, A, B, C);  // I3
+            SETVECTOR3(V, Q, P, R);  // PL = SL
+            SETVECTOR3(pu, 0, 1, 2);
+            SETVECTOR3(pv, 1, 0, 2);
+            z1 = 0;
+          } else {
+            if (sC > 0) { // (+++).
+              return 0; 
+            } else { // (++0).
+              SETVECTOR3(U, A, B, C);  // I3
+              SETVECTOR3(V, Q, P, R);  // PL = SL
+              SETVECTOR3(pu, 0, 1, 2);
+              SETVECTOR3(pv, 1, 0, 2);
+              z1 = 1; 
+            }
+          }
+        } else { // (+0#)
+          if (sC < 0) { // (+0-).
+            SETVECTOR3(U, B, C, A);  // PT = ST x ST
+            SETVECTOR3(V, P, Q, R);  // I2
+            SETVECTOR3(pu, 1, 2, 0);
+            SETVECTOR3(pv, 0, 1, 2);
+            z1 = 2;
+          } else {
+            if (sC > 0) { // (+0+).
+              SETVECTOR3(U, C, A, B);  // PT = ST
+              SETVECTOR3(V, Q, P, R);  // PL = SL
+              SETVECTOR3(pu, 2, 0, 1);
+              SETVECTOR3(pv, 1, 0, 2);
+              z1 = 1;
+            } else { // (+00).
+              SETVECTOR3(U, B, C, A);  // PT = ST x ST
+              SETVECTOR3(V, P, Q, R);  // I2
+              SETVECTOR3(pu, 1, 2, 0);
+              SETVECTOR3(pv, 0, 1, 2);
+              z1 = 3; 
+            }
+          }
+        }
+      }
+    } else { 
+      if (sB < 0) {
+        if (sC < 0) { // (0--).
+          SETVECTOR3(U, B, C, A);  // PT = ST x ST
+          SETVECTOR3(V, P, Q, R);  // I2
+          SETVECTOR3(pu, 1, 2, 0);
+          SETVECTOR3(pv, 0, 1, 2);
+          z1 = 1;
+        } else {
+          if (sC > 0) { // (0-+).
+            SETVECTOR3(U, A, B, C);  // I3
+            SETVECTOR3(V, P, Q, R);  // I2
+            SETVECTOR3(pu, 0, 1, 2);
+            SETVECTOR3(pv, 0, 1, 2);
+            z1 = 2;
+          } else { // (0-0).
+            SETVECTOR3(U, C, A, B);  // PT = ST
+            SETVECTOR3(V, Q, P, R);  // PL = SL
+            SETVECTOR3(pu, 2, 0, 1);
+            SETVECTOR3(pv, 1, 0, 2);
+            z1 = 3; 
+          }
+        }
+      } else { 
+        if (sB > 0) {
+          if (sC < 0) { // (0+-).
+            SETVECTOR3(U, A, B, C);  // I3
+            SETVECTOR3(V, Q, P, R);  // PL = SL
+            SETVECTOR3(pu, 0, 1, 2);
+            SETVECTOR3(pv, 1, 0, 2);
+            z1 = 2;
+          } else {
+            if (sC > 0) { // (0++).
+              SETVECTOR3(U, B, C, A);  // PT = ST x ST
+              SETVECTOR3(V, Q, P, R);  // PL = SL
+              SETVECTOR3(pu, 1, 2, 0);
+              SETVECTOR3(pv, 1, 0, 2);
+              z1 = 1;
+            } else { // (0+0).
+              SETVECTOR3(U, C, A, B);  // PT = ST
+              SETVECTOR3(V, P, Q, R);  // I2
+              SETVECTOR3(pu, 2, 0, 1);
+              SETVECTOR3(pv, 0, 1, 2);
+              z1 = 3; 
+            }
+          }
+        } else { // (00#)
+          if (sC < 0) { // (00-).
+            SETVECTOR3(U, A, B, C);  // I3
+            SETVECTOR3(V, Q, P, R);  // PL = SL
+            SETVECTOR3(pu, 0, 1, 2);
+            SETVECTOR3(pv, 1, 0, 2);
+            z1 = 3; 
+          } else {
+            if (sC > 0) { // (00+).
+              SETVECTOR3(U, A, B, C);  // I3
+              SETVECTOR3(V, P, Q, R);  // I2
+              SETVECTOR3(pu, 0, 1, 2);
+              SETVECTOR3(pv, 0, 1, 2);
+              z1 = 3; 
+            } else { // (000)
+              // Not possible unless ABC is degenerate.
+              // Avoiding compiler warnings.
+              SETVECTOR3(U, A, B, C);  // I3
+              SETVECTOR3(V, P, Q, R);  // I2
+              SETVECTOR3(pu, 0, 1, 2);
+              SETVECTOR3(pv, 0, 1, 2);
+              z1 = 4;
+            }
+          }
+        }
+      }
+    }
+  }
+
+  s1 = orient3d(U[0], U[2], R, V[1]);  // A, C, R, Q
+  s2 = orient3d(U[1], U[2], R, V[0]);  // B, C, R, P
+
+  if (s1 > 0) {
+    return 0;
+  }
+  if (s2 < 0) {
+    return 0;
+  }
+
+  if (level == 0) {
+    return 1;  // They are intersected.
+  }
+
+
+  if (z1 == 1) {
+    if (s1 == 0) {  // (0###)
+      // C = Q.
+      types[0] = (int) SHAREVERT;
+      pos[0] = pu[2]; // C
+      pos[1] = pv[1]; // Q
+      types[1] = (int) DISJOINT;
+    } else {
+      if (s2 == 0) { // (#0##)
+        // C = P.
+        types[0] = (int) SHAREVERT;
+        pos[0] = pu[2]; // C
+        pos[1] = pv[0]; // P
+        types[1] = (int) DISJOINT;
+      } else { // (-+##)
+        // C in [P, Q].
+        types[0] = (int) ACROSSVERT;
+        pos[0] = pu[2]; // C
+        pos[1] = pv[0]; // [P, Q]
+        types[1] = (int) DISJOINT;
+      }
+    }
+    return 4;
+  }
+
+  s3 = orient3d(U[0], U[2], R, V[0]);  // A, C, R, P
+  s4 = orient3d(U[1], U[2], R, V[1]);  // B, C, R, Q
+      
+  if (z1 == 0) {  // (tritri-03)
+    if (s1 < 0) {
+      if (s3 > 0) {
+        if (s4 > 0) {
+          // [P, Q] overlaps [k, l] (-+++).
+          types[0] = (int) ACROSSEDGE;
+          pos[0] = pu[2]; // [C, A]
+          pos[1] = pv[0]; // [P, Q]
+          types[1] = (int) TOUCHFACE;
+          pos[2] = 3;     // [A, B, C]
+          pos[3] = pv[1]; // Q
+        } else {
+          if (s4 == 0) {
+            // Q = l, [P, Q] contains [k, l] (-++0).
+            types[0] = (int) ACROSSEDGE;
+            pos[0] = pu[2]; // [C, A]
+            pos[1] = pv[0]; // [P, Q]
+            types[1] = (int) TOUCHEDGE;
+            pos[2] = pu[1]; // [B, C]
+            pos[3] = pv[1]; // Q
+          } else { // s4 < 0
+            // [P, Q] contains [k, l] (-++-).
+            types[0] = (int) ACROSSEDGE;
+            pos[0] = pu[2]; // [C, A]
+            pos[1] = pv[0]; // [P, Q]
+            types[1] = (int) ACROSSEDGE;
+            pos[2] = pu[1]; // [B, C]
+            pos[3] = pv[0]; // [P, Q]
+          }
+        }
+      } else {
+        if (s3 == 0) {
+          if (s4 > 0) {
+            // P = k, [P, Q] in [k, l] (-+0+).
+            types[0] = (int) TOUCHEDGE;
+            pos[0] = pu[2]; // [C, A]
+            pos[1] = pv[0]; // P
+            types[1] = (int) TOUCHFACE;
+            pos[2] = 3;     // [A, B, C]
+            pos[3] = pv[1]; // Q
+          } else {
+            if (s4 == 0) {
+              // [P, Q] = [k, l] (-+00).
+              types[0] = (int) TOUCHEDGE;
+              pos[0] = pu[2]; // [C, A]
+              pos[1] = pv[0]; // P
+              types[1] = (int) TOUCHEDGE;
+              pos[2] = pu[1]; // [B, C]
+              pos[3] = pv[1]; // Q
+            } else {
+              // P = k, [P, Q] contains [k, l] (-+0-).
+              types[0] = (int) TOUCHEDGE;
+              pos[0] = pu[2]; // [C, A]
+              pos[1] = pv[0]; // P
+              types[1] = (int) ACROSSEDGE;
+              pos[2] = pu[1]; // [B, C]
+              pos[3] = pv[0]; // [P, Q]
+            }
+          }
+        } else { // s3 < 0
+          if (s2 > 0) {
+            if (s4 > 0) {
+              // [P, Q] in [k, l] (-+-+).
+              types[0] = (int) TOUCHFACE;
+              pos[0] = 3;     // [A, B, C]
+              pos[1] = pv[0]; // P
+              types[1] = (int) TOUCHFACE;
+              pos[2] = 3;     // [A, B, C]
+              pos[3] = pv[1]; // Q
+            } else {
+              if (s4 == 0) {
+                // Q = l, [P, Q] in [k, l] (-+-0).
+                types[0] = (int) TOUCHFACE;
+                pos[0] = 3;     // [A, B, C]
+                pos[1] = pv[0]; // P
+                types[1] = (int) TOUCHEDGE;
+                pos[2] = pu[1]; // [B, C]
+                pos[3] = pv[1]; // Q
+              } else { // s4 < 0
+                // [P, Q] overlaps [k, l] (-+--).
+                types[0] = (int) TOUCHFACE;
+                pos[0] = 3;     // [A, B, C]
+                pos[1] = pv[0]; // P
+                types[1] = (int) ACROSSEDGE;
+                pos[2] = pu[1]; // [B, C]
+                pos[3] = pv[0]; // [P, Q]
+              }
+            }
+          } else { // s2 == 0
+            // P = l (#0##).
+            types[0] = (int) TOUCHEDGE;
+            pos[0] = pu[1]; // [B, C]
+            pos[1] = pv[0]; // P
+            types[1] = (int) DISJOINT;
+          }
+        }
+      }
+    } else { // s1 == 0
+      // Q = k (0####)
+      types[0] = (int) TOUCHEDGE;
+      pos[0] = pu[2]; // [C, A]
+      pos[1] = pv[1]; // Q
+      types[1] = (int) DISJOINT;
+    }
+  } else if (z1 == 2) {  // (tritri-23)
+    if (s1 < 0) {
+      if (s3 > 0) {
+        if (s4 > 0) {
+          // [P, Q] overlaps [A, l] (-+++).
+          types[0] = (int) ACROSSVERT;
+          pos[0] = pu[0]; // A
+          pos[1] = pv[0]; // [P, Q]
+          types[1] = (int) TOUCHFACE;
+          pos[2] = 3;     // [A, B, C]
+          pos[3] = pv[1]; // Q
+        } else {
+          if (s4 == 0) {
+            // Q = l, [P, Q] contains [A, l] (-++0).
+            types[0] = (int) ACROSSVERT;
+            pos[0] = pu[0]; // A
+            pos[1] = pv[0]; // [P, Q]
+            types[1] = (int) TOUCHEDGE;
+            pos[2] = pu[1]; // [B, C]
+            pos[3] = pv[1]; // Q
+          } else { // s4 < 0
+            // [P, Q] contains [A, l] (-++-).
+            types[0] = (int) ACROSSVERT;
+            pos[0] = pu[0]; // A
+            pos[1] = pv[0]; // [P, Q]
+            types[1] = (int) ACROSSEDGE;
+            pos[2] = pu[1]; // [B, C]
+            pos[3] = pv[0]; // [P, Q]
+          }
+        }
+      } else {
+        if (s3 == 0) {
+          if (s4 > 0) {
+            // P = A, [P, Q] in [A, l] (-+0+).
+            types[0] = (int) SHAREVERT;
+            pos[0] = pu[0]; // A
+            pos[1] = pv[0]; // P
+            types[1] = (int) TOUCHFACE;
+            pos[2] = 3;     // [A, B, C]
+            pos[3] = pv[1]; // Q
+          } else {
+            if (s4 == 0) {
+              // [P, Q] = [A, l] (-+00).
+              types[0] = (int) SHAREVERT;
+              pos[0] = pu[0]; // A
+              pos[1] = pv[0]; // P
+              types[1] = (int) TOUCHEDGE;
+              pos[2] = pu[1]; // [B, C]
+              pos[3] = pv[1]; // Q
+            } else { // s4 < 0
+              // Q = l, [P, Q] in [A, l] (-+0-).
+              types[0] = (int) SHAREVERT;
+              pos[0] = pu[0]; // A
+              pos[1] = pv[0]; // P
+              types[1] = (int) ACROSSEDGE;
+              pos[2] = pu[1]; // [B, C]
+              pos[3] = pv[0]; // [P, Q]
+            }
+          }
+        } else { // s3 < 0
+          if (s2 > 0) {
+            if (s4 > 0) {
+              // [P, Q] in [A, l] (-+-+).
+              types[0] = (int) TOUCHFACE;
+              pos[0] = 3;     // [A, B, C]
+              pos[1] = pv[0]; // P
+              types[0] = (int) TOUCHFACE;
+              pos[0] = 3;     // [A, B, C]
+              pos[1] = pv[1]; // Q
+            } else {
+              if (s4 == 0) {
+                // Q = l, [P, Q] in [A, l] (-+-0).
+                types[0] = (int) TOUCHFACE;
+                pos[0] = 3;     // [A, B, C]
+                pos[1] = pv[0]; // P
+                types[0] = (int) TOUCHEDGE;
+                pos[0] = pu[1]; // [B, C]
+                pos[1] = pv[1]; // Q
+              } else { // s4 < 0
+                // [P, Q] overlaps [A, l] (-+--).
+                types[0] = (int) TOUCHFACE;
+                pos[0] = 3;     // [A, B, C]
+                pos[1] = pv[0]; // P
+                types[0] = (int) ACROSSEDGE;
+                pos[0] = pu[1]; // [B, C]
+                pos[1] = pv[0]; // [P, Q]
+              }
+            }
+          } else { // s2 == 0
+            // P = l (#0##).
+            types[0] = (int) TOUCHEDGE;
+            pos[0] = pu[1]; // [B, C]
+            pos[1] = pv[0]; // P
+            types[1] = (int) DISJOINT;
+          }
+        }
+      }
+    } else { // s1 == 0
+      // Q = A (0###).
+      types[0] = (int) SHAREVERT;
+      pos[0] = pu[0]; // A
+      pos[1] = pv[1]; // Q
+      types[1] = (int) DISJOINT;
+    }
+  } else if (z1 == 3) {  // (tritri-33)
+    if (s1 < 0) {
+      if (s3 > 0) {
+        if (s4 > 0) {
+          // [P, Q] overlaps [A, B] (-+++).
+          types[0] = (int) ACROSSVERT;
+          pos[0] = pu[0]; // A
+          pos[1] = pv[0]; // [P, Q]
+          types[1] = (int) TOUCHEDGE;
+          pos[2] = pu[0]; // [A, B]
+          pos[3] = pv[1]; // Q
+        } else {
+          if (s4 == 0) {
+            // Q = B, [P, Q] contains [A, B] (-++0).
+            types[0] = (int) ACROSSVERT;
+            pos[0] = pu[0]; // A
+            pos[1] = pv[0]; // [P, Q]
+            types[1] = (int) SHAREVERT;
+            pos[2] = pu[1]; // B
+            pos[3] = pv[1]; // Q
+          } else { // s4 < 0
+            // [P, Q] contains [A, B] (-++-).
+            types[0] = (int) ACROSSVERT;
+            pos[0] = pu[0]; // A
+            pos[1] = pv[0]; // [P, Q]
+            types[1] = (int) ACROSSVERT;
+            pos[2] = pu[1]; // B
+            pos[3] = pv[0]; // [P, Q]
+          }
+        }
+      } else {
+        if (s3 == 0) {
+          if (s4 > 0) {
+            // P = A, [P, Q] in [A, B] (-+0+).
+            types[0] = (int) SHAREVERT;
+            pos[0] = pu[0]; // A
+            pos[1] = pv[0]; // P
+            types[1] = (int) TOUCHEDGE;
+            pos[2] = pu[0]; // [A, B]
+            pos[3] = pv[1]; // Q
+          } else {
+            if (s4 == 0) {
+              // [P, Q] = [A, B] (-+00).
+              types[0] = (int) SHAREEDGE;
+              pos[0] = pu[0]; // [A, B]
+              pos[1] = pv[0]; // [P, Q]
+              types[1] = (int) DISJOINT;
+            } else { // s4 < 0
+              // P= A, [P, Q] in [A, B] (-+0-).
+              types[0] = (int) SHAREVERT;
+              pos[0] = pu[0]; // A
+              pos[1] = pv[0]; // P
+              types[1] = (int) ACROSSVERT;
+              pos[2] = pu[1]; // B
+              pos[3] = pv[0]; // [P, Q]
+            }
+          }
+        } else { // s3 < 0
+          if (s2 > 0) {
+            if (s4 > 0) {
+              // [P, Q] in [A, B] (-+-+).
+              types[0] = (int) TOUCHEDGE;
+              pos[0] = pu[0]; // [A, B]
+              pos[1] = pv[0]; // P
+              types[1] = (int) TOUCHEDGE;
+              pos[2] = pu[0]; // [A, B]
+              pos[3] = pv[1]; // Q
+            } else {
+              if (s4 == 0) {
+                // Q = B, [P, Q] in [A, B] (-+-0).
+                types[0] = (int) TOUCHEDGE;
+                pos[0] = pu[0]; // [A, B]
+                pos[1] = pv[0]; // P
+                types[1] = (int) SHAREVERT;
+                pos[2] = pu[1]; // B
+                pos[3] = pv[1]; // Q
+              } else { // s4 < 0
+                // [P, Q] overlaps [A, B] (-+--).
+                types[0] = (int) TOUCHEDGE;
+                pos[0] = pu[0]; // [A, B]
+                pos[1] = pv[0]; // P
+                types[1] = (int) ACROSSVERT;
+                pos[2] = pu[1]; // B
+                pos[3] = pv[0]; // [P, Q]
+              }
+            }
+          } else { // s2 == 0
+            // P = B (#0##).
+            types[0] = (int) SHAREVERT;
+            pos[0] = pu[1]; // B
+            pos[1] = pv[0]; // P
+            types[1] = (int) DISJOINT;
+          }
+        }
+      }
+    } else { // s1 == 0
+      // Q = A (0###).
+      types[0] = (int) SHAREVERT;
+      pos[0] = pu[0]; // A
+      pos[1] = pv[1]; // Q
+      types[1] = (int) DISJOINT;
+    }
+  }
+
+  return 4;
+}
+
+int tetgenmesh::tri_edge_tail(point A,point B,point C,point P,point Q,point R,
+                              REAL sP,REAL sQ,int level,int *types,int *pos)
+{
+  point U[3], V[3]; //, Ptmp;
+  int pu[3], pv[3]; //, itmp;
+  REAL s1, s2, s3;
+  int z1;
+
+
+  if (sP < 0) {
+    if (sQ < 0) { // (--) disjoint
+      return 0;
+    } else {
+      if (sQ > 0) { // (-+)
+        SETVECTOR3(U, A, B, C);
+        SETVECTOR3(V, P, Q, R);
+        SETVECTOR3(pu, 0, 1, 2);
+        SETVECTOR3(pv, 0, 1, 2);
+        z1 = 0;
+      } else { // (-0)
+        SETVECTOR3(U, A, B, C);
+        SETVECTOR3(V, P, Q, R);
+        SETVECTOR3(pu, 0, 1, 2);
+        SETVECTOR3(pv, 0, 1, 2);
+        z1 = 1;
+      }
+    }
+  } else {
+    if (sP > 0) { // (+-)
+      if (sQ < 0) {
+        SETVECTOR3(U, A, B, C);
+        SETVECTOR3(V, Q, P, R);  // P and Q are flipped.
+        SETVECTOR3(pu, 0, 1, 2);
+        SETVECTOR3(pv, 1, 0, 2);
+        z1 = 0;
+      } else {
+        if (sQ > 0) { // (++) disjoint
+          return 0;
+        } else { // (+0)
+          SETVECTOR3(U, B, A, C); // A and B are flipped.
+          SETVECTOR3(V, P, Q, R);
+          SETVECTOR3(pu, 1, 0, 2);
+          SETVECTOR3(pv, 0, 1, 2);
+          z1 = 1;
+        }
+      }
+    } else { // sP == 0
+      if (sQ < 0) { // (0-)
+        SETVECTOR3(U, A, B, C);
+        SETVECTOR3(V, Q, P, R);  // P and Q are flipped.
+        SETVECTOR3(pu, 0, 1, 2);
+        SETVECTOR3(pv, 1, 0, 2);
+        z1 = 1;
+      } else {
+        if (sQ > 0) { // (0+)
+          SETVECTOR3(U, B, A, C);  // A and B are flipped.
+          SETVECTOR3(V, Q, P, R);  // P and Q are flipped.
+          SETVECTOR3(pu, 1, 0, 2);
+          SETVECTOR3(pv, 1, 0, 2);
+          z1 = 1;
+        } else { // (00)
+          // A, B, C, P, and Q are coplanar.
+          z1 = 2;
+        }
+      }
+    }
+  }
+
+  if (z1 == 2) {
+    // The triangle and the edge are coplanar.
+    return tri_edge_2d(A, B, C, P, Q, R, level, types, pos);
+  }
+
+  s1 = orient3d(U[0], U[1], V[0], V[1]);
+  if (s1 < 0) {
+    return 0;
+  }
+
+  s2 = orient3d(U[1], U[2], V[0], V[1]);
+  if (s2 < 0) {
+    return 0;
+  }
+
+  s3 = orient3d(U[2], U[0], V[0], V[1]);
+  if (s3 < 0) {
+    return 0;
+  }
+
+  if (level == 0) {
+    return 1;  // The are intersected.
+  }
+
+  types[1] = (int) DISJOINT; // No second intersection point.
+
+  if (z1 == 0) {
+    if (s1 > 0) {
+      if (s2 > 0) {
+        if (s3 > 0) { // (+++)
+          // [P, Q] passes interior of [A, B, C].
+          types[0] = (int) ACROSSFACE;
+          pos[0] = 3;  // interior of [A, B, C]
+          pos[1] = 0;  // [P, Q]
+        } else { // s3 == 0 (++0)
+          // [P, Q] intersects [C, A].
+          types[0] = (int) ACROSSEDGE;
+          pos[0] = pu[2];  // [C, A]
+          pos[1] = 0;  // [P, Q]
+        }
+      } else { // s2 == 0
+        if (s3 > 0) { // (+0+)
+          // [P, Q] intersects [B, C].
+          types[0] = (int) ACROSSEDGE;
+          pos[0] = pu[1];  // [B, C]
+          pos[1] = 0;  // [P, Q]
+        } else { // s3 == 0 (+00)
+          // [P, Q] passes C.
+          types[0] = (int) ACROSSVERT;
+          pos[0] = pu[2];  // C
+          pos[1] = 0;  // [P, Q]
+        }
+      }
+    } else { // s1 == 0
+      if (s2 > 0) {
+        if (s3 > 0) { // (0++)
+          // [P, Q] intersects [A, B].
+          types[0] = (int) ACROSSEDGE;
+          pos[0] = pu[0];  // [A, B]
+          pos[1] = 0;  // [P, Q]
+        } else { // s3 == 0 (0+0)
+          // [P, Q] passes A.
+          types[0] = (int) ACROSSVERT;
+          pos[0] = pu[0];  // A
+          pos[1] = 0;  // [P, Q]
+        }
+      } else { // s2 == 0
+        if (s3 > 0) { // (00+)
+          // [P, Q] passes B.
+          types[0] = (int) ACROSSVERT;
+          pos[0] = pu[1];  // B
+          pos[1] = 0;  // [P, Q]
+        }
+      }
+    }
+  } else { // z1 == 1
+    if (s1 > 0) {
+      if (s2 > 0) {
+        if (s3 > 0) { // (+++)
+          // Q lies in [A, B, C].
+          types[0] = (int) TOUCHFACE;
+          pos[0] = 0; // [A, B, C]
+          pos[1] = pv[1]; // Q
+        } else { // s3 == 0 (++0)
+          // Q lies on [C, A].
+          types[0] = (int) TOUCHEDGE;
+          pos[0] = pu[2]; // [C, A]
+          pos[1] = pv[1]; // Q
+        }
+      } else { // s2 == 0
+        if (s3 > 0) { // (+0+)
+          // Q lies on [B, C].
+          types[0] = (int) TOUCHEDGE;
+          pos[0] = pu[1]; // [B, C]
+          pos[1] = pv[1]; // Q
+        } else { // s3 == 0 (+00)
+          // Q = C.
+          types[0] = (int) SHAREVERT;
+          pos[0] = pu[2]; // C
+          pos[1] = pv[1]; // Q
+        }
+      }
+    } else { // s1 == 0
+      if (s2 > 0) {
+        if (s3 > 0) { // (0++)
+          // Q lies on [A, B].
+          types[0] = (int) TOUCHEDGE;
+          pos[0] = pu[0]; // [A, B]
+          pos[1] = pv[1]; // Q
+        } else { // s3 == 0 (0+0)
+          // Q = A.
+          types[0] = (int) SHAREVERT;
+          pos[0] = pu[0]; // A
+          pos[1] = pv[1]; // Q
+        }
+      } else { // s2 == 0
+        if (s3 > 0) { // (00+)
+          // Q = B.
+          types[0] = (int) SHAREVERT;
+          pos[0] = pu[1]; // B
+          pos[1] = pv[1]; // Q
+        }
+      }
+    }
+  }
+
+  // T and E intersect in a single point.
+  return 2;
+}
+
+int tetgenmesh::tri_edge_test(point A, point B, point C, point P, point Q, 
+                              point R, int level, int *types, int *pos)
+{
+  REAL sP, sQ;
+
+  // Test the locations of P and Q with respect to ABC.
+  sP = orient3d(A, B, C, P);
+  sQ = orient3d(A, B, C, Q);
+
+  return tri_edge_tail(A, B, C, P, Q, R, sP, sQ, level, types, pos);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// tri_tri_inter()    Test whether two triangle (abc) and (opq) are          //
+//                    intersecting or not.                                   //
+//                                                                           //
+// Return 0 if they are disjoint. Otherwise, return 1. 'type' returns one of //
+// the four cases: SHAREVERTEX, SHAREEDGE, SHAREFACE, and INTERSECT.         //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+int tetgenmesh::tri_edge_inter_tail(REAL* A, REAL* B, REAL* C,  REAL* P, 
+                                    REAL* Q, REAL s_p, REAL s_q)
+{
+  int types[2], pos[4];
+  int ni;  // =0, 2, 4
+
+  ni = tri_edge_tail(A, B, C, P, Q, NULL, s_p, s_q, 1, types, pos);
+
+  if (ni > 0) {
+    if (ni == 2) {
+      // Get the intersection type.
+      if (types[0] == (int) SHAREVERT) {
+        return (int) SHAREVERT;
+      } else {
+        return (int) INTERSECT;
+      }
+    } else if (ni == 4) { 
+      // There may be two intersections.
+      if (types[0] == (int) SHAREVERT) {
+        if (types[1] == (int) DISJOINT) {
+          return (int) SHAREVERT;
+        } else {
+          return (int) INTERSECT;
+        }
+      } else {
+        if (types[0] == (int) SHAREEDGE) {
+          return (int) SHAREEDGE;
+        } else {
+          return (int) INTERSECT;
+        }
+      }
+    }
+  }
+
+  return (int) DISJOINT;
+}
+
+int tetgenmesh::tri_tri_inter(REAL* A,REAL* B,REAL* C,REAL* O,REAL* P,REAL* Q)
+{
+  REAL s_o, s_p, s_q;
+  REAL s_a, s_b, s_c;
+
+  s_o = orient3d(A, B, C, O);
+  s_p = orient3d(A, B, C, P);
+  s_q = orient3d(A, B, C, Q);
+  if ((s_o * s_p > 0.0) && (s_o * s_q > 0.0)) {
+    // o, p, q are all in the same halfspace of ABC.
+    return 0; // DISJOINT;
+  }
+
+  s_a = orient3d(O, P, Q, A);
+  s_b = orient3d(O, P, Q, B);
+  s_c = orient3d(O, P, Q, C);
+  if ((s_a * s_b > 0.0) && (s_a * s_c > 0.0)) {
+    // a, b, c are all in the same halfspace of OPQ.
+    return 0; // DISJOINT;
+  }
+
+  int abcop, abcpq, abcqo;
+  int shareedge = 0;
+
+  abcop = tri_edge_inter_tail(A, B, C, O, P, s_o, s_p);
+  if (abcop == (int) INTERSECT) {
+    return (int) INTERSECT;
+  } else if (abcop == (int) SHAREEDGE) {
+    shareedge++;
+  }
+  abcpq = tri_edge_inter_tail(A, B, C, P, Q, s_p, s_q);
+  if (abcpq == (int) INTERSECT) {
+    return (int) INTERSECT;
+  } else if (abcpq == (int) SHAREEDGE) {
+    shareedge++;
+  }
+  abcqo = tri_edge_inter_tail(A, B, C, Q, O, s_q, s_o);
+  if (abcqo == (int) INTERSECT) {
+    return (int) INTERSECT;
+  } else if (abcqo == (int) SHAREEDGE) {
+    shareedge++;
+  }
+  if (shareedge == 3) {
+    // opq are coincident with abc.
+    return (int) SHAREFACE;
+  }
+
+  // Continue to detect whether opq and abc are intersecting or not.
+  int opqab, opqbc, opqca;
+
+  opqab = tri_edge_inter_tail(O, P, Q, A, B, s_a, s_b);
+  if (opqab == (int) INTERSECT) {
+    return (int) INTERSECT;
+  }
+  opqbc = tri_edge_inter_tail(O, P, Q, B, C, s_b, s_c);
+  if (opqbc == (int) INTERSECT) {
+    return (int) INTERSECT;
+  }
+  opqca = tri_edge_inter_tail(O, P, Q, C, A, s_c, s_a);
+  if (opqca == (int) INTERSECT) {
+    return (int) INTERSECT;
+  }
+
+  // At this point, two triangles are not intersecting and not coincident.
+  //   They may be share an edge, or share a vertex, or disjoint.
+  if (abcop == (int) SHAREEDGE) {
+    // op is coincident with an edge of abc.
+    return (int) SHAREEDGE;
+  }
+  if (abcpq == (int) SHAREEDGE) {
+    // pq is coincident with an edge of abc.
+    return (int) SHAREEDGE;
+  }
+  if (abcqo == (int) SHAREEDGE) {
+    // qo is coincident with an edge of abc.
+    return (int) SHAREEDGE;
+  }
+
+  // They may share a vertex or disjoint.
+  if (abcop == (int) SHAREVERT) {
+    return (int) SHAREVERT;
+  }
+  if (abcpq == (int) SHAREVERT) {
+    // q is the coincident vertex.
+    return (int) SHAREVERT;
+  }
+
+  // They are disjoint.
+  return (int) DISJOINT;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// lu_decmp()    Compute the LU decomposition of a matrix.                   //
+//                                                                           //
+// Compute the LU decomposition of a (non-singular) square matrix A using    //
+// partial pivoting and implicit row exchanges.  The result is:              //
+//     A = P * L * U,                                                        //
+// where P is a permutation matrix, L is unit lower triangular, and U is     //
+// upper triangular.  The factored form of A is used in combination with     //
+// 'lu_solve()' to solve linear equations: Ax = b, or invert a matrix.       //
+//                                                                           //
+// The inputs are a square matrix 'lu[N..n+N-1][N..n+N-1]', it's size is 'n'.//
+// On output, 'lu' is replaced by the LU decomposition of a rowwise permuta- //
+// tion of itself, 'ps[N..n+N-1]' is an output vector that records the row   //
+// permutation effected by the partial pivoting, effectively,  'ps' array    //
+// tells the user what the permutation matrix P is; 'd' is output as +1/-1   //
+// depending on whether the number of row interchanges was even or odd,      //
+// respectively.                                                             //
+//                                                                           //
+// Return true if the LU decomposition is successfully computed, otherwise,  //
+// return false in case that A is a singular matrix.                         //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+bool tetgenmesh::lu_decmp(REAL lu[4][4], int n, int* ps, REAL* d, int N)
+{
+  REAL scales[4];
+  REAL pivot, biggest, mult, tempf;
+  int pivotindex = 0;
+  int i, j, k;
+
+  *d = 1.0;                                      // No row interchanges yet.
+
+  for (i = N; i < n + N; i++) {                             // For each row.
+    // Find the largest element in each row for row equilibration
+    biggest = 0.0;
+    for (j = N; j < n + N; j++)
+      if (biggest < (tempf = fabs(lu[i][j])))
+        biggest  = tempf;
+    if (biggest != 0.0)
+      scales[i] = 1.0 / biggest;
+    else {
+      scales[i] = 0.0;
+      return false;                            // Zero row: singular matrix.
+    }
+    ps[i] = i;                                 // Initialize pivot sequence.
+  }
+
+  for (k = N; k < n + N - 1; k++) {                      // For each column.
+    // Find the largest element in each column to pivot around.
+    biggest = 0.0;
+    for (i = k; i < n + N; i++) {
+      if (biggest < (tempf = fabs(lu[ps[i]][k]) * scales[ps[i]])) {
+        biggest = tempf;
+        pivotindex = i;
+      }
+    }
+    if (biggest == 0.0) {
+      return false;                         // Zero column: singular matrix.
+    }
+    if (pivotindex != k) {                         // Update pivot sequence.
+      j = ps[k];
+      ps[k] = ps[pivotindex];
+      ps[pivotindex] = j;
+      *d = -(*d);                          // ...and change the parity of d.
+    }
+
+    // Pivot, eliminating an extra variable  each time
+    pivot = lu[ps[k]][k];
+    for (i = k + 1; i < n + N; i++) {
+      lu[ps[i]][k] = mult = lu[ps[i]][k] / pivot;
+      if (mult != 0.0) {
+        for (j = k + 1; j < n + N; j++)
+          lu[ps[i]][j] -= mult * lu[ps[k]][j];
+      }
+    }
+  }
+
+  // (lu[ps[n + N - 1]][n + N - 1] == 0.0) ==> A is singular.
+  return lu[ps[n + N - 1]][n + N - 1] != 0.0;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// lu_solve()    Solves the linear equation:  Ax = b,  after the matrix A    //
+//               has been decomposed into the lower and upper triangular     //
+//               matrices L and U, where A = LU.                             //
+//                                                                           //
+// 'lu[N..n+N-1][N..n+N-1]' is input, not as the matrix 'A' but rather as    //
+// its LU decomposition, computed by the routine 'lu_decmp'; 'ps[N..n+N-1]'  //
+// is input as the permutation vector returned by 'lu_decmp';  'b[N..n+N-1]' //
+// is input as the right-hand side vector, and returns with the solution     //
+// vector. 'lu', 'n', and 'ps' are not modified by this routine and can be   //
+// left in place for successive calls with different right-hand sides 'b'.   //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::lu_solve(REAL lu[4][4], int n, int* ps, REAL* b, int N)
+{
+  int i, j;
+  REAL X[4], dot;
+
+  for (i = N; i < n + N; i++) X[i] = 0.0;
+
+  // Vector reduction using U triangular matrix.
+  for (i = N; i < n + N; i++) {
+    dot = 0.0;
+    for (j = N; j < i + N; j++)
+      dot += lu[ps[i]][j] * X[j];
+    X[i] = b[ps[i]] - dot;
+  }
+
+  // Back substitution, in L triangular matrix.
+  for (i = n + N - 1; i >= N; i--) {
+    dot = 0.0;
+    for (j = i + 1; j < n + N; j++)
+      dot += lu[ps[i]][j] * X[j];
+    X[i] = (X[i] - dot) / lu[ps[i]][i];
+  }
+
+  for (i = N; i < n + N; i++) b[i] = X[i];
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// incircle3d()    3D in-circle test.                                        //
+//                                                                           //
+// Return a negative value if pd is inside the circumcircle of the triangle  //
+// pa, pb, and pc.                                                           //
+//                                                                           //
+// IMPORTANT: It assumes that [a,b] is the common edge, i.e., the two input  //
+// triangles are [a,b,c] and [b,a,d].                                        //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+REAL tetgenmesh::incircle3d(point pa, point pb, point pc, point pd)
+{
+  REAL area2[2], n1[3], n2[3], c[3];
+  REAL sign, r, d;
+
+  // Calculate the areas of the two triangles [a, b, c] and [b, a, d].
+  facenormal(pa, pb, pc, n1, 1, NULL);
+  area2[0] = dot(n1, n1);
+  facenormal(pb, pa, pd, n2, 1, NULL);
+  area2[1] = dot(n2, n2);
+
+  if (area2[0] > area2[1]) {
+    // Choose [a, b, c] as the base triangle.
+    circumsphere(pa, pb, pc, NULL, c, &r);
+    d = distance(c, pd);
+  } else {
+    // Choose [b, a, d] as the base triangle.
+    if (area2[1] > 0) {
+      circumsphere(pb, pa, pd, NULL, c, &r);
+      d = distance(c, pc);
+    } else {
+      // The four points are collinear. This case only happens on the boundary.
+      return 0; // Return "not inside".
+    }
+  }
+
+  sign = d - r;
+  if (fabs(sign) / r < b->epsilon) {
+    sign = 0;
+  }
+
+  return sign;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// facenormal()    Calculate the normal of the face.                         //
+//                                                                           //
+// The normal of the face abc can be calculated by the cross product of 2 of //
+// its 3 edge vectors.  A better choice of two edge vectors will reduce the  //
+// numerical error during the calculation.  Burdakov proved that the optimal //
+// basis problem is equivalent to the minimum spanning tree problem with the //
+// edge length be the functional, see Burdakov, "A greedy algorithm for the  //
+// optimal basis problem", BIT 37:3 (1997), 591-599. If 'pivot' > 0, the two //
+// short edges in abc are chosen for the calculation.                        //
+//                                                                           //
+// If 'lav' is not NULL and if 'pivot' is set, the average edge length of    //
+// the edges of the face [a,b,c] is returned.                                //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::facenormal(point pa, point pb, point pc, REAL *n, int pivot,
+                            REAL* lav)
+{
+  REAL v1[3], v2[3], v3[3], *pv1, *pv2;
+  REAL L1, L2, L3;
+
+  v1[0] = pb[0] - pa[0];  // edge vector v1: a->b
+  v1[1] = pb[1] - pa[1];
+  v1[2] = pb[2] - pa[2];
+  v2[0] = pa[0] - pc[0];  // edge vector v2: c->a
+  v2[1] = pa[1] - pc[1];
+  v2[2] = pa[2] - pc[2];
+
+  // Default, normal is calculated by: v1 x (-v2) (see Fig. fnormal).
+  if (pivot > 0) {
+    // Choose edge vectors by Burdakov's algorithm.
+    v3[0] = pc[0] - pb[0];  // edge vector v3: b->c
+    v3[1] = pc[1] - pb[1];
+    v3[2] = pc[2] - pb[2];
+    L1 = dot(v1, v1);
+    L2 = dot(v2, v2);
+    L3 = dot(v3, v3);
+    // Sort the three edge lengths.
+    if (L1 < L2) {
+      if (L2 < L3) {
+        pv1 = v1; pv2 = v2; // n = v1 x (-v2).
+      } else {
+        pv1 = v3; pv2 = v1; // n = v3 x (-v1).
+      }
+    } else {
+      if (L1 < L3) {
+        pv1 = v1; pv2 = v2; // n = v1 x (-v2).
+      } else {
+        pv1 = v2; pv2 = v3; // n = v2 x (-v3).
+      }
+    }
+    if (lav) {
+      // return the average edge length.
+      *lav = (sqrt(L1) + sqrt(L2) + sqrt(L3)) / 3.0;
+    }
+  } else {
+    pv1 = v1; pv2 = v2; // n = v1 x (-v2).
+  }
+
+  // Calculate the face normal.
+  cross(pv1, pv2, n);
+  // Inverse the direction;
+  n[0] = -n[0];
+  n[1] = -n[1];
+  n[2] = -n[2];
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// shortdistance()    Returns the shortest distance from point p to a line   //
+//                    defined by two points e1 and e2.                       //
+//                                                                           //
+// First compute the projection length l_p of the vector v1 = p - e1 along   //
+// the vector v2 = e2 - e1. Then Pythagoras' Theorem is used to compute the  //
+// shortest distance.                                                        //
+//                                                                           //
+// This routine allows that p is collinear with the line. In this case, the  //
+// return value is zero. The two points e1 and e2 should not be identical.   //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+REAL tetgenmesh::shortdistance(REAL* p, REAL* e1, REAL* e2)
+{
+  REAL v1[3], v2[3];
+  REAL len, l_p;
+
+  v1[0] = e2[0] - e1[0];
+  v1[1] = e2[1] - e1[1];
+  v1[2] = e2[2] - e1[2];
+  v2[0] = p[0] - e1[0];
+  v2[1] = p[1] - e1[1];
+  v2[2] = p[2] - e1[2];
+
+  len = sqrt(dot(v1, v1));
+
+  v1[0] /= len;
+  v1[1] /= len;
+  v1[2] /= len;
+  l_p = dot(v1, v2);
+
+  return sqrt(dot(v2, v2) - l_p * l_p);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// triarea()    Return the area of a triangle.                               //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+REAL tetgenmesh::triarea(REAL* pa, REAL* pb, REAL* pc)
+{
+  REAL A[4][4];  
+
+  // Compute the coefficient matrix A (3x3).
+  A[0][0] = pb[0] - pa[0];
+  A[0][1] = pb[1] - pa[1];
+  A[0][2] = pb[2] - pa[2]; // vector V1 (pa->pb)
+  A[1][0] = pc[0] - pa[0];
+  A[1][1] = pc[1] - pa[1];
+  A[1][2] = pc[2] - pa[2]; // vector V2 (pa->pc)
+
+  cross(A[0], A[1], A[2]); // vector V3 (V1 X V2)
+
+  return 0.5 * sqrt(dot(A[2], A[2])); // The area of [a,b,c].
+}
+
+REAL tetgenmesh::orient3dfast(REAL *pa, REAL *pb, REAL *pc, REAL *pd)
+{
+  REAL adx, bdx, cdx;
+  REAL ady, bdy, cdy;
+  REAL adz, bdz, cdz;
+
+  adx = pa[0] - pd[0];
+  bdx = pb[0] - pd[0];
+  cdx = pc[0] - pd[0];
+  ady = pa[1] - pd[1];
+  bdy = pb[1] - pd[1];
+  cdy = pc[1] - pd[1];
+  adz = pa[2] - pd[2];
+  bdz = pb[2] - pd[2];
+  cdz = pc[2] - pd[2];
+
+  return adx * (bdy * cdz - bdz * cdy)
+       + bdx * (cdy * adz - cdz * ady)
+       + cdx * (ady * bdz - adz * bdy);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// interiorangle()    Return the interior angle (0 - 2 * PI) between vectors //
+//                    o->p1 and o->p2.                                       //
+//                                                                           //
+// 'n' is the normal of the plane containing face (o, p1, p2).  The interior //
+// angle is the total angle rotating from o->p1 around n to o->p2.  Exchange //
+// the position of p1 and p2 will get the complement angle of the other one. //
+// i.e., interiorangle(o, p1, p2) = 2 * PI - interiorangle(o, p2, p1).  Set  //
+// 'n' be NULL if you only want the interior angle between 0 - PI.           //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+REAL tetgenmesh::interiorangle(REAL* o, REAL* p1, REAL* p2, REAL* n)
+{
+  REAL v1[3], v2[3], np[3];
+  REAL theta, costheta, lenlen;
+  REAL ori, len1, len2;
+
+  // Get the interior angle (0 - PI) between o->p1, and o->p2.
+  v1[0] = p1[0] - o[0];
+  v1[1] = p1[1] - o[1];
+  v1[2] = p1[2] - o[2];
+  v2[0] = p2[0] - o[0];
+  v2[1] = p2[1] - o[1];
+  v2[2] = p2[2] - o[2];
+  len1 = sqrt(dot(v1, v1));
+  len2 = sqrt(dot(v2, v2));
+  lenlen = len1 * len2;
+
+  costheta = dot(v1, v2) / lenlen;
+  if (costheta > 1.0) {
+    costheta = 1.0; // Roundoff. 
+  } else if (costheta < -1.0) {
+    costheta = -1.0; // Roundoff. 
+  }
+  theta = acos(costheta);
+  if (n != NULL) {
+    // Get a point above the face (o, p1, p2);
+    np[0] = o[0] + n[0];
+    np[1] = o[1] + n[1];
+    np[2] = o[2] + n[2];
+    // Adjust theta (0 - 2 * PI).
+    ori = orient3d(p1, o, np, p2);
+    if (ori > 0.0) {
+      theta = 2 * PI - theta;
+    }
+  }
+
+  return theta;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// projpt2edge()    Return the projection point from a point to an edge.     //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::projpt2edge(REAL* p, REAL* e1, REAL* e2, REAL* prj)
+{
+  REAL v1[3], v2[3];
+  REAL len, l_p;
+
+  v1[0] = e2[0] - e1[0];
+  v1[1] = e2[1] - e1[1];
+  v1[2] = e2[2] - e1[2];
+  v2[0] = p[0] - e1[0];
+  v2[1] = p[1] - e1[1];
+  v2[2] = p[2] - e1[2];
+
+  len = sqrt(dot(v1, v1));
+  v1[0] /= len;
+  v1[1] /= len;
+  v1[2] /= len;
+  l_p = dot(v1, v2);
+
+  prj[0] = e1[0] + l_p * v1[0];
+  prj[1] = e1[1] + l_p * v1[1];
+  prj[2] = e1[2] + l_p * v1[2];
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// projpt2face()    Return the projection point from a point to a face.      //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::projpt2face(REAL* p, REAL* f1, REAL* f2, REAL* f3, REAL* prj)
+{
+  REAL fnormal[3], v1[3];
+  REAL len, dist;
+
+  // Get the unit face normal.
+  facenormal(f1, f2, f3, fnormal, 1, NULL);
+  len = sqrt(fnormal[0]*fnormal[0] + fnormal[1]*fnormal[1] + 
+             fnormal[2]*fnormal[2]);
+  fnormal[0] /= len;
+  fnormal[1] /= len;
+  fnormal[2] /= len;
+  // Get the vector v1 = |p - f1|.
+  v1[0] = p[0] - f1[0];
+  v1[1] = p[1] - f1[1];
+  v1[2] = p[2] - f1[2];
+  // Get the project distance.
+  dist = dot(fnormal, v1);
+  
+  // Get the project point.
+  prj[0] = p[0] - dist * fnormal[0];
+  prj[1] = p[1] - dist * fnormal[1];
+  prj[2] = p[2] - dist * fnormal[2];
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// tetalldihedral()    Get all (six) dihedral angles of a tet.               //
+//                                                                           //
+// If 'cosdd' is not NULL, it returns the cosines of the 6 dihedral angles,  //
+// the edge indices are given in the global array 'edge2ver'. If 'cosmaxd'   //
+// (or 'cosmind') is not NULL, it returns the cosine of the maximal (or      //
+// minimal) dihedral angle.                                                  //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+bool tetgenmesh::tetalldihedral(point pa, point pb, point pc, point pd,
+                                REAL* cosdd, REAL* cosmaxd, REAL* cosmind)
+{
+  REAL N[4][3], vol, cosd, len;
+  int f1 = 0, f2 = 0, i, j;
+
+  vol = 0; // Check if the tet is valid or not.
+
+  // Get four normals of faces of the tet.
+  tetallnormal(pa, pb, pc, pd, N, &vol);
+
+  if (vol > 0) {
+    // Normalize the normals.
+    for (i = 0; i < 4; i++) {
+      len = sqrt(dot(N[i], N[i]));
+      if (len != 0.0) {
+        for (j = 0; j < 3; j++) N[i][j] /= len;
+      } else {
+        // There are degeneracies, such as duplicated vertices.
+        vol = 0; //assert(0);
+      }
+    }
+  }
+
+  if (vol <= 0) { // if (vol == 0.0) {
+    // A degenerated tet or an inverted tet.
+    facenormal(pc, pb, pd, N[0], 1, NULL);
+    facenormal(pa, pc, pd, N[1], 1, NULL);
+    facenormal(pb, pa, pd, N[2], 1, NULL);
+    facenormal(pa, pb, pc, N[3], 1, NULL);
+    // Normalize the normals.
+    for (i = 0; i < 4; i++) {
+      len = sqrt(dot(N[i], N[i]));
+      if (len != 0.0) {
+        for (j = 0; j < 3; j++) N[i][j] /= len;
+      } else {
+        // There are degeneracies, such as duplicated vertices.
+        break; // Not a valid normal.
+      }
+    }
+    if (i < 4) {
+      // Do not calculate dihedral angles.
+      // Set all angles be 0 degree. There will be no quality optimization for
+      //   this tet! Use volume optimization to correct it.
+      if (cosdd != NULL) {
+        for (i = 0; i < 6; i++) {
+          cosdd[i] = -1.0; // 180 degree.
+        }
+      }
+      // This tet has zero volume.
+      if (cosmaxd != NULL) {
+        *cosmaxd = -1.0; // 180 degree.
+      }
+      if (cosmind != NULL) {
+        *cosmind = -1.0; // 180 degree.
+      }
+      return false;
+    }
+  }
+
+  // Calculate the cosine of the dihedral angles of the edges.
+  for (i = 0; i < 6; i++) {
+    switch (i) {
+    case 0: f1 = 0; f2 = 1; break; // [c,d].
+    case 1: f1 = 1; f2 = 2; break; // [a,d].
+    case 2: f1 = 2; f2 = 3; break; // [a,b].
+    case 3: f1 = 0; f2 = 3; break; // [b,c].
+    case 4: f1 = 2; f2 = 0; break; // [b,d].
+    case 5: f1 = 1; f2 = 3; break; // [a,c].
+    }
+    cosd = -dot(N[f1], N[f2]);
+    if (cosd < -1.0) cosd = -1.0; // Rounding.
+    if (cosd >  1.0) cosd =  1.0; // Rounding.
+    if (cosdd) cosdd[i] = cosd;
+    if (cosmaxd || cosmind) {
+      if (i == 0) {
+        if (cosmaxd) *cosmaxd = cosd;
+        if (cosmind) *cosmind = cosd;
+      } else {
+        if (cosmaxd) *cosmaxd = cosd < *cosmaxd ? cosd : *cosmaxd;
+        if (cosmind) *cosmind = cosd > *cosmind ? cosd : *cosmind;
+      }
+    }
+  }
+
+  return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// tetallnormal()    Get the in-normals of the four faces of a given tet.    //
+//                                                                           //
+// Let tet be abcd. N[4][3] returns the four normals, which are: N[0] cbd,   //
+// N[1] acd, N[2] bad, N[3] abc (exactly corresponding to the face indices   //
+// of the mesh data structure). These normals are unnormalized.              //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::tetallnormal(point pa, point pb, point pc, point pd,
+                              REAL N[4][3], REAL* volume)
+{
+  REAL A[4][4], rhs[4], D;
+  int indx[4];
+  int i, j;
+
+  // get the entries of A[3][3].
+  for (i = 0; i < 3; i++) A[0][i] = pa[i] - pd[i];  // d->a vec
+  for (i = 0; i < 3; i++) A[1][i] = pb[i] - pd[i];  // d->b vec
+  for (i = 0; i < 3; i++) A[2][i] = pc[i] - pd[i];  // d->c vec
+
+  // Compute the inverse of matrix A, to get 3 normals of the 4 faces.
+  if (lu_decmp(A, 3, indx, &D, 0)) { // Decompose the matrix just once.
+    if (volume != NULL) {
+      // Get the volume of the tet.
+      *volume = fabs((A[indx[0]][0] * A[indx[1]][1] * A[indx[2]][2])) / 6.0;
+    }
+    for (j = 0; j < 3; j++) {
+      for (i = 0; i < 3; i++) rhs[i] = 0.0;
+      rhs[j] = 1.0;  // Positive means the inside direction
+      lu_solve(A, 3, indx, rhs, 0);
+      for (i = 0; i < 3; i++) N[j][i] = rhs[i];
+    }
+    // Get the fourth normal by summing up the first three.
+    for (i = 0; i < 3; i++) N[3][i] = - N[0][i] - N[1][i] - N[2][i];
+  } else {
+    // The tet is degenerated.
+    if (volume != NULL) {
+      *volume = 0;
+    }
+  }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// tetaspectratio()    Calculate the aspect ratio of the tetrahedron.        //
+//                                                                           //
+// The aspect ratio of a tet is L/h, where L is the longest edge length, and //
+// h is the shortest height of the tet.                                      //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+REAL tetgenmesh::tetaspectratio(point pa, point pb, point pc, point pd)
+{
+  REAL V[6][3], edgelength[6], longlen;
+  REAL vda[3], vdb[3], vdc[3];
+  REAL N[4][3], A[4][4], rhs[4], D;
+  REAL H[4], volume, minheightinv;
+  int indx[4];
+  int i, j; 
+
+  // Set the edge vectors: V[0], ..., V[5]
+  for (i = 0; i < 3; i++) V[0][i] = pa[i] - pd[i]; 
+  for (i = 0; i < 3; i++) V[1][i] = pb[i] - pd[i]; 
+  for (i = 0; i < 3; i++) V[2][i] = pc[i] - pd[i]; 
+  for (i = 0; i < 3; i++) V[3][i] = pb[i] - pa[i]; 
+  for (i = 0; i < 3; i++) V[4][i] = pc[i] - pb[i]; 
+  for (i = 0; i < 3; i++) V[5][i] = pa[i] - pc[i]; 
+
+  // Get the squares of the edge lengths.
+  for (i = 0; i < 6; i++) edgelength[i] = dot(V[i], V[i]);
+
+  // Calculate the longest and shortest edge length.
+  longlen = edgelength[0];
+  for (i = 1; i < 6; i++) {
+    longlen  = edgelength[i] > longlen ? edgelength[i] : longlen;
+  }
+
+  // Set the matrix A = [vda, vdb, vdc]^T.
+  for (i = 0; i < 3; i++) A[0][i] = vda[i] = pa[i] - pd[i];
+  for (i = 0; i < 3; i++) A[1][i] = vdb[i] = pb[i] - pd[i];
+  for (i = 0; i < 3; i++) A[2][i] = vdc[i] = pc[i] - pd[i];
+  // Lu-decompose the matrix A.
+  lu_decmp(A, 3, indx, &D, 0);
+  // Get the volume of abcd.
+  volume = (A[indx[0]][0] * A[indx[1]][1] * A[indx[2]][2]) / 6.0;
+  // Check if it is zero.
+  if (volume == 0.0) return 1.0e+200; // A degenerate tet.
+
+  // Compute the 4 face normals (N[0], ..., N[3]).
+  for (j = 0; j < 3; j++) {
+    for (i = 0; i < 3; i++) rhs[i] = 0.0;
+    rhs[j] = 1.0;  // Positive means the inside direction
+    lu_solve(A, 3, indx, rhs, 0);
+    for (i = 0; i < 3; i++) N[j][i] = rhs[i];
+  }
+  // Get the fourth normal by summing up the first three.
+  for (i = 0; i < 3; i++) N[3][i] = - N[0][i] - N[1][i] - N[2][i];
+  // Normalized the normals.
+  for (i = 0; i < 4; i++) {
+    // H[i] is the inverse of the height of its corresponding face.
+    H[i] = sqrt(dot(N[i], N[i]));
+    // if (H[i] > 0.0) {
+    //   for (j = 0; j < 3; j++) N[i][j] /= H[i];
+    // }
+  }
+  // Get the radius of the inscribed sphere.
+  // insradius = 1.0 / (H[0] + H[1] + H[2] + H[3]);
+  // Get the biggest H[i] (corresponding to the smallest height).
+  minheightinv = H[0];
+  for (i = 1; i < 4; i++) {
+    if (H[i] > minheightinv) minheightinv = H[i];
+  }
+
+  return sqrt(longlen) * minheightinv;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// circumsphere()    Calculate the smallest circumsphere (center and radius) //
+//                   of the given three or four points.                      //
+//                                                                           //
+// The circumsphere of four points (a tetrahedron) is unique if they are not //
+// degenerate. If 'pd = NULL', the smallest circumsphere of three points is  //
+// the diametral sphere of the triangle if they are not degenerate.          //
+//                                                                           //
+// Return TRUE if the input points are not degenerate and the circumcenter   //
+// and circumradius are returned in 'cent' and 'radius' respectively if they //
+// are not NULLs.  Otherwise, return FALSE, the four points are co-planar.   //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+bool tetgenmesh::circumsphere(REAL* pa, REAL* pb, REAL* pc, REAL* pd, 
+                              REAL* cent, REAL* radius)
+{
+  REAL A[4][4], rhs[4], D;
+  int indx[4];
+
+  // Compute the coefficient matrix A (3x3).
+  A[0][0] = pb[0] - pa[0];
+  A[0][1] = pb[1] - pa[1];
+  A[0][2] = pb[2] - pa[2];
+  A[1][0] = pc[0] - pa[0];
+  A[1][1] = pc[1] - pa[1];
+  A[1][2] = pc[2] - pa[2];
+  if (pd != NULL) {
+    A[2][0] = pd[0] - pa[0];
+    A[2][1] = pd[1] - pa[1]; 
+    A[2][2] = pd[2] - pa[2];
+  } else {
+    cross(A[0], A[1], A[2]);
+  }
+
+  // Compute the right hand side vector b (3x1).
+  rhs[0] = 0.5 * dot(A[0], A[0]);
+  rhs[1] = 0.5 * dot(A[1], A[1]);
+  if (pd != NULL) {
+    rhs[2] = 0.5 * dot(A[2], A[2]);
+  } else {
+    rhs[2] = 0.0;
+  }
+
+  // Solve the 3 by 3 equations use LU decomposition with partial pivoting
+  //   and backward and forward substitute..
+  if (!lu_decmp(A, 3, indx, &D, 0)) {
+    if (radius != (REAL *) NULL) *radius = 0.0;
+    return false;
+  }    
+  lu_solve(A, 3, indx, rhs, 0);
+  if (cent != (REAL *) NULL) {
+    cent[0] = pa[0] + rhs[0];
+    cent[1] = pa[1] + rhs[1];
+    cent[2] = pa[2] + rhs[2];
+  }
+  if (radius != (REAL *) NULL) {
+    *radius = sqrt(rhs[0] * rhs[0] + rhs[1] * rhs[1] + rhs[2] * rhs[2]);
+  }
+  return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// orthosphere()    Calulcate the orthosphere of four weighted points.       //
+//                                                                           //
+// A weighted point (p, P^2) can be interpreted as a sphere centered at the  //
+// point 'p' with a radius 'P'. The 'height' of 'p' is pheight = p[0]^2 +    //
+// p[1]^2 + p[2]^2 - P^2.                                                    //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+bool tetgenmesh::orthosphere(REAL* pa, REAL* pb, REAL* pc, REAL* pd,
+                             REAL  aheight, REAL bheight, REAL cheight, 
+                             REAL  dheight, REAL* orthocent, REAL* radius)
+{
+  REAL A[4][4], rhs[4], D;
+  int indx[4];
+
+  // Set the coefficient matrix A (4 x 4).
+  A[0][0] = 1.0; A[0][1] = pa[0]; A[0][2] = pa[1]; A[0][3] = pa[2];
+  A[1][0] = 1.0; A[1][1] = pb[0]; A[1][2] = pb[1]; A[1][3] = pb[2];
+  A[2][0] = 1.0; A[2][1] = pc[0]; A[2][2] = pc[1]; A[2][3] = pc[2];
+  A[3][0] = 1.0; A[3][1] = pd[0]; A[3][2] = pd[1]; A[3][3] = pd[2];
+
+  // Set the right hand side vector (4 x 1).
+  rhs[0] = 0.5 * aheight;
+  rhs[1] = 0.5 * bheight;
+  rhs[2] = 0.5 * cheight;
+  rhs[3] = 0.5 * dheight;
+
+  // Solve the 4 by 4 equations use LU decomposition with partial pivoting
+  //   and backward and forward substitute..
+  if (!lu_decmp(A, 4, indx, &D, 0)) {
+    if (radius != (REAL *) NULL) *radius = 0.0;
+    return false;
+  }
+  lu_solve(A, 4, indx, rhs, 0);
+
+  if (orthocent != (REAL *) NULL) {
+    orthocent[0] = rhs[1];
+    orthocent[1] = rhs[2];
+    orthocent[2] = rhs[3];
+  }
+  if (radius != (REAL *) NULL) {
+    // rhs[0] = - rheight / 2;
+    // rheight  = - 2 * rhs[0];
+    //          =  r[0]^2 + r[1]^2 + r[2]^2 - radius^2
+    // radius^2 = r[0]^2 + r[1]^2 + r[2]^2 -rheight
+    //          = r[0]^2 + r[1]^2 + r[2]^2 + 2 * rhs[0]
+    *radius = sqrt(rhs[1] * rhs[1] + rhs[2] * rhs[2] + rhs[3] * rhs[3]
+                   + 2.0 * rhs[0]);
+  }
+  return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// planelineint()    Calculate the intersection of a line and a plane.       //
+//                                                                           //
+// The equation of a plane (points P are on the plane with normal N and P3   //
+// on the plane) can be written as: N dot (P - P3) = 0. The equation of the  //
+// line (points P on the line passing through P1 and P2) can be written as:  //
+// P = P1 + u (P2 - P1). The intersection of these two occurs when:          //
+//   N dot (P1 + u (P2 - P1)) = N dot P3.                                    //
+// Solving for u gives:                                                      //
+//         N dot (P3 - P1)                                                   //
+//   u = ------------------.                                                 //
+//         N dot (P2 - P1)                                                   //
+// If the denominator is 0 then N (the normal to the plane) is perpendicular //
+// to the line.  Thus the line is either parallel to the plane and there are //
+// no solutions or the line is on the plane in which case there are an infi- //
+// nite number of solutions.                                                 //
+//                                                                           //
+// The plane is given by three points pa, pb, and pc, e1 and e2 defines the  //
+// line. If u is non-zero, The intersection point (if exists) returns in ip. //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::planelineint(REAL* pa, REAL* pb, REAL* pc, REAL* e1, REAL* e2,
+                              REAL* ip, REAL* u)
+{
+  REAL n[3], det, det1;
+
+  // Calculate N.
+  facenormal(pa, pb, pc, n, 1, NULL);
+  // Calculate N dot (e2 - e1).
+  det = n[0] * (e2[0] - e1[0]) + n[1] * (e2[1] - e1[1])
+      + n[2] * (e2[2] - e1[2]);
+  if (det != 0.0) {
+    // Calculate N dot (pa - e1)
+    det1 = n[0] * (pa[0] - e1[0]) + n[1] * (pa[1] - e1[1])
+         + n[2] * (pa[2] - e1[2]);
+    *u = det1 / det;
+    ip[0] = e1[0] + *u * (e2[0] - e1[0]);
+    ip[1] = e1[1] + *u * (e2[1] - e1[1]);
+    ip[2] = e1[2] + *u * (e2[2] - e1[2]);
+  } else {
+    *u = 0.0;
+  }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// linelineint()    Calculate the intersection(s) of two line segments.      //
+//                                                                           //
+// Calculate the line segment [P, Q] that is the shortest route between two  //
+// lines from A to B and C to D. Calculate also the values of tp and tq      //
+// where: P = A + tp (B - A), and Q = C + tq (D - C).                        //
+//                                                                           //
+// Return 1 if the line segment exists. Otherwise, return 0.                 //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+int tetgenmesh::linelineint(REAL* A, REAL* B, REAL* C, REAL* D, REAL* P, 
+		            REAL* Q, REAL* tp, REAL* tq)
+{
+  REAL vab[3], vcd[3], vca[3];
+  REAL vab_vab, vcd_vcd, vab_vcd;
+  REAL vca_vab, vca_vcd;
+  REAL det, eps;
+  int i;
+
+  for (i = 0; i < 3; i++) {
+    vab[i] = B[i] - A[i];
+    vcd[i] = D[i] - C[i];
+    vca[i] = A[i] - C[i];
+  }
+
+  vab_vab = dot(vab, vab);
+  vcd_vcd = dot(vcd, vcd);
+  vab_vcd = dot(vab, vcd);
+
+  det = vab_vab * vcd_vcd - vab_vcd * vab_vcd;
+  // Round the result.
+  eps = det / (fabs(vab_vab * vcd_vcd) + fabs(vab_vcd * vab_vcd));
+  if (eps < b->epsilon) {
+    return 0;
+  }
+
+  vca_vab = dot(vca, vab);
+  vca_vcd = dot(vca, vcd);
+
+  *tp = (vcd_vcd * (- vca_vab) + vab_vcd * vca_vcd) / det;
+  *tq = (vab_vcd * (- vca_vab) + vab_vab * vca_vcd) / det;
+
+  for (i = 0; i < 3; i++) P[i] = A[i] + (*tp) * vab[i];
+  for (i = 0; i < 3; i++) Q[i] = C[i] + (*tq) * vcd[i];
+
+  return 1;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// tetprismvol()    Calculate the volume of a tetrahedral prism in 4D.       //
+//                                                                           //
+// A tetrahedral prism is a convex uniform polychoron (four dimensional poly-//
+// tope). It has 6 polyhedral cells: 2 tetrahedra connected by 4 triangular  //
+// prisms. It has 14 faces: 8 triangular and 6 square. It has 16 edges and 8 //
+// vertices. (Wikipedia).                                                    //
+//                                                                           //
+// Let 'p0', ..., 'p3' be four affinely independent points in R^3. They form //
+// the lower tetrahedral facet of the prism.  The top tetrahedral facet is   //
+// formed by four vertices, 'p4', ..., 'p7' in R^4, which is obtained by     //
+// lifting each vertex of the lower facet into R^4 by a weight (height).  A  //
+// canonical choice of the weights is the square of Euclidean norm of of the //
+// points (vectors).                                                         //
+//                                                                           //
+//                                                                           //
+// The return value is (4!) 24 times of the volume of the tetrahedral prism. //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+REAL tetgenmesh::tetprismvol(REAL* p0, REAL* p1, REAL* p2, REAL* p3)
+{
+  REAL *p4, *p5, *p6, *p7;
+  REAL w4, w5, w6, w7;
+  REAL vol[4];
+
+  p4 = p0;
+  p5 = p1;
+  p6 = p2;
+  p7 = p3;
+
+  // TO DO: these weights can be pre-calculated!
+  w4 = dot(p0, p0);
+  w5 = dot(p1, p1);
+  w6 = dot(p2, p2);
+  w7 = dot(p3, p3);
+
+  // Calculate the volume of the tet-prism.
+  vol[0] = orient4d(p5, p6, p4, p3, p7, w5, w6, w4, 0, w7);
+  vol[1] = orient4d(p3, p6, p2, p0, p1,  0, w6,  0, 0,  0);
+  vol[2] = orient4d(p4, p6, p3, p0, p1, w4, w6,  0, 0,  0);
+  vol[3] = orient4d(p6, p5, p4, p3, p1, w6, w5, w4, 0,  0);
+
+  return fabs(vol[0]) + fabs(vol[1]) + fabs(vol[2]) + fabs(vol[3]);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// calculateabovepoint()    Calculate a point above a facet in 'dummypoint'. //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+bool tetgenmesh::calculateabovepoint(arraypool *facpoints, point *ppa,
+                                     point *ppb, point *ppc)
+{
+  point *ppt, pa, pb, pc;
+  REAL v1[3], v2[3], n[3];
+  REAL lab, len, A, area;
+  REAL x, y, z;
+  int i;
+
+  ppt = (point *) fastlookup(facpoints, 0);
+  pa = *ppt; // a is the first point.
+  pb = pc = NULL; // Avoid compiler warnings.
+
+  // Get a point b s.t. the length of [a, b] is maximal.
+  lab = 0;
+  for (i = 1; i < facpoints->objects; i++) {
+    ppt = (point *) fastlookup(facpoints, i);
+    x = (*ppt)[0] - pa[0];
+    y = (*ppt)[1] - pa[1];
+    z = (*ppt)[2] - pa[2];
+    len = x * x + y * y + z * z;
+    if (len > lab) {
+      lab = len;
+      pb = *ppt;
+    }
+  }
+  lab = sqrt(lab);
+  if (lab == 0) {
+    if (!b->quiet) {
+      printf("Warning:  All points of a facet are coincident with %d.\n",
+        pointmark(pa));
+    }
+    return false;
+  }
+
+  // Get a point c s.t. the area of [a, b, c] is maximal.
+  v1[0] = pb[0] - pa[0];
+  v1[1] = pb[1] - pa[1];
+  v1[2] = pb[2] - pa[2];
+  A = 0;
+  for (i = 1; i < facpoints->objects; i++) {
+    ppt = (point *) fastlookup(facpoints, i);
+    v2[0] = (*ppt)[0] - pa[0];
+    v2[1] = (*ppt)[1] - pa[1];
+    v2[2] = (*ppt)[2] - pa[2];
+    cross(v1, v2, n);
+    area = dot(n, n);
+    if (area > A) {
+      A = area;
+      pc = *ppt;
+    }
+  }
+  if (A == 0) {
+    // All points are collinear. No above point.
+    if (!b->quiet) {
+      printf("Warning:  All points of a facet are collinaer with [%d, %d].\n",
+        pointmark(pa), pointmark(pb));
+    }
+    return false;
+  }
+
+  // Calculate an above point of this facet.
+  facenormal(pa, pb, pc, n, 1, NULL);
+  len = sqrt(dot(n, n));
+  n[0] /= len;
+  n[1] /= len;
+  n[2] /= len;
+  lab /= 2.0; // Half the maximal length.
+  dummypoint[0] = pa[0] + lab * n[0];
+  dummypoint[1] = pa[1] + lab * n[1];
+  dummypoint[2] = pa[2] + lab * n[2];
+
+  if (ppa != NULL) {
+    // Return the three points.
+    *ppa = pa;
+    *ppb = pb;
+    *ppc = pc;
+  }
+
+  return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// Calculate an above point. It lies above the plane containing  the subface //
+//   [a,b,c], and save it in dummypoint. Moreover, the vector pa->dummypoint //
+//   is the normal of the plane.                                             //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::calculateabovepoint4(point pa, point pb, point pc, point pd)
+{
+  REAL n1[3], n2[3], *norm;
+  REAL len, len1, len2;
+
+  // Select a base.
+  facenormal(pa, pb, pc, n1, 1, NULL);
+  len1 = sqrt(dot(n1, n1));
+  facenormal(pa, pb, pd, n2, 1, NULL);
+  len2 = sqrt(dot(n2, n2));
+  if (len1 > len2) {
+    norm = n1;
+    len = len1;
+  } else {
+    norm = n2;
+    len = len2;
+  }
+  norm[0] /= len;
+  norm[1] /= len;
+  norm[2] /= len;
+  len = distance(pa, pb);
+  dummypoint[0] = pa[0] + len * norm[0];
+  dummypoint[1] = pa[1] + len * norm[1];
+  dummypoint[2] = pa[2] + len * norm[2];
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// report_overlapping_facets()    Report two overlapping facets.             //
+//                                                                           //
+// Two subfaces, f1 [a, b, c] and f2 [a, b, d], share the same edge [a, b].  //
+// 'dihedang' is the dihedral angle between these two facets. It must less   //
+// than the variable 'b->facet_overlap_angle_tol'.                           //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::report_overlapping_facets(face *f1, face *f2, REAL dihedang)
+{
+  point pa, pb, pc, pd;
+
+  pa = sorg(*f1);
+  pb = sdest(*f1);
+  pc = sapex(*f1);
+  pd = sapex(*f2);
+
+  if (pc != pd) {
+    printf("Found two %s self-intersecting facets.\n", 
+           dihedang > 0 ? "nearly" : "exactly");
+    printf("  1st: [%d, %d, %d] #%d\n", 
+	       pointmark(pa), pointmark(pb), pointmark(pc), shellmark(*f1));
+    printf("  2nd: [%d, %d, %d] #%d\n",
+	       pointmark(pa), pointmark(pb), pointmark(pd), shellmark(*f2));
+    if (dihedang > 0) {
+      printf("The dihedral angle between them is %g degree.\n", 
+             dihedang / PI * 180.0);
+      printf("Hint:  You may use -p/# to decrease the dihedral angle");
+      printf("  tolerance %g (degree).\n", b->facet_overlap_ang_tol);
+    }
+  } else {
+    if (shellmark(*f1) != shellmark(*f2)) {
+      // Two identical faces from two different facet.
+      printf("Found two overlapping facets.\n");
+    } else {
+      printf("Found two duplicated facets.\n");
+    }
+    printf("  1st: [%d, %d, %d] #%d\n", 
+	       pointmark(pa), pointmark(pb), pointmark(pc), shellmark(*f1));
+    printf("  2nd: [%d, %d, %d] #%d\n",
+	       pointmark(pa), pointmark(pb), pointmark(pd), shellmark(*f2));
+  }
+
+  // Return the information
+  sevent.e_type = 6;
+  sevent.f_marker1 = shellmark(*f1);
+  sevent.f_vertices1[0] = pointmark(pa);
+  sevent.f_vertices1[1] = pointmark(pb);
+  sevent.f_vertices1[2] = pointmark(pc);
+  sevent.f_marker2 = shellmark(*f2);
+  sevent.f_vertices2[0] = pointmark(pa);
+  sevent.f_vertices2[1] = pointmark(pb);
+  sevent.f_vertices2[2] = pointmark(pd);
+
+  terminatetetgen(this, 3);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// report_selfint_edge()    Report a self-intersection at an edge.           //
+//                                                                           //
+// The edge 'e1'->'e2' and the tetrahedron 'itet' intersect. 'dir' indicates //
+// that the edge intersects the tet at its origin vertex (ACROSSVERTEX), or  //
+// its current face (ACROSSFACE), or its current edge (ACROSSEDGE).          //
+// If 'iedge' is not NULL, it is either a segment or a subface that contains //
+// the edge 'e1'->'e2'.  It is used to report the geometry entity.           //
+//                                                                           //
+// Since it is a self-intersection, the vertex, edge or face of 'itet' that  //
+// is intersecting with this edge must be an input vertex, a segment, or a   //
+// subface, respectively.                                                    //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+int tetgenmesh::report_selfint_edge(point e1, point e2, face *iedge,
+  triface* itet, enum interresult dir)
+{
+  point forg = NULL, fdest = NULL, fapex = NULL;
+  int etype = 0, geomtag = 0, facemark = 0;
+
+  if (iedge != NULL) {
+    if (iedge->sh[5] != NULL) {
+      etype = 2;  // A subface
+      forg = e1;
+      fdest = e2;
+      fapex = sapex(*iedge);
+      facemark = shellmark(*iedge);
+    } else {
+      etype = 1;  // A segment
+      forg = farsorg(*iedge);
+      fdest = farsdest(*iedge);
+      // Get a facet containing this segment.
+      face parentsh;
+      spivot(*iedge, parentsh);
+      if (parentsh.sh != NULL) {
+        facemark = shellmark(parentsh);
+      }
+    }
+    geomtag = shellmark(*iedge);
+  }
+
+  if (dir == SHAREEDGE) {
+    // Two edges (segments) are coincide.
+    face colseg;
+    tsspivot1(*itet, colseg);
+    if (etype == 1) {
+      if (colseg.sh != iedge->sh) {
+        face parentsh;
+        spivot(colseg, parentsh);
+        printf("PLC Error:  Two segments are overlapping.\n");
+        printf("  Segment 1: [%d, %d] #%d (%d)\n", pointmark(sorg(colseg)),
+               pointmark(sdest(colseg)), shellmark(colseg),
+               parentsh.sh ? shellmark(parentsh) : 0);
+        printf("  Segment 2: [%d, %d] #%d (%d)\n", pointmark(forg),
+               pointmark(fdest), geomtag, facemark);
+        sevent.e_type = 4;
+        sevent.f_marker1 = (parentsh.sh ? shellmark(parentsh) : 0);
+        sevent.s_marker1 = shellmark(colseg);
+        sevent.f_vertices1[0] = pointmark( sorg(colseg));
+        sevent.f_vertices1[1] = pointmark(sdest(colseg));
+        sevent.f_vertices1[2] = 0;
+        sevent.f_marker2 = facemark;
+        sevent.s_marker2 = geomtag;
+        sevent.f_vertices2[0] = pointmark(forg);
+        sevent.f_vertices2[1] = pointmark(fdest);
+        sevent.f_vertices2[2] = 0;
+      } else {
+        // Two identical segments. Why report it?
+        terminatetetgen(this, 2); 
+      }
+    } else if (etype == 2) {
+      printf("PLC Error:  A segment lies in a facet.\n");
+      printf("  Segment: [%d, %d] #%d\n", pointmark(sorg(colseg)),
+             pointmark(sdest(colseg)), shellmark(colseg));
+      printf("  Facet:   [%d,%d,%d] #%d\n", pointmark(forg), 
+             pointmark(fdest), pointmark(fapex), geomtag);
+      sevent.e_type = 5;
+      sevent.f_marker1 = 0;
+      sevent.s_marker1 = shellmark(colseg);
+      sevent.f_vertices1[0] = pointmark( sorg(colseg));
+      sevent.f_vertices1[1] = pointmark(sdest(colseg));
+      sevent.f_vertices1[2] = 0;
+      sevent.f_marker2 = geomtag;
+      sevent.s_marker2 = 0;
+      sevent.f_vertices2[0] = pointmark(forg);
+      sevent.f_vertices2[1] = pointmark(fdest);
+      sevent.f_vertices2[2] = pointmark(fapex);
+    }
+  } else if (dir == SHAREFACE) {
+    // Two triangles (subfaces) are coincide.
+	face colface;
+    tspivot(*itet, colface);
+	if (etype == 2) {
+	  if (colface.sh != iedge->sh) {
+	    printf("PLC Error:  Two facets are overlapping.\n");
+		printf("  Facet 1:  [%d,%d,%d] #%d\n", pointmark(forg), 
+               pointmark(fdest), pointmark(fapex), geomtag);
+		printf("  Facet 2:  [%d,%d,%d] #%d\n", pointmark(sorg(colface)), 
+             pointmark(sdest(colface)), pointmark(sapex(colface)), 
+			 shellmark(colface));
+        sevent.e_type = 6;
+        sevent.f_marker1 = geomtag;
+        sevent.s_marker1 = 0;
+        sevent.f_vertices1[0] = pointmark(forg);
+        sevent.f_vertices1[1] = pointmark(fdest);
+        sevent.f_vertices1[2] = pointmark(fapex);
+        sevent.f_marker2 = shellmark(colface);
+        sevent.s_marker2 = 0;
+        sevent.f_vertices2[0] = pointmark(sorg(colface));
+        sevent.f_vertices2[1] = pointmark(sdest(colface));
+        sevent.f_vertices2[2] = pointmark(sapex(colface));
+	  } else {
+	    // Two identical subfaces. Why report it?
+        terminatetetgen(this, 2); 
+	  }
+	} else {
+	  terminatetetgen(this, 2);
+	}
+  } else if (dir == ACROSSVERT) {
+    point pp = dest(*itet);
+    if ((pointtype(pp) == RIDGEVERTEX) || (pointtype(pp) == FACETVERTEX)
+        || (pointtype(pp) == VOLVERTEX)) {
+      if (etype == 1) {
+        printf("PLC Error:  A vertex lies in a segment.\n");
+        printf("  Vertex:  [%d] (%g,%g,%g).\n",pointmark(pp),pp[0],pp[1],pp[2]);
+        printf("  Segment: [%d, %d] #%d (%d)\n", pointmark(forg), 
+               pointmark(fdest), geomtag, facemark);
+        sevent.e_type = 7;
+        sevent.f_marker1 = 0;
+        sevent.s_marker1 = 0;
+        sevent.f_vertices1[0] = pointmark(pp);
+        sevent.f_vertices1[1] = 0;
+        sevent.f_vertices1[2] = 0;
+        sevent.f_marker2 = facemark;
+        sevent.s_marker2 = geomtag;
+        sevent.f_vertices2[0] = pointmark(forg);
+        sevent.f_vertices2[1] = pointmark(fdest);
+        sevent.f_vertices2[2] = 0;
+        sevent.int_point[0] = pp[0];
+        sevent.int_point[1] = pp[1];
+        sevent.int_point[2] = pp[2];
+      } else if (etype == 2) {
+        printf("PLC Error:  A vertex lies in a facet.\n");
+        printf("  Vertex: [%d] (%g,%g,%g).\n",pointmark(pp),pp[0],pp[1],pp[2]);
+        printf("  Facet:  [%d,%d,%d] #%d\n", pointmark(forg), pointmark(fdest),
+               pointmark(fapex), geomtag);
+        sevent.e_type = 8;
+        sevent.f_marker1 = 0;
+        sevent.s_marker1 = 0;
+        sevent.f_vertices1[0] = pointmark(pp);
+        sevent.f_vertices1[1] = 0;
+        sevent.f_vertices1[2] = 0;
+        sevent.f_marker2 = geomtag;
+        sevent.s_marker2 = 0;
+        sevent.f_vertices2[0] = pointmark(forg);
+        sevent.f_vertices2[1] = pointmark(fdest);
+        sevent.f_vertices2[2] = pointmark(fapex);
+        sevent.int_point[0] = pp[0];
+        sevent.int_point[1] = pp[1];
+        sevent.int_point[2] = pp[2];
+      }
+    } else if (pointtype(pp) == FREESEGVERTEX) {
+      face parentseg, parentsh;
+      sdecode(point2sh(pp), parentseg);
+      spivot(parentseg, parentsh);
+      if (parentseg.sh != NULL) {
+        point p1 = farsorg(parentseg);
+        point p2 = farsdest(parentseg);
+        if (etype == 1) {
+          printf("PLC Error:  Two segments intersect at point (%g,%g,%g).\n",
+                 pp[0], pp[1], pp[2]);
+          printf("  Segment 1: [%d, %d], #%d (%d)\n", pointmark(forg), 
+                 pointmark(fdest), geomtag, facemark);
+          printf("  Segment 2: [%d, %d], #%d (%d)\n", pointmark(p1), 
+                 pointmark(p2), shellmark(parentseg),
+                 parentsh.sh ? shellmark(parentsh) : 0);
+          sevent.e_type = 1;
+          sevent.f_marker1 = facemark;
+          sevent.s_marker1 = geomtag;
+          sevent.f_vertices1[0] = pointmark(forg);
+          sevent.f_vertices1[1] = pointmark(fdest);
+          sevent.f_vertices1[2] = 0;
+          sevent.f_marker2 = (parentsh.sh ? shellmark(parentsh) : 0);
+          sevent.s_marker2 = shellmark(parentseg);
+          sevent.f_vertices2[0] = pointmark(p1);
+          sevent.f_vertices2[1] = pointmark(p2);
+          sevent.f_vertices2[2] = 0;
+          sevent.int_point[0] = pp[0];
+          sevent.int_point[1] = pp[1];
+          sevent.int_point[2] = pp[2];
+        } else if (etype == 2) {
+          printf("PLC Error:  A segment and a facet intersect at point");
+          printf(" (%g,%g,%g).\n", pp[0], pp[1], pp[2]);
+          printf("  Segment: [%d, %d], #%d (%d)\n", pointmark(p1),
+                 pointmark(p2), shellmark(parentseg),
+                 parentsh.sh ? shellmark(parentsh) : 0);
+          printf("  Facet:   [%d,%d,%d] #%d\n", pointmark(forg), 
+                 pointmark(fdest), pointmark(fapex), geomtag);
+          sevent.e_type = 2;
+          sevent.f_marker1 = (parentsh.sh ? shellmark(parentsh) : 0);
+          sevent.s_marker1 = shellmark(parentseg);
+          sevent.f_vertices1[0] = pointmark(p1);
+          sevent.f_vertices1[1] = pointmark(p2);
+          sevent.f_vertices1[2] = 0;
+          sevent.f_marker2 = geomtag;
+          sevent.s_marker2 = 0;
+          sevent.f_vertices2[0] = pointmark(forg);
+          sevent.f_vertices2[1] = pointmark(fdest);
+          sevent.f_vertices2[2] = pointmark(fapex);
+          sevent.int_point[0] = pp[0];
+          sevent.int_point[1] = pp[1];
+          sevent.int_point[2] = pp[2];
+        }
+      } else {
+        terminatetetgen(this, 2); // Report a bug.
+      }
+    } else if (pointtype(pp) == FREEFACETVERTEX) {
+      face parentsh;
+      sdecode(point2sh(pp), parentsh);
+      if (parentsh.sh != NULL) {
+        point p1 = sorg(parentsh);
+        point p2 = sdest(parentsh);
+        point p3 = sapex(parentsh);
+        if (etype == 1) {
+          printf("PLC Error:  A segment and a facet intersect at point");
+          printf(" (%g,%g,%g).\n", pp[0], pp[1], pp[2]);
+          printf("  Segment : [%d, %d], #%d (%d)\n", pointmark(forg), 
+                 pointmark(fdest), geomtag, facemark);
+          printf("  Facet   : [%d, %d, %d]  #%d.\n", pointmark(p1),
+                 pointmark(p2), pointmark(p3), shellmark(parentsh));
+          sevent.e_type = 2;
+          sevent.f_marker1 = facemark;
+          sevent.s_marker1 = geomtag;
+          sevent.f_vertices1[0] = pointmark(forg);
+          sevent.f_vertices1[1] = pointmark(fdest);
+          sevent.f_vertices1[2] = 0;
+          sevent.f_marker2 = shellmark(parentsh);
+          sevent.s_marker2 = 0;
+          sevent.f_vertices2[0] = pointmark(p1);
+          sevent.f_vertices2[1] = pointmark(p2);
+          sevent.f_vertices2[2] = pointmark(p3);
+          sevent.int_point[0] = pp[0];
+          sevent.int_point[1] = pp[1];
+          sevent.int_point[2] = pp[2];
+        } else if (etype == 2) {
+          printf("PLC Error:  Two facets intersect at point (%g,%g,%g).\n",
+                 pp[0], pp[1], pp[2]);
+          printf("  Facet 1: [%d, %d, %d] #%d.\n", pointmark(forg), 
+                 pointmark(fdest), pointmark(fapex), geomtag);
+          printf("  Facet 2: [%d, %d, %d] #%d.\n", pointmark(p1),
+                 pointmark(p2), pointmark(p3), shellmark(parentsh));
+          sevent.e_type = 3;
+          sevent.f_marker1 = geomtag;
+          sevent.s_marker1 = 0;
+          sevent.f_vertices1[0] = pointmark(forg);
+          sevent.f_vertices1[1] = pointmark(fdest);
+          sevent.f_vertices1[2] = pointmark(fapex);
+          sevent.f_marker2 = shellmark(parentsh);
+          sevent.s_marker2 = 0;
+          sevent.f_vertices2[0] = pointmark(p1);
+          sevent.f_vertices2[1] = pointmark(p2);
+          sevent.f_vertices2[2] = pointmark(p3);
+          sevent.int_point[0] = pp[0];
+          sevent.int_point[1] = pp[1];
+          sevent.int_point[2] = pp[2];
+        }
+      } else {
+        terminatetetgen(this, 2); // Report a bug.
+      }
+    } else if (pointtype(pp) == FREEVOLVERTEX) {
+      // This is not a PLC error. 
+      // We should shift the vertex.
+      // not down yet.
+      terminatetetgen(this, 2); // Report a bug.
+    } else {
+      terminatetetgen(this, 2); // Report a bug.
+    }
+    terminatetetgen(this, 3);
+  } else if (dir == ACROSSEDGE) {
+    if (issubseg(*itet)) {
+      face checkseg;
+      tsspivot1(*itet, checkseg);
+      face parentsh;
+      spivot(checkseg, parentsh);
+      // Calulcate the intersecting point.
+      point p1 = sorg(checkseg);
+      point p2 = sdest(checkseg);
+      REAL P[3], Q[3], tp = 0, tq = 0;
+      linelineint(e1, e2, p1, p2, P, Q, &tp, &tq);
+      if (etype == 1) {
+        printf("PLC Error:  Two segments intersect at point (%g,%g,%g).\n",
+               P[0], P[1], P[2]);
+        printf("  Segment 1: [%d, %d] #%d (%d)\n", pointmark(forg), 
+               pointmark(fdest), geomtag, facemark);
+        printf("  Segment 2: [%d, %d] #%d (%d)\n", pointmark(p1), 
+               pointmark(p2), shellmark(checkseg),
+               parentsh.sh ? shellmark(parentsh) : 0);
+        sevent.e_type = 1;
+        sevent.f_marker1 = facemark;
+        sevent.s_marker1 = geomtag;
+        sevent.f_vertices1[0] = pointmark(forg);
+        sevent.f_vertices1[1] = pointmark(fdest);
+        sevent.f_vertices1[2] = 0;
+        sevent.f_marker2 = (parentsh.sh ? shellmark(parentsh) : 0);
+        sevent.s_marker2 = shellmark(checkseg);
+        sevent.f_vertices2[0] = pointmark(p1);
+        sevent.f_vertices2[1] = pointmark(p2);
+        sevent.f_vertices2[2] = 0;
+        sevent.int_point[0] = P[0];
+        sevent.int_point[1] = P[1];
+        sevent.int_point[2] = P[2];
+      } else if (etype == 2) {
+        printf("PLC Error:  A segment and a facet intersect at point");
+        printf(" (%g,%g,%g).\n", P[0], P[1], P[2]);
+        printf("  Segment: [%d, %d] #%d (%d)\n", pointmark(p1), 
+               pointmark(p2), shellmark(checkseg),
+               parentsh.sh ? shellmark(parentsh) : 0);
+        printf("  Facet:   [%d, %d, %d] #%d.\n", pointmark(forg), 
+               pointmark(fdest), pointmark(fapex), geomtag);
+        sevent.e_type = 2;
+        sevent.f_marker1 = (parentsh.sh ? shellmark(parentsh) : 0);
+        sevent.s_marker1 = shellmark(checkseg);
+        sevent.f_vertices1[0] = pointmark(p1);
+        sevent.f_vertices1[1] = pointmark(p2);
+        sevent.f_vertices1[2] = 0;
+        sevent.f_marker2 = geomtag;
+        sevent.s_marker2 = 0;
+        sevent.f_vertices2[0] = pointmark(forg);
+        sevent.f_vertices2[1] = pointmark(fdest);
+        sevent.f_vertices2[2] = pointmark(fapex);
+        sevent.int_point[0] = P[0];
+        sevent.int_point[1] = P[1];
+        sevent.int_point[2] = P[2];
+      }
+      terminatetetgen(this, 3);
+    }
+  } else if (dir == ACROSSFACE) {
+    if (issubface(*itet)) {
+      face checksh;
+      tspivot(*itet, checksh);
+      point p1 = sorg(checksh);
+      point p2 = sdest(checksh);
+      point p3 = sapex(checksh);
+      REAL ip[3], u = 0;
+      planelineint(p1, p2, p3, e1, e2, ip, &u);
+      if (etype == 1) {
+        printf("PLC Error:  A segment and a facet intersect at point");
+        printf(" (%g,%g,%g).\n", ip[0], ip[1], ip[2]);
+        printf("  Segment: [%d, %d] #%d (%d)\n", pointmark(forg), 
+               pointmark(fdest), geomtag, facemark);
+        printf("  Facet:   [%d, %d, %d] #%d.\n", pointmark(p1),
+               pointmark(p2), pointmark(p3), shellmark(checksh));
+        sevent.e_type = 2;
+        sevent.f_marker1 = facemark;
+        sevent.s_marker1 = geomtag;
+        sevent.f_vertices1[0] = pointmark(forg);
+        sevent.f_vertices1[1] = pointmark(fdest);
+        sevent.f_vertices1[2] = 0;
+        sevent.f_marker2 = shellmark(checksh);
+        sevent.s_marker2 = 0;
+        sevent.f_vertices2[0] = pointmark(p1);
+        sevent.f_vertices2[1] = pointmark(p2);
+        sevent.f_vertices2[2] = pointmark(p3);
+        sevent.int_point[0] = ip[0];
+        sevent.int_point[1] = ip[1];
+        sevent.int_point[2] = ip[2];
+      } else if (etype == 2) {
+        printf("PLC Error:  Two facets intersect at point (%g,%g,%g).\n",
+               ip[0], ip[1], ip[2]);
+        printf("  Facet 1: [%d, %d, %d] #%d.\n", pointmark(forg), 
+               pointmark(fdest), pointmark(fapex), geomtag);
+        printf("  Facet 2: [%d, %d, %d] #%d.\n", pointmark(p1),
+               pointmark(p2), pointmark(p3), shellmark(checksh));
+        sevent.e_type = 3;
+        sevent.f_marker1 = geomtag;
+        sevent.s_marker1 = 0;
+        sevent.f_vertices1[0] = pointmark(forg);
+        sevent.f_vertices1[1] = pointmark(fdest);
+        sevent.f_vertices1[2] = pointmark(fapex);
+        sevent.f_marker2 = shellmark(checksh);
+        sevent.s_marker2 = 0;
+        sevent.f_vertices2[0] = pointmark(p1);
+        sevent.f_vertices2[1] = pointmark(p2);
+        sevent.f_vertices2[2] = pointmark(p3);
+        sevent.int_point[0] = ip[0];
+        sevent.int_point[1] = ip[1];
+        sevent.int_point[2] = ip[2];
+      }
+      terminatetetgen(this, 3);
+    }
+  } else {
+    // An unknown 'dir'.
+    terminatetetgen(this, 2);
+  }
+  return 0;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// report_selfint_face()    Report a self-intersection at a facet.           //
+//                                                                           //
+// The triangle with vertices 'p1', 'p2', and 'p3' intersects with the edge  //
+// of the tetrahedra 'iedge'. The intersection type is reported by 'intflag',//
+// 'types', and 'poss'.                                                      //
+//                                                                           //
+// This routine ASSUMES (1) the triangle (p1,p2,p3) must belong to a facet,  //
+// 'sface' is a subface of the same facet; and (2) 'iedge' must be either a  //
+// segment or an edge of another facet.                                      //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+int tetgenmesh::report_selfint_face(point p1, point p2, point p3, face* sface,
+  triface* iedge, int intflag, int* types, int* poss)
+{
+  face iface;
+  point e1 = NULL, e2 = NULL, e3 = NULL;
+  int etype = 0, geomtag = 0, facemark = 0;
+
+  geomtag = shellmark(*sface);
+
+  if (issubface(*iedge)) {
+    tspivot(*iedge, iface);
+    e1 = sorg(iface);
+    e2 = sdest(iface);
+    e3 = sapex(iface);
+    etype = 2;
+    facemark = geomtag;
+  } else if (issubseg(*iedge)) {
+    tsspivot1(*iedge, iface);
+    e1 = farsorg(iface);
+    e2 = farsdest(iface);
+    etype = 1;
+    face parentsh;
+    spivot(iface, parentsh);
+    facemark = shellmark(parentsh);
+  } else {
+    terminatetetgen(this, 2);
+  }
+
+  if (intflag == 2) {
+    // The triangle and the edge intersect only at one point.
+    REAL ip[3], u = 0;
+    planelineint(p1, p2, p3, e1, e2, ip, &u);
+    if ((types[0] == (int) ACROSSFACE) ||
+        (types[0] == (int) ACROSSEDGE)) {
+      // The triangle and the edge intersect in their interiors. 
+      if (etype == 1) {
+        printf("PLC Error:  A segment and a facet intersect at point");
+        printf(" (%g,%g,%g).\n", ip[0], ip[1], ip[2]);
+        printf("  Segment: [%d,%d] #%d (%d)\n", pointmark(e1), pointmark(e2),
+               shellmark(iface), facemark);
+        printf("  Facet:   [%d,%d,%d] #%d\n", pointmark(p1), 
+               pointmark(p2), pointmark(p3), geomtag);
+        sevent.e_type = 2;
+        sevent.f_marker1 = facemark;
+        sevent.s_marker1 = shellmark(iface);
+        sevent.f_vertices1[0] = pointmark(e1);
+        sevent.f_vertices1[1] = pointmark(e2);
+        sevent.f_vertices1[2] = 0;
+        sevent.f_marker2 = geomtag;
+        sevent.s_marker2 = 0;
+        sevent.f_vertices2[0] = pointmark(p1);
+        sevent.f_vertices2[1] = pointmark(p2);
+        sevent.f_vertices2[2] = pointmark(p3);
+        sevent.int_point[0] = ip[0];
+        sevent.int_point[1] = ip[1];
+        sevent.int_point[2] = ip[2];
+      } else {
+        printf("PLC Error:  Two facets intersect at point");
+        printf(" (%g,%g,%g).\n", ip[0], ip[1], ip[2]);
+        printf("  Facet 1: [%d,%d,%d] #%d\n", pointmark(e1), pointmark(e2),
+               pointmark(sorg(iface)), shellmark(iface));
+        printf("  Facet 2: [%d,%d,%d] #%d\n", pointmark(p1), 
+               pointmark(p2), pointmark(p3), geomtag);
+        sevent.e_type = 3;
+        sevent.f_marker1 = shellmark(iface);
+        sevent.s_marker1 = 0;
+        sevent.f_vertices1[0] = pointmark(e1);
+        sevent.f_vertices1[1] = pointmark(e2);
+        sevent.f_vertices1[2] = pointmark(sorg(iface));
+        sevent.f_marker2 = geomtag;
+        sevent.s_marker2 = 0;
+        sevent.f_vertices2[0] = pointmark(p1);
+        sevent.f_vertices2[1] = pointmark(p2);
+        sevent.f_vertices2[2] = pointmark(p3);
+        sevent.int_point[0] = ip[0];
+        sevent.int_point[1] = ip[1];
+        sevent.int_point[2] = ip[2];
+      }
+    } else if (types[0] == (int) ACROSSVERT) {
+      // A vertex of the triangle and the edge intersect.
+      point crosspt = NULL;
+      if (poss[0] == 0) {
+        crosspt = p1;
+      } else if (poss[0] == 1) {
+        crosspt = p2;
+      } else if (poss[0] == 2) {
+        crosspt = p3;
+      } else {
+        terminatetetgen(this, 2);
+      }
+      if (!issteinerpoint(crosspt)) {
+        if (etype == 1) {
+          printf("PLC Error:  A vertex and a segment intersect at (%g,%g,%g)\n",
+                 crosspt[0], crosspt[1], crosspt[2]);
+          printf("  Vertex:  #%d\n", pointmark(crosspt));
+          printf("  Segment: [%d,%d] #%d (%d)\n", pointmark(e1), pointmark(e2),
+                 shellmark(iface), facemark);
+          sevent.e_type = 7;
+          sevent.f_marker1 = 0;
+          sevent.s_marker1 = 0;
+          sevent.f_vertices1[0] = pointmark(crosspt);
+          sevent.f_vertices1[1] = 0;
+          sevent.f_vertices1[2] = 0;
+          sevent.f_marker2 = facemark;
+          sevent.s_marker2 = shellmark(iface);
+          sevent.f_vertices2[0] = pointmark(e1);
+          sevent.f_vertices2[1] = pointmark(e2);
+          sevent.f_vertices2[2] = 0;
+          sevent.int_point[0] = crosspt[0];
+          sevent.int_point[1] = crosspt[1];
+          sevent.int_point[2] = crosspt[2];
+        } else {
+          printf("PLC Error:  A vertex and a facet intersect at (%g,%g,%g)\n",
+                 crosspt[0], crosspt[1], crosspt[2]);
+          printf("  Vertex:  #%d\n", pointmark(crosspt));
+          printf("  Facet:   [%d,%d,%d] #%d\n", pointmark(p1), 
+                 pointmark(p2), pointmark(p3), geomtag);
+          sevent.e_type = 8;
+          sevent.f_marker1 = 0;
+          sevent.s_marker1 = 0;
+          sevent.f_vertices1[0] = pointmark(crosspt);
+          sevent.f_vertices1[1] = 0;
+          sevent.f_vertices1[2] = 0;
+          sevent.f_marker2 = geomtag;
+          sevent.s_marker2 = 0;
+          sevent.f_vertices2[0] = pointmark(p1);
+          sevent.f_vertices2[1] = pointmark(p2);
+          sevent.f_vertices2[2] = pointmark(p3);
+          sevent.int_point[0] = crosspt[0];
+          sevent.int_point[1] = crosspt[1];
+          sevent.int_point[2] = crosspt[2];
+        }
+      } else {
+        // It is a Steiner point. To be processed.
+        terminatetetgen(this, 2);
+      }
+    } else if ((types[0] == (int) TOUCHFACE) ||
+               (types[0] == (int) TOUCHEDGE)) {
+      // The triangle and a vertex of the edge intersect.
+      point touchpt = NULL;
+      if (poss[1] == 0) {
+        touchpt = org(*iedge);
+      } else if (poss[1] == 1) {
+        touchpt = dest(*iedge);
+      } else {
+        terminatetetgen(this, 2);
+      }
+      if (!issteinerpoint(touchpt)) {
+        printf("PLC Error:  A vertex and a facet intersect at (%g,%g,%g)\n",
+               touchpt[0], touchpt[1], touchpt[2]);
+        printf("  Vertex:  #%d\n", pointmark(touchpt));
+        printf("  Facet:   [%d,%d,%d] #%d\n", pointmark(p1), 
+               pointmark(p2), pointmark(p3), geomtag);
+        sevent.e_type = 8;
+        sevent.f_marker1 = 0;
+        sevent.s_marker1 = 0;
+        sevent.f_vertices1[0] = pointmark(touchpt);
+        sevent.f_vertices1[1] = 0;
+        sevent.f_vertices1[2] = 0;
+        sevent.f_marker2 = geomtag;
+        sevent.s_marker2 = 0;
+        sevent.f_vertices2[0] = pointmark(p1);
+        sevent.f_vertices2[1] = pointmark(p2);
+        sevent.f_vertices2[2] = pointmark(p3);
+        sevent.int_point[0] = touchpt[0];
+        sevent.int_point[1] = touchpt[1];
+        sevent.int_point[2] = touchpt[2];
+      } else {
+        // It is a Steiner point. To be processed.
+        terminatetetgen(this, 2);
+      }
+    } else if (types[0] == (int) SHAREVERT) {
+      terminatetetgen(this, 2);
+    } else {
+      terminatetetgen(this, 2);
+    }
+  } else if (intflag == 4) {
+    if (types[0] == (int) SHAREFACE) {
+      printf("PLC Error:  Two facets are overlapping.\n");
+      printf("  Facet 1:   [%d,%d,%d] #%d\n", pointmark(e1), 
+             pointmark(e2), pointmark(e3), facemark);
+      printf("  Facet 2:   [%d,%d,%d] #%d\n", pointmark(p1), 
+             pointmark(p2), pointmark(p3), geomtag);
+      sevent.e_type = 6;
+      sevent.f_marker1 = facemark;
+      sevent.s_marker1 = 0;
+      sevent.f_vertices1[0] = pointmark(e1);
+      sevent.f_vertices1[1] = pointmark(e2);
+      sevent.f_vertices1[2] = pointmark(e3);
+      sevent.f_marker2 = geomtag;
+      sevent.s_marker2 = 0;
+      sevent.f_vertices2[0] = pointmark(p1);
+      sevent.f_vertices2[1] = pointmark(p2);
+      sevent.f_vertices2[2] = pointmark(p3);
+    } else {
+      terminatetetgen(this, 2);
+    }
+  } else {
+    terminatetetgen(this, 2);
+  }
+
+  terminatetetgen(this, 3);
+  return 0;
+}
+
+////                                                                       ////
+////                                                                       ////
+//// geom_cxx /////////////////////////////////////////////////////////////////
+
+//// flip_cxx /////////////////////////////////////////////////////////////////
+////                                                                       ////
+////                                                                       ////
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// flip23()    Perform a 2-to-3 flip (face-to-edge flip).                    //
+//                                                                           //
+// 'fliptets' is an array of three tets (handles), where the [0] and [1] are  //
+// [a,b,c,d] and [b,a,c,e].  The three new tets: [e,d,a,b], [e,d,b,c], and   //
+// [e,d,c,a] are returned in [0], [1], and [2] of 'fliptets'.  As a result,  //
+// The face [a,b,c] is removed, and the edge [d,e] is created.               //
+//                                                                           //
+// If 'hullflag' > 0, hull tets may be involved in this flip, i.e., one of   //
+// the five vertices may be 'dummypoint'. There are two canonical cases:     //
+//   (1) d is 'dummypoint', then all three new tets are hull tets.  If e is  //
+//       'dummypoint', we reconfigure e to d, i.e., turn it up-side down.    //
+//   (2) c is 'dummypoint', then two new tets: [e,d,b,c] and [e,d,c,a], are  //
+//       hull tets. If a or b is 'dummypoint', we reconfigure it to c, i.e., //
+//       rotate the three input tets counterclockwisely (right-hand rule)    //
+//       until a or b is in c's position.                                    //
+//                                                                           //
+// If 'fc->enqflag' is set, convex hull faces will be queued for flipping.   //
+// In particular, if 'fc->enqflag' is 1, it is called by incrementalflip()   //
+// after the insertion of a new point.  It is assumed that 'd' is the new    //
+// point. IN this case, only link faces of 'd' are queued.                   //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::flip23(triface* fliptets, int hullflag, flipconstraints *fc)
+{
+  triface topcastets[3], botcastets[3];
+  triface newface, casface;
+  point pa, pb, pc, pd, pe;
+  REAL attrib, volume;
+  int dummyflag = 0;  // range = {-1, 0, 1, 2}.
+  int i;
+
+  if (hullflag > 0) {
+    // Check if e is dummypoint.
+    if (oppo(fliptets[1]) == dummypoint) {
+      // Swap the two old tets.
+      newface = fliptets[0];
+      fliptets[0] = fliptets[1];
+      fliptets[1] = newface;
+      dummyflag = -1;  // d is dummypoint.
+    } else {
+      // Check if either a or b is dummypoint.
+      if (org(fliptets[0]) == dummypoint) {
+        dummyflag = 1; // a is dummypoint.
+        enextself(fliptets[0]);
+        eprevself(fliptets[1]);
+      } else if (dest(fliptets[0]) == dummypoint) {
+        dummyflag = 2; // b is dummypoint.
+        eprevself(fliptets[0]);
+        enextself(fliptets[1]);
+      } else {
+        dummyflag = 0; // either c or d may be dummypoint.
+      }
+    }
+  }
+
+  pa =  org(fliptets[0]);
+  pb = dest(fliptets[0]);
+  pc = apex(fliptets[0]);
+  pd = oppo(fliptets[0]);
+  pe = oppo(fliptets[1]);
+
+  flip23count++;
+
+  // Get the outer boundary faces.
+  for (i = 0; i < 3; i++) {
+    fnext(fliptets[0], topcastets[i]);
+    enextself(fliptets[0]);
+  }
+  for (i = 0; i < 3; i++) {
+    fnext(fliptets[1], botcastets[i]);
+    eprevself(fliptets[1]);
+  }
+
+  // Re-use fliptets[0] and fliptets[1].
+  fliptets[0].ver = 11;
+  fliptets[1].ver = 11;
+  setelemmarker(fliptets[0].tet, 0); // Clear all flags.
+  setelemmarker(fliptets[1].tet, 0);
+  // NOTE: the element attributes and volume constraint remain unchanged.
+  if (checksubsegflag) {
+    // Dealloc the space to subsegments.
+    if (fliptets[0].tet[8] != NULL) {
+      tet2segpool->dealloc((shellface *) fliptets[0].tet[8]);
+      fliptets[0].tet[8] = NULL;
+    }
+    if (fliptets[1].tet[8] != NULL) {
+      tet2segpool->dealloc((shellface *) fliptets[1].tet[8]);
+      fliptets[1].tet[8] = NULL;
+    }
+  }
+  if (checksubfaceflag) {
+    // Dealloc the space to subfaces.
+    if (fliptets[0].tet[9] != NULL) {
+      tet2subpool->dealloc((shellface *) fliptets[0].tet[9]);
+      fliptets[0].tet[9] = NULL;
+    }
+    if (fliptets[1].tet[9] != NULL) {
+      tet2subpool->dealloc((shellface *) fliptets[1].tet[9]);
+      fliptets[1].tet[9] = NULL;
+    }
+  }
+  // Create a new tet.
+  maketetrahedron(&(fliptets[2]));
+  // The new tet have the same attributes from the old tet.
+  for (i = 0; i < numelemattrib; i++) {
+    attrib = elemattribute(fliptets[0].tet, i);
+    setelemattribute(fliptets[2].tet, i, attrib);
+  }
+  if (b->varvolume) {
+    volume = volumebound(fliptets[0].tet);
+    setvolumebound(fliptets[2].tet, volume);
+  }
+
+  if (hullflag > 0) {
+    // Check if d is dummytet.
+    if (pd != dummypoint) {
+      setvertices(fliptets[0], pe, pd, pa, pb); // [e,d,a,b] *
+      setvertices(fliptets[1], pe, pd, pb, pc); // [e,d,b,c] *
+      // Check if c is dummypoint.
+      if (pc != dummypoint) {
+        setvertices(fliptets[2], pe, pd, pc, pa);  // [e,d,c,a] *
+      } else {
+        setvertices(fliptets[2], pd, pe, pa, pc); // [d,e,a,c]
+        esymself(fliptets[2]);                    // [e,d,c,a] *
+      }
+      // The hullsize does not change.
+    } else {
+      // d is dummypoint.
+      setvertices(fliptets[0], pa, pb, pe, pd); // [a,b,e,d]
+      setvertices(fliptets[1], pb, pc, pe, pd); // [b,c,e,d]
+      setvertices(fliptets[2], pc, pa, pe, pd); // [c,a,e,d]
+      // Adjust the faces to [e,d,a,b], [e,d,b,c], [e,d,c,a] *
+      for (i = 0; i < 3; i++) {
+        eprevesymself(fliptets[i]);
+        enextself(fliptets[i]);
+      }
+      // We deleted one hull tet, and created three hull tets.
+      hullsize += 2;
+    }
+  } else {
+    setvertices(fliptets[0], pe, pd, pa, pb); // [e,d,a,b] *
+    setvertices(fliptets[1], pe, pd, pb, pc); // [e,d,b,c] *
+    setvertices(fliptets[2], pe, pd, pc, pa); // [e,d,c,a] *
+  }
+
+  if (fc->remove_ndelaunay_edge) { // calc_tetprism_vol
+    REAL volneg[2], volpos[3], vol_diff;
+    if (pd != dummypoint) {
+      if (pc != dummypoint) {
+        volpos[0] = tetprismvol(pe, pd, pa, pb);
+        volpos[1] = tetprismvol(pe, pd, pb, pc);
+        volpos[2] = tetprismvol(pe, pd, pc, pa);
+        volneg[0] = tetprismvol(pa, pb, pc, pd);
+        volneg[1] = tetprismvol(pb, pa, pc, pe);
+      } else { // pc == dummypoint
+        volpos[0] = tetprismvol(pe, pd, pa, pb);
+        volpos[1] = 0.;
+        volpos[2] = 0.;
+        volneg[0] = 0.;
+        volneg[1] = 0.;
+      }
+    } else { // pd == dummypoint.
+      volpos[0] = 0.;
+      volpos[1] = 0.;
+      volpos[2] = 0.;
+      volneg[0] = 0.;
+      volneg[1] = tetprismvol(pb, pa, pc, pe);
+    }
+    vol_diff = volpos[0] + volpos[1] + volpos[2] - volneg[0] - volneg[1];
+    fc->tetprism_vol_sum  += vol_diff; // Update the total sum.
+  }
+
+  // Bond three new tets together.
+  for (i = 0; i < 3; i++) {
+    esym(fliptets[i], newface);
+    bond(newface, fliptets[(i + 1) % 3]);
+  }
+  // Bond to top outer boundary faces (at [a,b,c,d]).
+  for (i = 0; i < 3; i++) {
+    eorgoppo(fliptets[i], newface); // At edges [b,a], [c,b], [a,c].
+    bond(newface, topcastets[i]);
+  }
+  // Bond bottom outer boundary faces (at [b,a,c,e]).
+  for (i = 0; i < 3; i++) {
+    edestoppo(fliptets[i], newface); // At edges [a,b], [b,c], [c,a].
+    bond(newface, botcastets[i]);
+  }
+
+  if (checksubsegflag) {
+    // Bond subsegments if there are.
+    // Each new tet has 5 edges to be checked (except the edge [e,d]). 
+    face checkseg;
+    // The middle three: [a,b], [b,c], [c,a].
+    for (i = 0; i < 3; i++) {      
+      if (issubseg(topcastets[i])) {
+        tsspivot1(topcastets[i], checkseg);
+        eorgoppo(fliptets[i], newface);
+        tssbond1(newface, checkseg);
+        sstbond1(checkseg, newface);
+        if (fc->chkencflag & 1) {
+          enqueuesubface(badsubsegs, &checkseg);
+        }
+      }
+    }
+    // The top three: [d,a], [d,b], [d,c]. Two tets per edge.
+    for (i = 0; i < 3; i++) {
+      eprev(topcastets[i], casface);      
+      if (issubseg(casface)) {
+        tsspivot1(casface, checkseg);
+        enext(fliptets[i], newface);
+        tssbond1(newface, checkseg);
+        sstbond1(checkseg, newface);
+        esym(fliptets[(i + 2) % 3], newface);
+        eprevself(newface);
+        tssbond1(newface, checkseg);
+        sstbond1(checkseg, newface);
+        if (fc->chkencflag & 1) {
+          enqueuesubface(badsubsegs, &checkseg);
+        }
+      }
+    }
+    // The bot three: [a,e], [b,e], [c,e]. Two tets per edge.
+    for (i = 0; i < 3; i++) {
+      enext(botcastets[i], casface);
+      if (issubseg(casface)) {
+        tsspivot1(casface, checkseg);
+        eprev(fliptets[i], newface);
+        tssbond1(newface, checkseg);
+        sstbond1(checkseg, newface);
+        esym(fliptets[(i + 2) % 3], newface);
+        enextself(newface);
+        tssbond1(newface, checkseg);
+        sstbond1(checkseg, newface);
+        if (fc->chkencflag & 1) {
+          enqueuesubface(badsubsegs, &checkseg);
+        }
+      }
+    }
+  } // if (checksubsegflag)
+
+  if (checksubfaceflag) {
+    // Bond 6 subfaces if there are.
+    face checksh;
+    for (i = 0; i < 3; i++) {      
+      if (issubface(topcastets[i])) {
+        tspivot(topcastets[i], checksh);
+        eorgoppo(fliptets[i], newface);
+        sesymself(checksh);
+        tsbond(newface, checksh);
+        if (fc->chkencflag & 2) {
+          enqueuesubface(badsubfacs, &checksh);
+        }
+      }
+    }
+    for (i = 0; i < 3; i++) {
+      if (issubface(botcastets[i])) {
+        tspivot(botcastets[i], checksh);
+        edestoppo(fliptets[i], newface);
+        sesymself(checksh);
+        tsbond(newface, checksh);
+        if (fc->chkencflag & 2) {
+          enqueuesubface(badsubfacs, &checksh);
+        }
+      }
+    }
+  } // if (checksubfaceflag)
+
+  if (fc->chkencflag & 4) {
+    // Put three new tets into check list.
+    for (i = 0; i < 3; i++) {
+      enqueuetetrahedron(&(fliptets[i]));
+    }
+  }
+
+  // Update the point-to-tet map.
+  setpoint2tet(pa, (tetrahedron) fliptets[0].tet);
+  setpoint2tet(pb, (tetrahedron) fliptets[0].tet);
+  setpoint2tet(pc, (tetrahedron) fliptets[1].tet);
+  setpoint2tet(pd, (tetrahedron) fliptets[0].tet);
+  setpoint2tet(pe, (tetrahedron) fliptets[0].tet);
+
+  if (hullflag > 0) {
+    if (dummyflag != 0) {
+      // Restore the original position of the points (for flipnm()).
+      if (dummyflag == -1) { 
+        // Reverse the edge.
+        for (i = 0; i < 3; i++) {
+          esymself(fliptets[i]);
+        }
+        // Swap the last two new tets.
+        newface = fliptets[1];
+        fliptets[1] = fliptets[2];
+        fliptets[2] = newface;
+      } else {
+        // either a or b were swapped.
+        if (dummyflag == 1) {
+          // a is dummypoint.
+          newface = fliptets[0];
+          fliptets[0] = fliptets[2];
+          fliptets[2] = fliptets[1];
+          fliptets[1] = newface;
+        } else { // dummyflag == 2
+          // b is dummypoint.
+          newface = fliptets[0];
+          fliptets[0] = fliptets[1];
+          fliptets[1] = fliptets[2];
+          fliptets[2] = newface;
+        }
+      }
+    }
+  }
+
+  if (fc->enqflag > 0) {
+    // Queue faces which may be locally non-Delaunay.  
+    for (i = 0; i < 3; i++) {
+      eprevesym(fliptets[i], newface);
+      flippush(flipstack, &newface);
+    }
+    if (fc->enqflag > 1) {
+      for (i = 0; i < 3; i++) {
+        enextesym(fliptets[i], newface);
+        flippush(flipstack, &newface);
+      }
+    }
+  }
+
+  recenttet = fliptets[0];
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// flip32()    Perform a 3-to-2 flip (edge-to-face flip).                    //
+//                                                                           //
+// 'fliptets' is an array of three tets (handles),  which are [e,d,a,b],     //
+// [e,d,b,c], and [e,d,c,a].  The two new tets: [a,b,c,d] and [b,a,c,e] are  //
+// returned in [0] and [1] of 'fliptets'.  As a result, the edge [e,d] is    //
+// replaced by the face [a,b,c].                                             //
+//                                                                           //
+// If 'hullflag' > 0, hull tets may be involved in this flip, i.e., one of   //
+// the five vertices may be 'dummypoint'. There are two canonical cases:     //
+//   (1) d is 'dummypoint', then [a,b,c,d] is hull tet. If e is 'dummypoint',//
+//       we reconfigure e to d, i.e., turnover it.                           //
+//   (2) c is 'dummypoint' then both [a,b,c,d] and [b,a,c,e] are hull tets.  //
+//       If a or b is 'dummypoint', we reconfigure it to c, i.e., rotate the //
+//       three old tets counterclockwisely (right-hand rule) until a or b    //
+//       is in c's position.                                                 //
+//                                                                           //
+// If 'fc->enqflag' is set, convex hull faces will be queued for flipping.   //
+// In particular, if 'fc->enqflag' is 1, it is called by incrementalflip()   //
+// after the insertion of a new point.  It is assumed that 'a' is the new    //
+// point. In this case, only link faces of 'a' are queued.                   //
+//                                                                           //
+// If 'checksubfaceflag' is on (global variable), and assume [e,d] is not a  //
+// segment. There may be two (interior) subfaces sharing at [e,d], which are //
+// [e,d,p] and [e,d,q], where the pair (p,q) may be either (a,b), or (b,c),  //
+// or (c,a)  In such case, a 2-to-2 flip is performed on these two subfaces  //
+// and two new subfaces [p,q,e] and [p,q,d] are created. They are inserted   //
+// back into the tetrahedralization.                                         //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::flip32(triface* fliptets, int hullflag, flipconstraints *fc)
+{
+  triface topcastets[3], botcastets[3];
+  triface newface, casface;
+  face flipshs[3]; 
+  face checkseg; 
+  point pa, pb, pc, pd, pe;
+  REAL attrib, volume;
+  int dummyflag = 0;  // Rangle = {-1, 0, 1, 2}
+  int spivot = -1, scount = 0; // for flip22()
+  int t1ver; 
+  int i, j;
+
+  if (hullflag > 0) {
+    // Check if e is 'dummypoint'.
+    if (org(fliptets[0]) == dummypoint) {
+      // Reverse the edge.
+      for (i = 0; i < 3; i++) {
+        esymself(fliptets[i]);
+      }
+      // Swap the last two tets.
+      newface = fliptets[1];
+      fliptets[1] = fliptets[2];
+      fliptets[2] = newface;
+      dummyflag = -1; // e is dummypoint.
+    } else {
+      // Check if a or b is the 'dummypoint'.
+      if (apex(fliptets[0]) == dummypoint) { 
+        dummyflag = 1;  // a is dummypoint.
+        newface = fliptets[0];
+        fliptets[0] = fliptets[1];
+        fliptets[1] = fliptets[2];
+        fliptets[2] = newface;
+      } else if (apex(fliptets[1]) == dummypoint) {
+        dummyflag = 2;  // b is dummypoint.
+        newface = fliptets[0];
+        fliptets[0] = fliptets[2];
+        fliptets[2] = fliptets[1];
+        fliptets[1] = newface;
+      } else {
+        dummyflag = 0;  // either c or d may be dummypoint.
+      }
+    }
+  }
+
+  pa = apex(fliptets[0]);
+  pb = apex(fliptets[1]);
+  pc = apex(fliptets[2]);
+  pd = dest(fliptets[0]);
+  pe = org(fliptets[0]);
+
+  flip32count++;
+
+  // Get the outer boundary faces.
+  for (i = 0; i < 3; i++) {
+    eorgoppo(fliptets[i], casface);
+    fsym(casface, topcastets[i]);
+  }
+  for (i = 0; i < 3; i++) {
+    edestoppo(fliptets[i], casface);
+    fsym(casface, botcastets[i]);
+  }
+
+  if (checksubfaceflag) {
+    // Check if there are interior subfaces at the edge [e,d].
+    for (i = 0; i < 3; i++) {
+      tspivot(fliptets[i], flipshs[i]);
+      if (flipshs[i].sh != NULL) {
+        // Found an interior subface.
+        stdissolve(flipshs[i]); // Disconnect the sub-tet bond.
+        scount++;
+      } else {
+        spivot = i;
+      }
+    }
+  }
+
+  // Re-use fliptets[0] and fliptets[1].
+  fliptets[0].ver = 11;
+  fliptets[1].ver = 11;
+  setelemmarker(fliptets[0].tet, 0); // Clear all flags.
+  setelemmarker(fliptets[1].tet, 0);
+  if (checksubsegflag) {
+    // Dealloc the space to subsegments.
+    if (fliptets[0].tet[8] != NULL) {
+      tet2segpool->dealloc((shellface *) fliptets[0].tet[8]);
+      fliptets[0].tet[8] = NULL;
+    }
+    if (fliptets[1].tet[8] != NULL) {
+      tet2segpool->dealloc((shellface *) fliptets[1].tet[8]);
+      fliptets[1].tet[8] = NULL;
+    }
+  }
+  if (checksubfaceflag) {
+    // Dealloc the space to subfaces.
+    if (fliptets[0].tet[9] != NULL) {
+      tet2subpool->dealloc((shellface *) fliptets[0].tet[9]);
+      fliptets[0].tet[9] = NULL;
+    }
+    if (fliptets[1].tet[9] != NULL) {
+      tet2subpool->dealloc((shellface *) fliptets[1].tet[9]);
+      fliptets[1].tet[9] = NULL;
+    }
+  }
+  if (checksubfaceflag) {
+    if (scount > 0) {
+      // The element attributes and volume constraint must be set correctly.
+      // There are two subfaces involved in this flip. The three tets are
+      //   separated into two different regions, one may be exterior. The
+      //   first region has two tets, and the second region has only one.
+      //   The two created tets must be in the same region as the first region. 
+      //   The element attributes and volume constraint must be set correctly.
+      //assert(spivot != -1);
+      // The tet fliptets[spivot] is in the first region.
+      for (j = 0; j < 2; j++) {
+        for (i = 0; i < numelemattrib; i++) {
+          attrib = elemattribute(fliptets[spivot].tet, i);
+          setelemattribute(fliptets[j].tet, i, attrib);
+        }
+        if (b->varvolume) {
+          volume = volumebound(fliptets[spivot].tet);
+          setvolumebound(fliptets[j].tet, volume);
+        }
+      }
+    }
+  }
+  // Delete an old tet.
+  tetrahedrondealloc(fliptets[2].tet);
+
+  if (hullflag > 0) {
+    // Check if c is dummypointc.
+    if (pc != dummypoint) {
+      // Check if d is dummypoint.
+      if (pd != dummypoint) {
+        // No hull tet is involved.
+      } else {
+        // We deleted three hull tets, and created one hull tet.
+        hullsize -= 2;
+      }
+      setvertices(fliptets[0], pa, pb, pc, pd);
+      setvertices(fliptets[1], pb, pa, pc, pe);
+    } else {
+      // c is dummypoint. The two new tets are hull tets.
+      setvertices(fliptets[0], pb, pa, pd, pc);
+      setvertices(fliptets[1], pa, pb, pe, pc);
+      // Adjust badc -> abcd.
+      esymself(fliptets[0]);
+      // Adjust abec -> bace.
+      esymself(fliptets[1]);
+      // The hullsize does not change.
+    }
+  } else {
+    setvertices(fliptets[0], pa, pb, pc, pd);
+    setvertices(fliptets[1], pb, pa, pc, pe);
+  }
+
+  if (fc->remove_ndelaunay_edge) { // calc_tetprism_vol
+    REAL volneg[3], volpos[2], vol_diff;
+    if (pc != dummypoint) {
+      if (pd != dummypoint) {
+        volneg[0] = tetprismvol(pe, pd, pa, pb);
+        volneg[1] = tetprismvol(pe, pd, pb, pc);
+        volneg[2] = tetprismvol(pe, pd, pc, pa);
+        volpos[0] = tetprismvol(pa, pb, pc, pd);
+        volpos[1] = tetprismvol(pb, pa, pc, pe);
+      } else { // pd == dummypoint
+        volneg[0] = 0.;
+        volneg[1] = 0.;
+        volneg[2] = 0.;
+        volpos[0] = 0.;
+        volpos[1] = tetprismvol(pb, pa, pc, pe);
+      }
+    } else { // pc == dummypoint.
+      volneg[0] = tetprismvol(pe, pd, pa, pb);
+      volneg[1] = 0.;
+      volneg[2] = 0.;
+      volpos[0] = 0.;
+      volpos[1] = 0.;
+    }
+    vol_diff = volpos[0] + volpos[1] - volneg[0] - volneg[1] - volneg[2];
+    fc->tetprism_vol_sum  += vol_diff; // Update the total sum.
+  }
+
+  // Bond abcd <==> bace.
+  bond(fliptets[0], fliptets[1]);
+  // Bond new faces to top outer boundary faces (at abcd).
+  for (i = 0; i < 3; i++) {
+    esym(fliptets[0], newface);
+    bond(newface, topcastets[i]);
+    enextself(fliptets[0]);
+  }
+  // Bond new faces to bottom outer boundary faces (at bace).
+  for (i = 0; i < 3; i++) {
+    esym(fliptets[1], newface);
+    bond(newface, botcastets[i]);
+    eprevself(fliptets[1]);
+  }
+
+  if (checksubsegflag) {
+    // Bond 9 segments to new (flipped) tets.
+    for (i = 0; i < 3; i++) { // edges a->b, b->c, c->a.      
+      if (issubseg(topcastets[i])) {
+        tsspivot1(topcastets[i], checkseg);
+        tssbond1(fliptets[0], checkseg);
+        sstbond1(checkseg, fliptets[0]);
+        tssbond1(fliptets[1], checkseg);
+        sstbond1(checkseg, fliptets[1]);
+        if (fc->chkencflag & 1) {
+          enqueuesubface(badsubsegs, &checkseg);
+        }
+      }
+      enextself(fliptets[0]);
+      eprevself(fliptets[1]);
+    }
+    // The three top edges.
+    for (i = 0; i < 3; i++) { // edges b->d, c->d, a->d.
+      esym(fliptets[0], newface);
+      eprevself(newface); 
+      enext(topcastets[i], casface);      
+      if (issubseg(casface)) {
+        tsspivot1(casface, checkseg);
+        tssbond1(newface, checkseg);
+        sstbond1(checkseg, newface);
+        if (fc->chkencflag & 1) {
+          enqueuesubface(badsubsegs, &checkseg);
+        }
+      }
+      enextself(fliptets[0]);
+    }
+    // The three bot edges.
+    for (i = 0; i < 3; i++) { // edges b<-e, c<-e, a<-e.
+      esym(fliptets[1], newface);
+      enextself(newface); 
+      eprev(botcastets[i], casface);
+      if (issubseg(casface)) {
+        tsspivot1(casface, checkseg);
+        tssbond1(newface, checkseg);
+        sstbond1(checkseg, newface);
+        if (fc->chkencflag & 1) {
+          enqueuesubface(badsubsegs, &checkseg);
+        }
+      }
+      eprevself(fliptets[1]);
+    }
+  } // if (checksubsegflag)
+
+  if (checksubfaceflag) {
+    face checksh;
+    // Bond the top three casing subfaces.
+    for (i = 0; i < 3; i++) { // At edges [b,a], [c,b], [a,c]
+      if (issubface(topcastets[i])) {
+        tspivot(topcastets[i], checksh);
+        esym(fliptets[0], newface); 
+        sesymself(checksh);
+        tsbond(newface, checksh);
+        if (fc->chkencflag & 2) {
+          enqueuesubface(badsubfacs, &checksh);
+        }
+      }
+      enextself(fliptets[0]);
+    }
+    // Bond the bottom three casing subfaces.
+    for (i = 0; i < 3; i++) { // At edges [a,b], [b,c], [c,a]
+      if (issubface(botcastets[i])) {
+        tspivot(botcastets[i], checksh);
+        esym(fliptets[1], newface); 
+        sesymself(checksh);
+        tsbond(newface, checksh);
+        if (fc->chkencflag & 2) {
+          enqueuesubface(badsubfacs, &checksh);
+        }
+      }
+      eprevself(fliptets[1]);
+    }
+
+    if (scount > 0) {
+      face flipfaces[2];
+      // Perform a 2-to-2 flip in subfaces.
+      flipfaces[0] = flipshs[(spivot + 1) % 3];
+      flipfaces[1] = flipshs[(spivot + 2) % 3];
+      sesymself(flipfaces[1]);
+      flip22(flipfaces, 0, fc->chkencflag);
+      // Connect the flipped subfaces to flipped tets.
+      // First go to the corresponding flipping edge.
+      //   Re-use top- and botcastets[0].
+      topcastets[0] = fliptets[0];
+      botcastets[0] = fliptets[1];
+      for (i = 0; i < ((spivot + 1) % 3); i++) {
+        enextself(topcastets[0]);
+        eprevself(botcastets[0]);
+      }
+      // Connect the top subface to the top tets.
+      esymself(topcastets[0]);
+      sesymself(flipfaces[0]);
+      // Check if there already exists a subface.
+      tspivot(topcastets[0], checksh);
+      if (checksh.sh == NULL) {
+        tsbond(topcastets[0], flipfaces[0]);
+        fsymself(topcastets[0]);
+        sesymself(flipfaces[0]);
+        tsbond(topcastets[0], flipfaces[0]);
+      } else {
+        // An invalid 2-to-2 flip. Report a bug.
+        terminatetetgen(this, 2); 
+      }
+      // Connect the bot subface to the bottom tets.
+      esymself(botcastets[0]);
+      sesymself(flipfaces[1]);
+      // Check if there already exists a subface.
+      tspivot(botcastets[0], checksh);
+      if (checksh.sh == NULL) {
+        tsbond(botcastets[0], flipfaces[1]);
+        fsymself(botcastets[0]);
+        sesymself(flipfaces[1]);
+        tsbond(botcastets[0], flipfaces[1]);
+      } else {
+        // An invalid 2-to-2 flip. Report a bug.
+        terminatetetgen(this, 2);  
+      }
+    } // if (scount > 0)
+  } // if (checksubfaceflag)
+
+  if (fc->chkencflag & 4) {
+    // Put two new tets into check list.
+    for (i = 0; i < 2; i++) {
+      enqueuetetrahedron(&(fliptets[i]));
+    }
+  }
+
+  setpoint2tet(pa, (tetrahedron) fliptets[0].tet);
+  setpoint2tet(pb, (tetrahedron) fliptets[0].tet);
+  setpoint2tet(pc, (tetrahedron) fliptets[0].tet);
+  setpoint2tet(pd, (tetrahedron) fliptets[0].tet);
+  setpoint2tet(pe, (tetrahedron) fliptets[1].tet);
+
+  if (hullflag > 0) {
+    if (dummyflag != 0) {
+      // Restore the original position of the points (for flipnm()).
+      if (dummyflag == -1) {
+        // e were dummypoint. Swap the two new tets.
+        newface = fliptets[0];
+        fliptets[0] = fliptets[1];
+        fliptets[1] = newface;
+      } else {
+        // a or b was dummypoint.
+        if (dummyflag == 1) {
+          eprevself(fliptets[0]);
+          enextself(fliptets[1]);
+        } else { // dummyflag == 2
+          enextself(fliptets[0]);
+          eprevself(fliptets[1]);
+        }
+      }
+    }
+  }
+  
+  if (fc->enqflag > 0) {
+    // Queue faces which may be locally non-Delaunay.
+    // pa = org(fliptets[0]); // 'a' may be a new vertex.
+    enextesym(fliptets[0], newface);
+    flippush(flipstack, &newface);
+    eprevesym(fliptets[1], newface);
+    flippush(flipstack, &newface);
+    if (fc->enqflag > 1) {
+      //pb = dest(fliptets[0]);
+      eprevesym(fliptets[0], newface);
+      flippush(flipstack, &newface);
+      enextesym(fliptets[1], newface);
+      flippush(flipstack, &newface);
+      //pc = apex(fliptets[0]);
+      esym(fliptets[0], newface);
+      flippush(flipstack, &newface);
+      esym(fliptets[1], newface);
+      flippush(flipstack, &newface);
+    }
+  }
+
+  recenttet = fliptets[0];
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// flip41()    Perform a 4-to-1 flip (Remove a vertex).                      //
+//                                                                           //
+// 'fliptets' is an array of four tetrahedra in the star of the removing     //
+// vertex 'p'. Let the four vertices in the star of p be a, b, c, and d. The //
+// four tets in 'fliptets' are: [p,d,a,b], [p,d,b,c], [p,d,c,a], and [a,b,c, //
+// p].  On return, 'fliptets[0]' is the new tet [a,b,c,d].                   //
+//                                                                           //
+// If 'hullflag' is set (> 0), one of the five vertices may be 'dummypoint'. //
+// The 'hullsize' may be changed.  Note that p may be dummypoint.  In this   //
+// case, four hull tets are replaced by one real tet.                        //
+//                                                                           //
+// If 'checksubface' flag is set (>0), it is possible that there are three   //
+// interior subfaces connecting at p.  If so, a 3-to-1 flip is performed to  //
+// to remove p from the surface triangulation.                               //
+//                                                                           //
+// If it is called by the routine incrementalflip(), we assume that d is the //
+// newly inserted vertex.                                                    //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::flip41(triface* fliptets, int hullflag, flipconstraints *fc)
+{
+  triface topcastets[3], botcastet;
+  triface newface, neightet;
+  face flipshs[4];
+  point pa, pb, pc, pd, pp;
+  int dummyflag = 0; // in {0, 1, 2, 3, 4}
+  int spivot = -1, scount = 0;
+  int t1ver; 
+  int i;
+
+  pa =  org(fliptets[3]);
+  pb = dest(fliptets[3]);
+  pc = apex(fliptets[3]);
+  pd = dest(fliptets[0]);
+  pp =  org(fliptets[0]); // The removing vertex.
+
+  flip41count++;
+
+  // Get the outer boundary faces.
+  for (i = 0; i < 3; i++) {
+    enext(fliptets[i], topcastets[i]);
+    fnextself(topcastets[i]); // [d,a,b,#], [d,b,c,#], [d,c,a,#]
+    enextself(topcastets[i]); // [a,b,d,#], [b,c,d,#], [c,a,d,#]
+  }
+  fsym(fliptets[3], botcastet); // [b,a,c,#]
+
+  if (checksubfaceflag) {
+    // Check if there are three subfaces at 'p'.
+    //   Re-use 'newface'.
+    for (i = 0; i < 3; i++) {
+      fnext(fliptets[3], newface); // [a,b,p,d],[b,c,p,d],[c,a,p,d].
+      tspivot(newface, flipshs[i]);
+      if (flipshs[i].sh != NULL) {
+        spivot = i; // Remember this subface.
+        scount++;
+      }
+      enextself(fliptets[3]);
+    }
+    if (scount > 0) {
+      // There are three subfaces connecting at p.
+      if (scount < 3) {
+        // The new subface is one of {[a,b,d], [b,c,d], [c,a,d]}.
+        // Go to the tet containing the three subfaces.
+        fsym(topcastets[spivot], neightet);
+        // Get the three subfaces connecting at p.
+        for (i = 0; i < 3; i++) {
+          esym(neightet, newface);
+          tspivot(newface, flipshs[i]);
+          eprevself(neightet);
+        }
+      } else {
+        spivot = 3; // The new subface is [a,b,c].
+      }
+    }
+  } // if (checksubfaceflag)
+
+
+  // Re-use fliptets[0] for [a,b,c,d].
+  fliptets[0].ver = 11;
+  setelemmarker(fliptets[0].tet, 0); // Clean all flags.
+  // NOTE: the element attributes and volume constraint remain unchanged.
+  if (checksubsegflag) {
+    // Dealloc the space to subsegments.
+    if (fliptets[0].tet[8] != NULL) {
+      tet2segpool->dealloc((shellface *) fliptets[0].tet[8]);
+      fliptets[0].tet[8] = NULL;
+    }
+  }
+  if (checksubfaceflag) {
+    // Dealloc the space to subfaces.
+    if (fliptets[0].tet[9] != NULL) {
+      tet2subpool->dealloc((shellface *) fliptets[0].tet[9]);
+      fliptets[0].tet[9] = NULL;
+    }
+  }
+  // Delete the other three tets.
+  for (i = 1; i < 4; i++) {
+    tetrahedrondealloc(fliptets[i].tet);
+  }
+
+  if (pp != dummypoint) {
+    // Mark the point pp as unused.
+    setpointtype(pp, UNUSEDVERTEX);
+    unuverts++;
+  }
+
+  // Create the new tet [a,b,c,d].
+  if (hullflag > 0) {
+    // One of the five vertices may be 'dummypoint'.
+    if (pa == dummypoint) {
+      // pa is dummypoint.
+      setvertices(fliptets[0], pc, pb, pd, pa);
+      esymself(fliptets[0]);  // [b,c,a,d]
+      eprevself(fliptets[0]); // [a,b,c,d]
+      dummyflag = 1;
+    } else if (pb == dummypoint) {
+      setvertices(fliptets[0], pa, pc, pd, pb);
+      esymself(fliptets[0]);  // [c,a,b,d]
+      enextself(fliptets[0]); // [a,b,c,d]
+      dummyflag = 2;
+    } else if (pc == dummypoint) {
+      setvertices(fliptets[0], pb, pa, pd, pc);
+      esymself(fliptets[0]);  // [a,b,c,d]
+      dummyflag = 3;
+    } else if (pd == dummypoint) {
+      setvertices(fliptets[0], pa, pb, pc, pd);
+      dummyflag = 4;
+    } else {
+      setvertices(fliptets[0], pa, pb, pc, pd);
+      if (pp == dummypoint) {
+        dummyflag = -1;
+      } else {
+        dummyflag = 0;
+      }
+    }
+    if (dummyflag > 0) {
+      // We deleted 3 hull tets, and create 1 hull tet.
+      hullsize -= 2;
+    } else if (dummyflag < 0) {
+      // We deleted 4 hull tets.
+      hullsize -= 4;
+      // meshedges does not change.
+    }
+  } else {
+    setvertices(fliptets[0], pa, pb, pc, pd);
+  }
+
+  if (fc->remove_ndelaunay_edge) { // calc_tetprism_vol
+    REAL volneg[4], volpos[1], vol_diff;
+    if (dummyflag > 0) {
+      if (pa == dummypoint) {
+        volneg[0] = 0.;
+        volneg[1] = tetprismvol(pp, pd, pb, pc);
+        volneg[2] = 0.;
+        volneg[3] = 0.;
+      } else if (pb == dummypoint) {
+        volneg[0] = 0.;
+        volneg[1] = 0.;
+        volneg[2] = tetprismvol(pp, pd, pc, pa);
+        volneg[3] = 0.;
+      } else if (pc == dummypoint) {
+        volneg[0] = tetprismvol(pp, pd, pa, pb);
+        volneg[1] = 0.;
+        volneg[2] = 0.;
+        volneg[3] = 0.;
+      } else { // pd == dummypoint
+        volneg[0] = 0.;
+        volneg[1] = 0.;
+        volneg[2] = 0.;
+        volneg[3] = tetprismvol(pa, pb, pc, pp);
+      }
+      volpos[0] = 0.;
+    } else if (dummyflag < 0) {
+      volneg[0] = 0.;
+      volneg[1] = 0.;
+      volneg[2] = 0.;
+      volneg[3] = 0.;
+      volpos[0] = tetprismvol(pa, pb, pc, pd);
+    } else {
+      volneg[0] = tetprismvol(pp, pd, pa, pb);
+      volneg[1] = tetprismvol(pp, pd, pb, pc);
+      volneg[2] = tetprismvol(pp, pd, pc, pa);
+      volneg[3] = tetprismvol(pa, pb, pc, pp);
+      volpos[0] = tetprismvol(pa, pb, pc, pd);
+    }
+    vol_diff = volpos[0] - volneg[0] - volneg[1] - volneg[2] - volneg[3];
+    fc->tetprism_vol_sum  += vol_diff; // Update the total sum.
+  }
+
+  // Bond the new tet to adjacent tets.
+  for (i = 0; i < 3; i++) {
+    esym(fliptets[0], newface); // At faces [b,a,d], [c,b,d], [a,c,d].
+    bond(newface, topcastets[i]);
+    enextself(fliptets[0]);
+  }
+  bond(fliptets[0], botcastet);
+
+  if (checksubsegflag) {
+    face checkseg;
+    // Bond 6 segments (at edges of [a,b,c,d]) if there there are.
+    for (i = 0; i < 3; i++) {
+      eprev(topcastets[i], newface); // At edges [d,a],[d,b],[d,c].
+      if (issubseg(newface)) {
+        tsspivot1(newface, checkseg);
+        esym(fliptets[0], newface);
+        enextself(newface); // At edges [a,d], [b,d], [c,d].
+        tssbond1(newface, checkseg);
+        sstbond1(checkseg, newface);
+        if (fc->chkencflag & 1) {
+          enqueuesubface(badsubsegs, &checkseg);
+        }
+      }
+      enextself(fliptets[0]);
+    }
+    for (i = 0; i < 3; i++) {
+      if (issubseg(topcastets[i])) {
+        tsspivot1(topcastets[i], checkseg); // At edges [a,b],[b,c],[c,a].
+        tssbond1(fliptets[0], checkseg);
+        sstbond1(checkseg, fliptets[0]);
+        if (fc->chkencflag & 1) {
+          enqueuesubface(badsubsegs, &checkseg);
+        }
+      }
+      enextself(fliptets[0]);
+    }
+  }
+
+  if (checksubfaceflag) {
+    face checksh;
+    // Bond 4 subfaces (at faces of [a,b,c,d]) if there are.
+    for (i = 0; i < 3; i++) {
+      if (issubface(topcastets[i])) {
+        tspivot(topcastets[i], checksh); // At faces [a,b,d],[b,c,d],[c,a,d]
+        esym(fliptets[0], newface); // At faces [b,a,d],[c,b,d],[a,c,d]
+        sesymself(checksh);
+        tsbond(newface, checksh);
+        if (fc->chkencflag & 2) {
+          enqueuesubface(badsubfacs, &checksh);
+        }
+      }
+      enextself(fliptets[0]);
+    }
+    if (issubface(botcastet)) {
+      tspivot(botcastet, checksh); // At face [b,a,c]
+      sesymself(checksh);
+      tsbond(fliptets[0], checksh);
+      if (fc->chkencflag & 2) {
+        enqueuesubface(badsubfacs, &checksh);
+      }
+    }
+
+    if (spivot >= 0) {
+      // Perform a 3-to-1 flip in surface triangulation.
+      // Depending on the value of 'spivot', the three subfaces are:
+      //   - 0: [a,b,p], [b,d,p], [d,a,p]
+      //   - 1: [b,c,p], [c,d,p], [d,b,p] 
+      //   - 2: [c,a,p], [a,d,p], [d,c,p] 
+      //   - 3: [a,b,p], [b,c,p], [c,a,p]
+      // Adjust the three subfaces such that their origins are p, i.e., 
+      //   - 3: [p,a,b], [p,b,c], [p,c,a]. (Required by the flip31()).
+      for (i = 0; i < 3; i++) {
+        senext2self(flipshs[i]);
+      }
+      flip31(flipshs, 0);
+      // Delete the three old subfaces.
+      for (i = 0; i < 3; i++) {
+        shellfacedealloc(subfaces, flipshs[i].sh);
+      }
+      if (spivot < 3) {
+        // // Bond the new subface to the new tet [a,b,c,d].
+        tsbond(topcastets[spivot], flipshs[3]);
+        fsym(topcastets[spivot], newface);
+        sesym(flipshs[3], checksh);
+        tsbond(newface, checksh);
+      } else {
+        // Bound the new subface [a,b,c] to the new tet [a,b,c,d].
+        tsbond(fliptets[0], flipshs[3]);
+        fsym(fliptets[0], newface);
+        sesym(flipshs[3], checksh);
+        tsbond(newface, checksh);
+      }
+    } // if (spivot > 0)
+  } // if (checksubfaceflag)
+
+  if (fc->chkencflag & 4) {
+    enqueuetetrahedron(&(fliptets[0]));
+  }
+
+  // Update the point-to-tet map.
+  setpoint2tet(pa, (tetrahedron) fliptets[0].tet);
+  setpoint2tet(pb, (tetrahedron) fliptets[0].tet);
+  setpoint2tet(pc, (tetrahedron) fliptets[0].tet);
+  setpoint2tet(pd, (tetrahedron) fliptets[0].tet);
+
+  if (fc->enqflag > 0) {
+    // Queue faces which may be locally non-Delaunay.
+    flippush(flipstack, &(fliptets[0])); // [a,b,c] (opposite to new point).
+    if (fc->enqflag > 1) {
+      for (i = 0; i < 3; i++) {
+        esym(fliptets[0], newface);
+        flippush(flipstack, &newface);
+        enextself(fliptets[0]);
+      }
+    }
+  }
+
+  recenttet = fliptets[0];
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// flipnm()    Flip an edge through a sequence of elementary flips.          //
+//                                                                           //
+// 'abtets' is an array of 'n' tets in the star of edge [a,b].These tets are //
+// ordered in a counterclockwise cycle with respect to the vector a->b, i.e.,//
+// use the right-hand rule.                                                  //
+//                                                                           //
+// 'level' (>= 0) indicates the current link level. If 'level > 0', we are   //
+// flipping a link edge of an edge [a',b'],  and 'abedgepivot' indicates     //
+// which link edge, i.e., [c',b'] or [a',c'], is [a,b]  These two parameters //
+// allow us to determine the new tets after a 3-to-2 flip, i.e., tets that   //
+// do not inside the reduced star of edge [a',b'].                           //
+//                                                                           //
+// If the flag 'fc->unflip' is set, this routine un-does the flips performed //
+// in flipnm([a,b]) so that the mesh is returned to its original state       //
+// before doing the flipnm([a,b]) operation.                                 //
+//                                                                           //
+// The return value is an integer nn, where nn <= n.  If nn is 2, then the   //
+// edge is flipped.  The first and the second tets in 'abtets' are new tets. //
+// Otherwise, nn > 2, the edge is not flipped, and nn is the number of tets  //
+// in the current star of [a,b].                                             //
+//                                                                           //
+// ASSUMPTIONS:                                                              //
+//  - Neither a nor b is 'dummypoint'.                                       //
+//  - [a,b] must not be a segment.                                           //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+int tetgenmesh::flipnm(triface* abtets, int n, int level, int abedgepivot,
+                       flipconstraints* fc)
+{
+  triface fliptets[3], spintet, flipedge;
+  triface *tmpabtets, *parytet;
+  point pa, pb, pc, pd, pe, pf;
+  REAL ori;
+  int hullflag, hulledgeflag;
+  int reducflag, rejflag;
+  int reflexlinkedgecount;
+  int edgepivot;
+  int n1, nn;
+  int t1ver;
+  int i, j;
+
+  pa = org(abtets[0]);
+  pb = dest(abtets[0]);
+
+  if (n > 3) {
+    // Try to reduce the size of the Star(ab) by flipping a face in it. 
+    reflexlinkedgecount = 0;
+
+    for (i = 0; i < n; i++) {
+      // Let the face of 'abtets[i]' be [a,b,c].
+      if (checksubfaceflag) {
+        if (issubface(abtets[i])) {
+          continue; // Skip a subface.
+        }
+      }
+      // Do not flip this face if it is involved in two Stars.
+      if ((elemcounter(abtets[i]) > 1) ||
+          (elemcounter(abtets[(i - 1 + n) % n]) > 1)) {
+        continue;
+      }
+
+      pc = apex(abtets[i]); 
+      pd = apex(abtets[(i + 1) % n]);
+      pe = apex(abtets[(i - 1 + n) % n]);
+      if ((pd == dummypoint) || (pe == dummypoint)) {
+        continue; // [a,b,c] is a hull face.
+      }
+
+
+      // Decide whether [a,b,c] is flippable or not.
+      reducflag = 0; 
+
+      hullflag = (pc == dummypoint); // pc may be dummypoint.
+      hulledgeflag = 0;
+      if (hullflag == 0) {
+        ori = orient3d(pb, pc, pd, pe); // Is [b,c] locally convex?
+        if (ori > 0) {
+          ori = orient3d(pc, pa, pd, pe); // Is [c,a] locally convex?
+          if (ori > 0) {
+            // Test if [a,b] is locally convex OR flat.
+            ori = orient3d(pa, pb, pd, pe);
+            if (ori > 0) {
+              // Found a 2-to-3 flip: [a,b,c] => [e,d]
+              reducflag = 1;
+            } else if (ori == 0) {
+              // [a,b] is flat.
+              if (n == 4) {
+                // The "flat" tet can be removed immediately by a 3-to-2 flip.
+                reducflag = 1;
+                // Check if [e,d] is a hull edge.
+                pf = apex(abtets[(i + 2) % n]);
+                hulledgeflag = (pf == dummypoint);
+              }
+            }
+          }
+        }
+        if (!reducflag) {
+          reflexlinkedgecount++;
+        }
+      } else {
+        // 'c' is dummypoint.
+        if (n == 4) {
+          // Let the vertex opposite to 'c' is 'f'.
+          // A 4-to-4 flip is possible if the two tets [d,e,f,a] and [e,d,f,b]
+          //   are valid tets. 
+          // Note: When the mesh is not convex, it is possible that [a,b] is
+          //   locally non-convex (at hull faces [a,b,e] and [b,a,d]).
+          //   In this case, an edge flip [a,b] to [e,d] is still possible.
+          pf = apex(abtets[(i + 2) % n]);
+          ori = orient3d(pd, pe, pf, pa);
+          if (ori < 0) {
+            ori = orient3d(pe, pd, pf, pb);
+            if (ori < 0) {
+              // Found a 4-to-4 flip: [a,b] => [e,d]
+              reducflag = 1;
+              ori = 0; // Signal as a 4-to-4 flip (like a co-planar case).
+              hulledgeflag = 1; // [e,d] is a hull edge.
+            }
+          }
+        }
+      } // if (hullflag)
+
+      if (reducflag) {
+        if (nonconvex && hulledgeflag) {
+          // We will create a hull edge [e,d]. Make sure it does not exist.
+          if (getedge(pe, pd, &spintet)) {
+            // The 2-to-3 flip is not a topological valid flip. 
+            reducflag = 0;
+          }
+        }
+      }
+
+      if (reducflag) {
+        // [a,b,c] could be removed by a 2-to-3 flip.
+        rejflag = 0;
+        if (fc->checkflipeligibility) {
+          // Check if the flip can be performed.
+          rejflag = checkflipeligibility(1, pa, pb, pc, pd, pe, level,
+                                         abedgepivot, fc);
+        }
+        if (!rejflag) {
+          // Do flip: [a,b,c] => [e,d].
+          fliptets[0] = abtets[i];
+          fsym(fliptets[0], fliptets[1]); // abtets[i-1].
+          flip23(fliptets, hullflag, fc);
+
+          // Shrink the array 'abtets', maintain the original order.
+          //   Two tets 'abtets[i-1] ([a,b,e,c])' and 'abtets[i] ([a,b,c,d])'
+          //   are flipped, i.e., they do not in Star(ab) anymore. 
+          //   'fliptets[0]' ([e,d,a,b]) is in Star(ab), it is saved in
+          //   'abtets[i-1]' (adjust it to be [a,b,e,d]), see below: 
+          // 
+          //            before                   after
+          //     [0] |___________|        [0] |___________| 
+          //     ... |___________|        ... |___________|
+          //   [i-1] |_[a,b,e,c]_|      [i-1] |_[a,b,e,d]_|
+          //     [i] |_[a,b,c,d]_| -->    [i] |_[a,b,d,#]_|
+          //   [i+1] |_[a,b,d,#]_|      [i+1] |_[a,b,#,*]_|
+          //     ... |___________|        ... |___________|
+          //   [n-2] |___________|      [n-2] |___________| 
+          //   [n-1] |___________|      [n-1] |_[i]_2-t-3_|
+          //
+          edestoppoself(fliptets[0]); // [a,b,e,d]
+          // Increase the counter of this new tet (it is in Star(ab)).
+          increaseelemcounter(fliptets[0]); 
+          abtets[(i - 1 + n) % n] = fliptets[0];
+          for (j = i; j < n - 1; j++) {
+            abtets[j] = abtets[j + 1];  // Upshift
+          }
+          // The last entry 'abtets[n-1]' is empty. It is used in two ways:
+          //   (i) it remembers the vertex 'c' (in 'abtets[n-1].tet'), and
+          //  (ii) it remembers the position [i] where this flip took place.
+          // These information let us to either undo this flip or recover
+          //   the original edge link (for collecting new created tets).
+          abtets[n - 1].tet = (tetrahedron *) pc;
+          abtets[n - 1].ver = 0; // Clear it.
+          // 'abtets[n - 1].ver' is in range [0,11] -- only uses 4 bits.
+          // Use the 5th bit in 'abtets[n - 1].ver' to signal a 2-to-3 flip.
+          abtets[n - 1].ver |= (1 << 4);
+          // The poisition [i] of this flip is saved above the 7th bit.
+          abtets[n - 1].ver |= (i << 6);
+
+          if (fc->collectnewtets) {
+            // Push the two new tets [e,d,b,c] and [e,d,c,a] into a stack.
+            //   Re-use the global array 'cavetetlist'.
+            for (j = 1; j < 3; j++) {
+              cavetetlist->newindex((void **) &parytet);
+              *parytet = fliptets[j]; // fliptets[1], fliptets[2].
+            }
+          }
+
+          // Star(ab) is reduced. Try to flip the edge [a,b].
+          nn = flipnm(abtets, n - 1, level, abedgepivot, fc);
+
+          if (nn == 2) {
+            // The edge has been flipped.
+            return nn;
+          } else { // if (nn > 2)
+            // The edge is not flipped.
+            if (fc->unflip || (ori == 0)) {
+              // Undo the previous 2-to-3 flip, i.e., do a 3-to-2 flip to 
+              //   transform [e,d] => [a,b,c].
+              // 'ori == 0' means that the previous flip created a degenerated
+              //   tet. It must be removed. 
+              // Remember that 'abtets[i-1]' is [a,b,e,d]. We can use it to
+              //   find another two tets [e,d,b,c] and [e,d,c,a].
+              fliptets[0] = abtets[(i-1 + (n-1)) % (n-1)]; // [a,b,e,d]
+              edestoppoself(fliptets[0]); // [e,d,a,b]
+              fnext(fliptets[0], fliptets[1]); // [1] is [e,d,b,c]
+              fnext(fliptets[1], fliptets[2]); // [2] is [e,d,c,a]
+              // Restore the two original tets in Star(ab). 
+              flip32(fliptets, hullflag, fc);
+              // Marktest the two restored tets in Star(ab).
+              for (j = 0; j < 2; j++) {
+                increaseelemcounter(fliptets[j]);
+              }
+              // Expand the array 'abtets', maintain the original order.
+              for (j = n - 2; j>= i; j--) {
+                abtets[j + 1] = abtets[j];  // Downshift
+              }
+              // Insert the two new tets 'fliptets[0]' [a,b,c,d] and 
+              //  'fliptets[1]' [b,a,c,e] into the (i-1)-th and i-th entries, 
+              //  respectively.
+              esym(fliptets[1], abtets[(i - 1 + n) % n]); // [a,b,e,c]
+              abtets[i] = fliptets[0]; // [a,b,c,d]
+              nn++;
+              if (fc->collectnewtets) {
+                // Pop two (flipped) tets from the stack.
+                cavetetlist->objects -= 2;
+              }
+            } // if (unflip || (ori == 0))
+          } // if (nn > 2)
+
+          if (!fc->unflip) {
+            // The flips are not reversed. The current Star(ab) can not be
+            //   further reduced. Return its current size (# of tets).
+            return nn; 
+          }
+          // unflip is set. 
+          // Continue the search for flips.
+        }
+      } // if (reducflag)
+    } // i
+
+    // The Star(ab) is not reduced. 
+    if (reflexlinkedgecount > 0) {
+      // There are reflex edges in the Link(ab).
+      if (((b->fliplinklevel < 0) && (level < autofliplinklevel)) || 
+          ((b->fliplinklevel >= 0) && (level < b->fliplinklevel))) {
+        // Try to reduce the Star(ab) by flipping a reflex edge in Link(ab).
+        for (i = 0; i < n; i++) {
+          // Do not flip this face [a,b,c] if there are two Stars involved.
+          if ((elemcounter(abtets[i]) > 1) ||
+              (elemcounter(abtets[(i - 1 + n) % n]) > 1)) {
+            continue;
+          }
+          pc = apex(abtets[i]);
+          if (pc == dummypoint) {
+            continue; // [a,b] is a hull edge.
+          }
+          pd = apex(abtets[(i + 1) % n]);
+          pe = apex(abtets[(i - 1 + n) % n]);
+          if ((pd == dummypoint) || (pe == dummypoint)) {
+            continue; // [a,b,c] is a hull face.
+          }
+
+
+          edgepivot = 0; // No edge is selected yet.
+
+          // Test if [b,c] is locally convex or flat.
+          ori = orient3d(pb, pc, pd, pe);
+          if (ori <= 0) {
+            // Select the edge [c,b].
+            enext(abtets[i], flipedge); // [b,c,a,d]
+            edgepivot = 1;
+          }
+          if (!edgepivot) {
+            // Test if [c,a] is locally convex or flat.
+            ori = orient3d(pc, pa, pd, pe);
+            if (ori <= 0) {
+              // Select the edge [a,c].
+              eprev(abtets[i], flipedge); // [c,a,b,d].
+              edgepivot = 2;
+            }
+          }
+
+          if (!edgepivot) continue;
+
+          // An edge is selected.
+          if (checksubsegflag) {
+            // Do not flip it if it is a segment.
+            if (issubseg(flipedge)) {
+              if (fc->collectencsegflag) {
+                face checkseg, *paryseg;
+                tsspivot1(flipedge, checkseg);
+                if (!sinfected(checkseg)) {
+                  // Queue this segment in list.
+                  sinfect(checkseg);                
+                  caveencseglist->newindex((void **) &paryseg);
+                  *paryseg = checkseg;
+                }
+              }
+              continue;
+            }
+          }
+
+          // Try to flip the selected edge ([c,b] or [a,c]).
+          esymself(flipedge); 
+          // Count the number of tets at the edge.
+          n1 = 0;
+          j = 0; // Sum of the star counters.
+          spintet = flipedge;
+          while (1) {
+            n1++;
+            j += (elemcounter(spintet)); 
+            fnextself(spintet);
+            if (spintet.tet == flipedge.tet) break;
+          }
+          if (n1 < 3) {
+            // This is only possible when the mesh contains inverted
+            //   elements.  Reprot a bug.
+            terminatetetgen(this, 2);
+          }
+          if (j > 2) {
+            // The Star(flipedge) overlaps other Stars.
+            continue; // Do not flip this edge.
+          }
+
+          if ((b->flipstarsize > 0) && (n1 > b->flipstarsize)) {
+            // The star size exceeds the given limit.
+            continue; // Do not flip it.
+          }
+
+          // Allocate spaces for Star(flipedge).
+          tmpabtets = new triface[n1];
+          // Form the Star(flipedge).
+          j = 0;
+          spintet = flipedge;
+          while (1) {
+            tmpabtets[j] = spintet;
+            // Increase the star counter of this tet.
+            increaseelemcounter(tmpabtets[j]); 
+            j++;
+            fnextself(spintet);
+            if (spintet.tet == flipedge.tet) break;
+          }
+
+          // Try to flip the selected edge away.
+          nn = flipnm(tmpabtets, n1, level + 1, edgepivot, fc);
+
+          if (nn == 2) {
+            // The edge is flipped. Star(ab) is reduced.
+            // Shrink the array 'abtets', maintain the original order.
+            if (edgepivot == 1) {
+              // 'tmpabtets[0]' is [d,a,e,b] => contains [a,b].
+              spintet = tmpabtets[0]; // [d,a,e,b]
+              enextself(spintet);
+              esymself(spintet);
+              enextself(spintet); // [a,b,e,d]
+            } else {
+              // 'tmpabtets[1]' is [b,d,e,a] => contains [a,b].
+              spintet = tmpabtets[1]; // [b,d,e,a]
+              eprevself(spintet);
+              esymself(spintet);
+              eprevself(spintet); // [a,b,e,d]
+            } // edgepivot == 2
+            increaseelemcounter(spintet); // It is in Star(ab).
+            // Put the new tet at [i-1]-th entry.
+            abtets[(i - 1 + n) % n] = spintet;
+            for (j = i; j < n - 1; j++) {
+              abtets[j] = abtets[j + 1];  // Upshift
+            }
+            // Remember the flips in the last entry of the array 'abtets'.
+            // They can be used to recover the flipped edge.
+            abtets[n - 1].tet = (tetrahedron *) tmpabtets; // The star(fedge).
+            abtets[n - 1].ver = 0; // Clear it.
+            // Use the 1st and 2nd bit to save 'edgepivot' (1 or 2).
+            abtets[n - 1].ver |= edgepivot;
+            // Use the 6th bit to signal this n1-to-m1 flip.
+            abtets[n - 1].ver |= (1 << 5); 
+            // The poisition [i] of this flip is saved from 7th to 19th bit.
+            abtets[n - 1].ver |= (i << 6);
+            // The size of the star 'n1' is saved from 20th bit.
+            abtets[n - 1].ver |= (n1 << 19);
+
+            // Remember the flipped link vertex 'c'. It can be used to recover
+            //   the original edge link of [a,b], and to collect new tets.
+            tmpabtets[0].tet = (tetrahedron *) pc;
+            tmpabtets[0].ver = (1 << 5); // Flag it as a vertex handle.
+
+            // Continue to flip the edge [a,b].
+            nn = flipnm(abtets, n - 1, level, abedgepivot, fc);
+
+            if (nn == 2) { 
+              // The edge has been flipped.
+              return nn;
+            } else { // if (nn > 2) {
+              // The edge is not flipped.
+              if (fc->unflip) {
+                // Recover the flipped edge ([c,b] or [a,c]).
+                // The sequence of flips are saved in 'tmpabtets'. 
+                // abtets[(i-1) % (n-1)] is [a,b,e,d], i.e., the tet created by
+                //   the flipping of edge [c,b] or [a,c].It must still exist in
+                //   Star(ab). It is the start tet to recover the flipped edge.
+                if (edgepivot == 1) { 
+                  // The flip edge is [c,b].
+                  tmpabtets[0] = abtets[((i-1)+(n-1))%(n-1)]; // [a,b,e,d]
+                  eprevself(tmpabtets[0]);
+                  esymself(tmpabtets[0]);
+                  eprevself(tmpabtets[0]); // [d,a,e,b]
+                  fsym(tmpabtets[0], tmpabtets[1]); // [a,d,e,c]
+                } else {
+                  // The flip edge is [a,c].
+                  tmpabtets[1] = abtets[((i-1)+(n-1))%(n-1)]; // [a,b,e,d]
+                  enextself(tmpabtets[1]);
+                  esymself(tmpabtets[1]);
+                  enextself(tmpabtets[1]); // [b,d,e,a]
+                  fsym(tmpabtets[1], tmpabtets[0]); // [d,b,e,c]
+                } // if (edgepivot == 2)
+
+                // Recover the flipped edge ([c,b] or [a,c]).
+                flipnm_post(tmpabtets, n1, 2, edgepivot, fc);
+
+                // Insert the two recovered tets into Star(ab).
+                for (j = n - 2; j >= i; j--) {
+                  abtets[j + 1] = abtets[j];  // Downshift
+                }
+                if (edgepivot == 1) {
+                  // tmpabtets[0] is [c,b,d,a] ==> contains [a,b]
+                  // tmpabtets[1] is [c,b,a,e] ==> contains [a,b]
+                  // tmpabtets[2] is [c,b,e,d]
+                  fliptets[0] = tmpabtets[1];
+                  enextself(fliptets[0]);
+                  esymself(fliptets[0]); // [a,b,e,c]
+                  fliptets[1] = tmpabtets[0];
+                  esymself(fliptets[1]);
+                  eprevself(fliptets[1]); // [a,b,c,d]
+                } else {
+                  // tmpabtets[0] is [a,c,d,b] ==> contains [a,b]
+                  // tmpabtets[1] is [a,c,b,e] ==> contains [a,b]
+                  // tmpabtets[2] is [a,c,e,d]
+                  fliptets[0] = tmpabtets[1];
+                  eprevself(fliptets[0]);
+                  esymself(fliptets[0]); // [a,b,e,c]
+                  fliptets[1] = tmpabtets[0];
+                  esymself(fliptets[1]);
+                  enextself(fliptets[1]); // [a,b,c,d]
+                } // edgepivot == 2
+                for (j = 0; j < 2; j++) {
+                  increaseelemcounter(fliptets[j]);
+                }
+                // Insert the two recovered tets into Star(ab).
+                abtets[(i - 1 + n) % n] = fliptets[0];
+                abtets[i] = fliptets[1];
+                nn++;
+                // Release the allocated spaces.
+                delete [] tmpabtets;
+              } // if (unflip)
+            } // if (nn > 2)
+
+            if (!fc->unflip) {
+              // The flips are not reversed. The current Star(ab) can not be
+              //   further reduced. Return its size (# of tets).
+              return nn; 
+            }
+            // unflip is set. 
+            // Continue the search for flips.
+          } else {
+            // The selected edge is not flipped.
+            if (!fc->unflip) {
+              // Release the memory used in this attempted flip.
+              flipnm_post(tmpabtets, n1, nn, edgepivot, fc);
+            }
+            // Decrease the star counters of tets in Star(flipedge).
+            for (j = 0; j < nn; j++) {
+              decreaseelemcounter(tmpabtets[j]);
+            }
+            // Release the allocated spaces.
+            delete [] tmpabtets;
+          }
+        } // i
+      } // if (level...)
+    } // if (reflexlinkedgecount > 0)
+  } else {
+    // Check if a 3-to-2 flip is possible.
+    // Let the three apexes be c, d,and e. Hull tets may be involved. If so, 
+    //   we rearrange them such that the vertex e is dummypoint. 
+    hullflag = 0;
+
+    if (apex(abtets[0]) == dummypoint) {
+      pc = apex(abtets[1]);
+      pd = apex(abtets[2]);
+      pe = apex(abtets[0]);
+      hullflag = 1;
+    } else if (apex(abtets[1]) == dummypoint) {
+      pc = apex(abtets[2]);
+      pd = apex(abtets[0]);
+      pe = apex(abtets[1]);
+      hullflag = 2;
+    } else {
+      pc = apex(abtets[0]);
+      pd = apex(abtets[1]);
+      pe = apex(abtets[2]);
+      hullflag = (pe == dummypoint) ? 3 : 0;
+    }
+
+    reducflag = 0;
+    rejflag = 0;
+
+
+    if (hullflag == 0) {
+      // Make sure that no inverted tet will be created, i.e. the new tets
+      //   [d,c,e,a] and [c,d,e,b] must be valid tets. 
+      ori = orient3d(pd, pc, pe, pa);
+      if (ori < 0) {
+        ori = orient3d(pc, pd, pe, pb);
+        if (ori < 0) {
+          reducflag = 1;
+        }
+      }
+    } else {
+      // [a,b] is a hull edge.
+      //   Note: This can happen when it is in the middle of a 4-to-4 flip.
+      //   Note: [a,b] may even be a non-convex hull edge.
+      if (!nonconvex) {
+        //  The mesh is convex, only do flip if it is a coplanar hull edge.
+        ori = orient3d(pa, pb, pc, pd); 
+        if (ori == 0) {
+          reducflag = 1;
+        }
+      } else { // nonconvex
+        reducflag = 1;
+      }
+      if (reducflag == 1) {
+        // [a,b], [a,b,c] and [a,b,d] are on the convex hull.
+        // Make sure that no inverted tet will be created.
+        point searchpt = NULL, chkpt;
+        REAL bigvol = 0.0, ori1, ori2;
+        // Search an interior vertex which is an apex of edge [c,d].
+        //   In principle, it can be arbitrary interior vertex.  To avoid
+        //   numerical issue, we choose the vertex which belongs to a tet
+        //   't' at edge [c,d] and 't' has the biggest volume.  
+        fliptets[0] = abtets[hullflag % 3]; // [a,b,c,d].
+        eorgoppoself(fliptets[0]);  // [d,c,b,a]
+        spintet = fliptets[0];
+        while (1) {
+          fnextself(spintet);
+          chkpt = oppo(spintet);
+          if (chkpt == pb) break;
+          if ((chkpt != dummypoint) && (apex(spintet) != dummypoint)) {
+            ori = -orient3d(pd, pc, apex(spintet), chkpt);
+            if (ori > bigvol) {
+              bigvol = ori;
+              searchpt = chkpt;
+            }
+          }
+        }
+        if (searchpt != NULL) { 
+          // Now valid the configuration.
+          ori1 = orient3d(pd, pc, searchpt, pa);
+          ori2 = orient3d(pd, pc, searchpt, pb);
+          if (ori1 * ori2 >= 0.0) {
+            reducflag = 0; // Not valid. 
+          } else {
+            ori1 = orient3d(pa, pb, searchpt, pc);
+            ori2 = orient3d(pa, pb, searchpt, pd);
+            if (ori1 * ori2 >= 0.0) {
+              reducflag = 0; // Not valid.
+            }
+          }
+        } else {
+          // No valid searchpt is found.
+          reducflag = 0; // Do not flip it.
+        }
+      } // if (reducflag == 1)
+    } // if (hullflag == 1)
+
+    if (reducflag) {
+      // A 3-to-2 flip is possible.
+      if (checksubfaceflag) {
+        // This edge (must not be a segment) can be flipped ONLY IF it belongs
+        //   to either 0 or 2 subfaces.  In the latter case, a 2-to-2 flip in 
+        //   the surface mesh will be automatically performed within the 
+        //   3-to-2 flip.
+        nn = 0;
+        edgepivot = -1; // Re-use it.
+        for (j = 0; j < 3; j++) {
+          if (issubface(abtets[j])) {
+            nn++; // Found a subface.
+          } else {
+            edgepivot = j;
+          }
+        }
+        if (nn == 1) {
+          // Found only 1 subface containing this edge. This can happen in 
+          //   the boundary recovery phase. The neighbor subface is not yet 
+          //   recovered. This edge should not be flipped at this moment.
+          rejflag = 1; 
+        } else if (nn == 2) {
+          // Found two subfaces. A 2-to-2 flip is possible. Validate it.
+          // Below we check if the two faces [p,q,a] and [p,q,b] are subfaces.
+          eorgoppo(abtets[(edgepivot + 1) % 3], spintet); // [q,p,b,a]
+          if (issubface(spintet)) {
+            rejflag = 1; // Conflict to a 2-to-2 flip.
+          } else {
+            esymself(spintet);
+            if (issubface(spintet)) {
+              rejflag = 1; // Conflict to a 2-to-2 flip.
+            }
+          }
+        } else if (nn == 3) {
+          // Report a bug.
+          terminatetetgen(this, 2);
+        }
+      }
+      if (!rejflag && fc->checkflipeligibility) {
+        // Here we must exchange 'a' and 'b'. Since in the check... function,
+        //   we assume the following point sequence, 'a,b,c,d,e', where
+        //   the face [a,b,c] will be flipped and the edge [e,d] will be
+        //   created. The two new tets are [a,b,c,d] and [b,a,c,e]. 
+        rejflag = checkflipeligibility(2, pc, pd, pe, pb, pa, level, 
+                                       abedgepivot, fc);
+      }
+      if (!rejflag) {
+        // Do flip: [a,b] => [c,d,e]
+        flip32(abtets, hullflag, fc);
+        if (fc->remove_ndelaunay_edge) {
+          if (level == 0) {
+            // It is the desired removing edge. Check if we have improved
+            //   the objective function.
+            if ((fc->tetprism_vol_sum >= 0.0) ||
+                (fabs(fc->tetprism_vol_sum) < fc->bak_tetprism_vol)) {
+              // No improvement! flip back: [c,d,e] => [a,b].
+              flip23(abtets, hullflag, fc);
+              // Increase the element counter -- They are in cavity.
+              for (j = 0; j < 3; j++) {
+                increaseelemcounter(abtets[j]); 
+              }
+              return 3;
+            }
+          } // if (level == 0)
+        }
+        if (fc->collectnewtets) {
+          // Collect new tets.
+          if (level == 0) {
+            // Push the two new tets into stack.
+            for (j = 0; j < 2; j++) {
+              cavetetlist->newindex((void **) &parytet);
+              *parytet = abtets[j];
+            }
+          } else {
+            // Only one of the new tets is collected. The other one is inside
+            //   the reduced edge star. 'abedgepivot' is either '1' or '2'.
+            cavetetlist->newindex((void **) &parytet);
+            if (abedgepivot == 1) { // [c,b]
+              *parytet = abtets[1];
+            } else {
+              *parytet = abtets[0];
+            }
+          }
+        } // if (fc->collectnewtets)
+        return 2;
+      }
+    } // if (reducflag)
+  } // if (n == 3)
+
+  // The current (reduced) Star size.
+  return n;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// flipnm_post()    Post process a n-to-m flip.                              //
+//                                                                           //
+// IMPORTANT: This routine only works when there is no other flip operation  //
+// is done after flipnm([a,b]) which attempts to remove an edge [a,b].       //
+//                                                                           //
+// 'abtets' is an array of 'n' (>= 3) tets which are in the original star of //
+// [a,b] before flipnm([a,b]).  'nn' (< n) is the value returned by flipnm.  //
+// If 'nn == 2', the edge [a,b] has been flipped. 'abtets[0]' and 'abtets[1]'//
+// are [c,d,e,b] and [d,c,e,a], i.e., a 2-to-3 flip can recover the edge [a, //
+// b] and its initial Star([a,b]).  If 'nn >= 3' edge [a,b] still exists in  //
+// current mesh and 'nn' is the current number of tets in Star([a,b]).       //
+//                                                                           //
+// Each 'abtets[i]', where nn <= i < n, saves either a 2-to-3 flip or a      //
+// flipnm([p1,p2]) operation ([p1,p2] != [a,b]) which created the tet        //
+// 'abtets[t-1]', where '0 <= t <= i'.  These information can be used to     //
+// undo the flips performed in flipnm([a,b]) or to collect new tets created  //
+// by the flipnm([a,b]) operation.                                           //
+//                                                                           //
+// Default, this routine only walks through the flips and frees the spaces   //
+// allocated during the flipnm([a,b]) operation.                             //
+//                                                                           //
+// If the flag 'fc->unflip' is set, this routine un-does the flips performed //
+// in flipnm([a,b]) so that the mesh is returned to its original state       //
+// before doing the flipnm([a,b]) operation.                                 //
+//                                                                           //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+int tetgenmesh::flipnm_post(triface* abtets, int n, int nn, int abedgepivot,
+                            flipconstraints* fc)
+{
+  triface fliptets[3], flipface;
+  triface *tmpabtets;
+  int fliptype;
+  int edgepivot;
+  int t, n1;
+  int i, j;
+
+
+  if (nn == 2) {
+    // The edge [a,b] has been flipped.
+    // 'abtets[0]' is [c,d,e,b] or [#,#,#,b].
+    // 'abtets[1]' is [d,c,e,a] or [#,#,#,a].
+    if (fc->unflip) {
+      // Do a 2-to-3 flip to recover the edge [a,b]. There may be hull tets.
+      flip23(abtets, 1, fc);
+      if (fc->collectnewtets) {
+        // Pop up new (flipped) tets from the stack.
+        if (abedgepivot == 0) {
+          // Two new tets were collected.
+          cavetetlist->objects -= 2;
+        } else {
+          // Only one of the two new tets was collected.
+          cavetetlist->objects -= 1;
+        }
+      }
+    } 
+    // The initial size of Star(ab) is 3.
+    nn++;
+  } 
+
+  // Walk through the performed flips.
+  for (i = nn; i < n; i++) {
+    // At the beginning of each step 'i', the size of the Star([a,b]) is 'i'.
+    // At the end of this step, the size of the Star([a,b]) is 'i+1'.
+    // The sizes of the Link([a,b]) are the same.
+    fliptype = ((abtets[i].ver >> 4) & 3); // 0, 1, or 2.
+    if (fliptype == 1) {
+      // It was a 2-to-3 flip: [a,b,c]->[e,d].
+      t = (abtets[i].ver >> 6);
+      if (fc->unflip) {
+        if (b->verbose > 2) {
+          printf("      Recover a 2-to-3 flip at f[%d].\n", t);
+        }
+        // 'abtets[(t-1)%i]' is the tet [a,b,e,d] in current Star(ab), i.e.,
+        //   it is created by a 2-to-3 flip [a,b,c] => [e,d].
+        fliptets[0] = abtets[((t - 1) + i) % i]; // [a,b,e,d]
+        eprevself(fliptets[0]);
+        esymself(fliptets[0]);
+        enextself(fliptets[0]); // [e,d,a,b]
+        fnext(fliptets[0], fliptets[1]); // [e,d,b,c]
+        fnext(fliptets[1], fliptets[2]); // [e,d,c,a]
+        // Do a 3-to-2 flip: [e,d] => [a,b,c].
+        // NOTE: hull tets may be invloved.
+        flip32(fliptets, 1, fc);
+        // Expand the array 'abtets', maintain the original order.
+        // The new array length is (i+1).
+        for (j = i - 1; j >= t; j--) {
+          abtets[j + 1] = abtets[j];  // Downshift
+        }
+        // The tet abtets[(t-1)%i] is deleted. Insert the two new tets 
+        //   'fliptets[0]' [a,b,c,d] and 'fliptets[1]' [b,a,c,e] into
+        //   the (t-1)-th and t-th entries, respectively.
+        esym(fliptets[1], abtets[((t-1) + (i+1)) % (i+1)]); // [a,b,e,c]
+        abtets[t] = fliptets[0]; // [a,b,c,d]
+        if (fc->collectnewtets) {
+          // Pop up two (flipped) tets from the stack.
+          cavetetlist->objects -= 2;
+        }
+      } 
+    } else if (fliptype == 2) {
+      tmpabtets = (triface *) (abtets[i].tet);
+      n1 = ((abtets[i].ver >> 19) & 8191); // \sum_{i=0^12}{2^i} = 8191
+      edgepivot = (abtets[i].ver & 3); 
+      t = ((abtets[i].ver >> 6) & 8191);
+      if (fc->unflip) {        
+        if (b->verbose > 2) {
+          printf("      Recover a %d-to-m flip at e[%d] of f[%d].\n", n1, 
+                 edgepivot, t);
+        }
+        // Recover the flipped edge ([c,b] or [a,c]).
+        // abtets[(t - 1 + i) % i] is [a,b,e,d], i.e., the tet created by
+        //   the flipping of edge [c,b] or [a,c]. It must still exist in
+        //   Star(ab). Use it to recover the flipped edge.
+        if (edgepivot == 1) { 
+          // The flip edge is [c,b].
+          tmpabtets[0] = abtets[(t - 1 + i) % i]; // [a,b,e,d]
+          eprevself(tmpabtets[0]);
+          esymself(tmpabtets[0]);
+          eprevself(tmpabtets[0]); // [d,a,e,b]
+          fsym(tmpabtets[0], tmpabtets[1]); // [a,d,e,c]
+        } else {
+          // The flip edge is [a,c].
+          tmpabtets[1] = abtets[(t - 1 + i) % i]; // [a,b,e,d]
+          enextself(tmpabtets[1]);
+          esymself(tmpabtets[1]);
+          enextself(tmpabtets[1]); // [b,d,e,a]
+          fsym(tmpabtets[1], tmpabtets[0]); // [d,b,e,c]
+        } // if (edgepivot == 2)
+
+        // Do a n1-to-m1 flip to recover the flipped edge ([c,b] or [a,c]).
+        flipnm_post(tmpabtets, n1, 2, edgepivot, fc);
+
+        // Insert the two recovered tets into the original Star(ab).
+        for (j = i - 1; j >= t; j--) {
+          abtets[j + 1] = abtets[j];  // Downshift
+        }
+        if (edgepivot == 1) {
+          // tmpabtets[0] is [c,b,d,a] ==> contains [a,b]
+          // tmpabtets[1] is [c,b,a,e] ==> contains [a,b]
+          // tmpabtets[2] is [c,b,e,d]
+          fliptets[0] = tmpabtets[1];
+          enextself(fliptets[0]);
+          esymself(fliptets[0]); // [a,b,e,c]
+          fliptets[1] = tmpabtets[0];
+          esymself(fliptets[1]);
+          eprevself(fliptets[1]); // [a,b,c,d]
+        } else {
+          // tmpabtets[0] is [a,c,d,b] ==> contains [a,b]
+          // tmpabtets[1] is [a,c,b,e] ==> contains [a,b]
+          // tmpabtets[2] is [a,c,e,d]
+          fliptets[0] = tmpabtets[1];
+          eprevself(fliptets[0]);
+          esymself(fliptets[0]); // [a,b,e,c]
+          fliptets[1] = tmpabtets[0];
+          esymself(fliptets[1]);
+          enextself(fliptets[1]); // [a,b,c,d]
+        } // edgepivot == 2
+        // Insert the two recovered tets into Star(ab).
+        abtets[((t-1) + (i+1)) % (i+1)] = fliptets[0];
+        abtets[t] = fliptets[1];
+      } 
+      else {
+        // Only free the spaces.
+        flipnm_post(tmpabtets, n1, 2, edgepivot, fc);
+      } // if (!unflip)
+      if (b->verbose > 2) {
+        printf("      Release %d spaces at f[%d].\n", n1, i);
+      }
+      delete [] tmpabtets;
+    }
+  } // i
+
+  return 1;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// insertpoint()    Insert a point into current tetrahedralization.          //
+//                                                                           //
+// The Bowyer-Watson (B-W) algorithm is used to add a new point p into the   //
+// tetrahedralization T. It first finds a "cavity", denoted as C, in T,  C   //
+// consists of tetrahedra in T that "conflict" with p.  If T is a Delaunay   //
+// tetrahedralization, then all boundary faces (triangles) of C are visible  //
+// by p, i.e.,C is star-shaped. We can insert p into T by first deleting all //
+// tetrahedra in C, then creating new tetrahedra formed by boundary faces of //
+// C and p.  If T is not a DT, then C may be not star-shaped.  It must be    //
+// modified so that it becomes star-shaped.                                  //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+int tetgenmesh::insertpoint(point insertpt, triface *searchtet, face *splitsh,
+                            face *splitseg, insertvertexflags *ivf)
+{
+  arraypool *swaplist;
+  triface *cavetet, spintet, neightet, neineitet, *parytet;
+  triface oldtet, newtet, newneitet;
+  face checksh, neighsh, *parysh;
+  face checkseg, *paryseg;
+  point *pts, pa, pb, pc, *parypt;
+  enum locateresult loc = OUTSIDE;
+  REAL sign, ori;
+  REAL attrib, volume;
+  bool enqflag;
+  int t1ver;
+  int i, j, k, s;
+
+  if (b->verbose > 2) {
+    printf("      Insert point %d\n", pointmark(insertpt));
+  }
+
+  // Locate the point.
+  if (searchtet->tet != NULL) {
+    loc = (enum locateresult) ivf->iloc;
+  }
+
+  if (loc == OUTSIDE) {
+    if (searchtet->tet == NULL) {
+      if (!b->weighted) {
+        randomsample(insertpt, searchtet);
+      } else {
+        // Weighted DT. There may exist dangling vertex. 
+        *searchtet = recenttet;
+      }
+    }
+    // Locate the point.
+    loc = locate(insertpt, searchtet); 
+  }
+
+  ivf->iloc = (int) loc; // The return value.
+
+  if (b->weighted) {
+    if (loc != OUTSIDE) {
+      // Check if this vertex is regular.
+      pts = (point *) searchtet->tet;
+      sign = orient4d_s(pts[4], pts[5], pts[6], pts[7], insertpt,
+                        pts[4][3], pts[5][3], pts[6][3], pts[7][3],
+                        insertpt[3]);
+      if (sign > 0) {
+        // This new vertex lies above the lower hull. Do not insert it.
+        ivf->iloc = (int) NONREGULAR;
+        return 0;
+      }
+    }
+  }
+
+  // Create the initial cavity C(p) which contains all tetrahedra that
+  //   intersect p. It may include 1, 2, or n tetrahedra.
+  // If p lies on a segment or subface, also create the initial sub-cavity
+  //   sC(p) which contains all subfaces (and segment) which intersect p.
+
+  if (loc == OUTSIDE) {
+    flip14count++;
+    // The current hull will be enlarged.
+    // Add four adjacent boundary tets into list.
+    for (i = 0; i < 4; i++) {
+      decode(searchtet->tet[i], neightet);
+      neightet.ver = epivot[neightet.ver];
+      cavebdrylist->newindex((void **) &parytet);
+      *parytet = neightet;
+    }
+    infect(*searchtet);
+    caveoldtetlist->newindex((void **) &parytet);
+    *parytet = *searchtet;
+  } else if (loc == INTETRAHEDRON) {
+    flip14count++;
+    // Add four adjacent boundary tets into list.
+    for (i = 0; i < 4; i++) {
+      decode(searchtet->tet[i], neightet);
+      neightet.ver = epivot[neightet.ver];
+      cavebdrylist->newindex((void **) &parytet);
+      *parytet = neightet;
+    }
+    infect(*searchtet);
+    caveoldtetlist->newindex((void **) &parytet);
+    *parytet = *searchtet;
+  } else if (loc == ONFACE) {
+    flip26count++;
+    // Add six adjacent boundary tets into list.
+    j = (searchtet->ver & 3); // The current face number.
+    for (i = 1; i < 4; i++) { 
+      decode(searchtet->tet[(j + i) % 4], neightet);
+      neightet.ver = epivot[neightet.ver];
+      cavebdrylist->newindex((void **) &parytet);
+      *parytet = neightet;
+    }
+    decode(searchtet->tet[j], spintet);
+    j = (spintet.ver & 3); // The current face number.
+    for (i = 1; i < 4; i++) {
+      decode(spintet.tet[(j + i) % 4], neightet);
+      neightet.ver = epivot[neightet.ver];
+      cavebdrylist->newindex((void **) &parytet);
+      *parytet = neightet;
+    }
+    infect(spintet);
+    caveoldtetlist->newindex((void **) &parytet);
+    *parytet = spintet;
+    infect(*searchtet);
+    caveoldtetlist->newindex((void **) &parytet);
+    *parytet = *searchtet;
+
+    if (ivf->splitbdflag) { 
+      if ((splitsh != NULL) && (splitsh->sh != NULL)) {
+        // Create the initial sub-cavity sC(p).
+        smarktest(*splitsh);
+        caveshlist->newindex((void **) &parysh);
+        *parysh = *splitsh;
+      }
+    } // if (splitbdflag)
+  } else if (loc == ONEDGE) {
+    flipn2ncount++;
+    // Add all adjacent boundary tets into list.
+    spintet = *searchtet;
+    while (1) {
+      eorgoppo(spintet, neightet);
+      decode(neightet.tet[neightet.ver & 3], neightet);
+      neightet.ver = epivot[neightet.ver];
+      cavebdrylist->newindex((void **) &parytet);
+      *parytet = neightet;
+      edestoppo(spintet, neightet);
+      decode(neightet.tet[neightet.ver & 3], neightet);
+      neightet.ver = epivot[neightet.ver];
+      cavebdrylist->newindex((void **) &parytet);
+      *parytet = neightet;
+      infect(spintet);
+      caveoldtetlist->newindex((void **) &parytet);
+      *parytet = spintet;
+      fnextself(spintet);
+      if (spintet.tet == searchtet->tet) break;
+    } // while (1)
+
+    if (ivf->splitbdflag) {
+      // Create the initial sub-cavity sC(p).
+      if ((splitseg != NULL) && (splitseg->sh != NULL)) {
+        smarktest(*splitseg);
+        splitseg->shver = 0;
+        spivot(*splitseg, *splitsh);
+      }
+      if (splitsh != NULL) {
+        if (splitsh->sh != NULL) {
+          // Collect all subfaces share at this edge.
+          pa = sorg(*splitsh);
+          neighsh = *splitsh;
+          while (1) {
+            // Adjust the origin of its edge to be 'pa'.
+            if (sorg(neighsh) != pa) {
+              sesymself(neighsh);
+            }
+            // Add this face into list (in B-W cavity).
+            smarktest(neighsh);
+            caveshlist->newindex((void **) &parysh);
+            *parysh = neighsh;
+            // Add this face into face-at-splitedge list.
+            cavesegshlist->newindex((void **) &parysh);
+            *parysh = neighsh;
+            // Go to the next face at the edge.
+            spivotself(neighsh);
+            // Stop if all faces at the edge have been visited.
+            if (neighsh.sh == splitsh->sh) break;
+            if (neighsh.sh == NULL) break;
+          } // while (1)
+        } // if (not a dangling segment)
+      }
+    } // if (splitbdflag)
+  } else if (loc == INSTAR) {
+    // We assume that all tets in the star are given in 'caveoldtetlist',
+    //   and they are all infected.
+    // Collect the boundary faces of the star.
+    for (i = 0; i < caveoldtetlist->objects; i++) {
+      cavetet = (triface *) fastlookup(caveoldtetlist, i);
+      // Check its 4 neighbor tets.
+      for (j = 0; j < 4; j++) {
+        decode(cavetet->tet[j], neightet);
+        if (!infected(neightet)) {
+          // It's a boundary face.
+          neightet.ver = epivot[neightet.ver];
+          cavebdrylist->newindex((void **) &parytet);
+          *parytet = neightet;
+        }
+      }
+    }
+  } else if (loc == ONVERTEX) {
+    // The point already exist. Do nothing and return.
+    return 0;
+  } 
+
+
+  if (ivf->bowywat) {
+    // Update the cavity C(p) using the Bowyer-Watson algorithm.
+    swaplist = cavetetlist;
+    cavetetlist = cavebdrylist;
+    cavebdrylist = swaplist;
+    for (i = 0; i < cavetetlist->objects; i++) {
+      // 'cavetet' is an adjacent tet at outside of the cavity.
+      cavetet = (triface *) fastlookup(cavetetlist, i);
+      // The tet may be tested and included in the (enlarged) cavity.
+      if (!infected(*cavetet)) {
+        // Check for two possible cases for this tet: 
+        //   (1) It is a cavity tet, or
+        //   (2) it is a cavity boundary face.
+        enqflag = false;
+        if (!marktested(*cavetet)) {
+          // Do Delaunay (in-sphere) test.
+          pts = (point *) cavetet->tet;
+          if (pts[7] != dummypoint) {
+            // A volume tet. Operate on it.
+            if (b->weighted) {
+              sign = orient4d_s(pts[4], pts[5], pts[6], pts[7], insertpt,
+                                pts[4][3], pts[5][3], pts[6][3], pts[7][3],
+                                insertpt[3]);
+            } else {
+              sign = insphere_s(pts[4], pts[5], pts[6], pts[7], insertpt);
+            }
+            enqflag = (sign < 0.0);
+          } else {
+            if (!nonconvex) {
+              // Test if this hull face is visible by the new point. 
+              ori = orient3d(pts[4], pts[5], pts[6], insertpt); 
+              if (ori < 0) {
+                // A visible hull face. 
+                // Include it in the cavity. The convex hull will be enlarged.
+                enqflag = true; 
+              } else if (ori == 0.0) {
+                // A coplanar hull face. We need to test if this hull face is
+                //   Delaunay or not. We test if the adjacent tet (not faked)
+                //   of this hull face is Delaunay or not.
+                decode(cavetet->tet[3], neineitet);
+                if (!infected(neineitet)) {
+                  if (!marktested(neineitet)) {
+                    // Do Delaunay test on this tet.
+                    pts = (point *) neineitet.tet;
+                    if (b->weighted) {
+                      sign = orient4d_s(pts[4],pts[5],pts[6],pts[7], insertpt,
+                                        pts[4][3], pts[5][3], pts[6][3], 
+                                        pts[7][3], insertpt[3]);
+                    } else {
+                      sign = insphere_s(pts[4],pts[5],pts[6],pts[7], insertpt);
+                    }
+                    enqflag = (sign < 0.0);
+                  } 
+                } else {
+                  // The adjacent tet is non-Delaunay. The hull face is non-
+                  //   Delaunay as well. Include it in the cavity.
+                  enqflag = true;
+                } // if (!infected(neineitet))
+              } // if (ori == 0.0)
+            } else {
+              // A hull face (must be a subface).
+              // We FIRST include it in the initial cavity if the adjacent tet
+              //   (not faked) of this hull face is not Delaunay wrt p.
+              //   Whether it belongs to the final cavity will be determined
+              //   during the validation process. 'validflag'.
+              decode(cavetet->tet[3], neineitet);
+              if (!infected(neineitet)) {
+                if (!marktested(neineitet)) {
+                  // Do Delaunay test on this tet.
+                  pts = (point *) neineitet.tet;
+                  if (b->weighted) {
+                    sign = orient4d_s(pts[4],pts[5],pts[6],pts[7], insertpt,
+                                      pts[4][3], pts[5][3], pts[6][3], 
+                                      pts[7][3], insertpt[3]);
+                  } else {
+                    sign = insphere_s(pts[4],pts[5],pts[6],pts[7], insertpt);
+                  }
+                  enqflag = (sign < 0.0);
+                } 
+              } else {
+                // The adjacent tet is non-Delaunay. The hull face is non-
+                //   Delaunay as well. Include it in the cavity.
+                enqflag = true;
+              } // if (infected(neineitet))
+            } // if (nonconvex)
+          } // if (pts[7] != dummypoint)
+          marktest(*cavetet); // Only test it once.
+        } // if (!marktested(*cavetet))
+
+        if (enqflag) {
+          // Found a tet in the cavity. Put other three faces in check list.
+          k = (cavetet->ver & 3); // The current face number
+          for (j = 1; j < 4; j++) {
+            decode(cavetet->tet[(j + k) % 4], neightet);
+            cavetetlist->newindex((void **) &parytet);
+            *parytet = neightet;
+          }
+          infect(*cavetet);
+          caveoldtetlist->newindex((void **) &parytet);
+          *parytet = *cavetet;
+        } else {
+          // Found a boundary face of the cavity. 
+          cavetet->ver = epivot[cavetet->ver];
+          cavebdrylist->newindex((void **) &parytet);
+          *parytet = *cavetet;
+        }
+      } // if (!infected(*cavetet))
+    } // i
+
+    cavetetlist->restart(); // Clear the working list.
+  } // if (ivf->bowywat)
+
+  if (checksubsegflag) {
+    // Collect all segments of C(p).
+    shellface *ssptr;
+    for (i = 0; i < caveoldtetlist->objects; i++) {
+      cavetet = (triface *) fastlookup(caveoldtetlist, i);
+      if ((ssptr = (shellface*) cavetet->tet[8]) != NULL) {
+        for (j = 0; j < 6; j++) {
+          if (ssptr[j]) {
+            sdecode(ssptr[j], checkseg);
+            if (!sinfected(checkseg)) {
+              sinfect(checkseg);
+              cavetetseglist->newindex((void **) &paryseg);
+              *paryseg = checkseg;
+            }
+          }
+        } // j
+      }
+    } // i
+    // Uninfect collected segments.
+    for (i = 0; i < cavetetseglist->objects; i++) {
+      paryseg = (face *) fastlookup(cavetetseglist, i);
+      suninfect(*paryseg);
+    }
+
+  } // if (checksubsegflag)
+
+  if (checksubfaceflag) {
+    // Collect all subfaces of C(p).
+    shellface *sptr;
+    for (i = 0; i < caveoldtetlist->objects; i++) {
+      cavetet = (triface *) fastlookup(caveoldtetlist, i);
+      if ((sptr = (shellface*) cavetet->tet[9]) != NULL) {
+        for (j = 0; j < 4; j++) {
+          if (sptr[j]) {
+            sdecode(sptr[j], checksh);
+            if (!sinfected(checksh)) {
+              sinfect(checksh);
+              cavetetshlist->newindex((void **) &parysh);
+              *parysh = checksh;
+            }
+          }
+        } // j
+      }
+    } // i
+    // Uninfect collected subfaces.
+    for (i = 0; i < cavetetshlist->objects; i++) {
+      parysh = (face *) fastlookup(cavetetshlist, i);
+      suninfect(*parysh);
+    }
+
+  } // if (checksubfaceflag)
+
+  if ((ivf->iloc == (int) OUTSIDE) && ivf->refineflag) {
+    // The vertex lies outside of the domain. And it does not encroach
+    //   upon any boundary segment or subface. Do not insert it.
+    insertpoint_abort(splitseg, ivf);
+    return 0;
+  }
+
+  if (ivf->splitbdflag) { 
+    // The new point locates in surface mesh. Update the sC(p). 
+    // We have already 'smarktested' the subfaces which directly intersect
+    //   with p in 'caveshlist'. From them, we 'smarktest' their neighboring
+    //   subfaces which are included in C(p). Do not across a segment.
+    for (i = 0; i < caveshlist->objects; i++) {
+      parysh = (face *) fastlookup(caveshlist, i);
+      checksh = *parysh;
+      for (j = 0; j < 3; j++) {
+        if (!isshsubseg(checksh)) {
+          spivot(checksh, neighsh);
+          if (!smarktested(neighsh)) {
+            stpivot(neighsh, neightet);
+            if (infected(neightet)) {
+              fsymself(neightet);
+              if (infected(neightet)) {
+                // This subface is inside C(p). 
+                // Check if its diametrical circumsphere encloses 'p'.
+                //   The purpose of this check is to avoid forming invalid
+                //   subcavity in surface mesh.
+                sign = incircle3d(sorg(neighsh), sdest(neighsh),
+                                  sapex(neighsh), insertpt);
+                if (sign < 0) {
+                  smarktest(neighsh);
+                  caveshlist->newindex((void **) &parysh);
+                  *parysh = neighsh;
+                }
+              }
+            }
+          }
+        }
+        senextself(checksh);
+      } // j
+    } // i
+  } // if (ivf->splitbdflag)
+
+  if (ivf->validflag) {
+    // Validate C(p) and update it if it is not star-shaped.
+    int cutcount = 0;
+
+    if (ivf->respectbdflag) {
+      // The initial cavity may include subfaces which are not on the facets
+      //   being splitting. Find them and make them as boundary of C(p). 
+      // Comment: We have already 'smarktested' the subfaces in sC(p). They
+      //   are completely inside C(p). 
+      for (i = 0; i < cavetetshlist->objects; i++) {
+        parysh = (face *) fastlookup(cavetetshlist, i);
+        stpivot(*parysh, neightet);
+        if (infected(neightet)) {
+          fsymself(neightet);
+          if (infected(neightet)) {
+            // Found a subface inside C(p).
+            if (!smarktested(*parysh)) {
+              // It is possible that this face is a boundary subface.
+              // Check if it is a hull face.
+              //assert(apex(neightet) != dummypoint);
+              if (oppo(neightet) != dummypoint) {
+                fsymself(neightet);
+              }
+              if (oppo(neightet) != dummypoint) {
+                ori = orient3d(org(neightet), dest(neightet), apex(neightet),
+                               insertpt);
+                if (ori < 0) {
+                  // A visible face, get its neighbor face.
+                  fsymself(neightet);
+                  ori = -ori; // It must be invisible by p.
+                }
+              } else {
+                // A hull tet. It needs to be cut.
+                ori = 1;
+              }
+              // Cut this tet if it is either invisible by or coplanar with p.
+              if (ori >= 0) {
+                uninfect(neightet);
+                unmarktest(neightet);
+                cutcount++;
+                neightet.ver = epivot[neightet.ver];
+                cavebdrylist->newindex((void **) &parytet);
+                *parytet = neightet;
+                // Add three new faces to find new boundaries.
+                for (j = 0; j < 3; j++) {
+                  esym(neightet, neineitet);
+                  neineitet.ver = epivot[neineitet.ver];
+                  cavebdrylist->newindex((void **) &parytet);
+                  *parytet = neineitet;
+                  enextself(neightet);
+                }
+              } // if (ori >= 0) 
+            }
+          }
+        }
+      } // i
+
+      // The initial cavity may include segments in its interior. We need to
+      //   Update the cavity so that these segments are on the boundary of
+      //   the cavity.
+      for (i = 0; i < cavetetseglist->objects; i++) {
+        paryseg = (face *) fastlookup(cavetetseglist, i);
+        // Check this segment if it is not a splitting segment.
+        if (!smarktested(*paryseg)) {
+          sstpivot1(*paryseg, neightet);
+          spintet = neightet;
+          while (1) {
+            if (!infected(spintet)) break;
+            fnextself(spintet);
+            if (spintet.tet == neightet.tet) break;
+          }
+          if (infected(spintet)) {
+            // Find an adjacent tet at this segment such that both faces
+            //   at this segment are not visible by p.
+            pa = org(neightet);
+            pb = dest(neightet);
+            spintet = neightet;
+            j = 0;
+            while (1) {
+              // Check if this face is visible by p.
+              pc = apex(spintet);
+              if (pc != dummypoint) {
+                ori = orient3d(pa, pb, pc, insertpt);
+                if (ori >= 0) {
+                  // Not visible. Check another face in this tet.
+                  esym(spintet, neineitet);
+                  pc = apex(neineitet);
+                  if (pc != dummypoint) {
+                    ori = orient3d(pb, pa, pc, insertpt);
+                    if (ori >= 0) {
+                      // Not visible. Found this face.
+                      j = 1; // Flag that it is found.
+                      break;
+                    }
+                  }
+                }
+              }
+              fnextself(spintet);
+              if (spintet.tet == neightet.tet) break;
+            }
+            if (j == 0) {
+              // Not found such a face.
+              terminatetetgen(this, 2); 
+            }
+            neightet = spintet;
+            if (b->verbose > 3) {
+               printf("        Cut tet (%d, %d, %d, %d)\n", 
+                      pointmark(org(neightet)), pointmark(dest(neightet)),
+                      pointmark(apex(neightet)), pointmark(oppo(neightet)));
+            }
+            uninfect(neightet);
+            unmarktest(neightet);
+            cutcount++;
+            neightet.ver = epivot[neightet.ver];
+            cavebdrylist->newindex((void **) &parytet);
+            *parytet = neightet;
+            // Add three new faces to find new boundaries.
+            for (j = 0; j < 3; j++) {
+              esym(neightet, neineitet);
+              neineitet.ver = epivot[neineitet.ver];
+              cavebdrylist->newindex((void **) &parytet);
+              *parytet = neineitet;
+              enextself(neightet);
+            }
+          }
+        }
+      } // i
+    } // if (ivf->respectbdflag)
+
+    // Update the cavity by removing invisible faces until it is star-shaped.
+    for (i = 0; i < cavebdrylist->objects; i++) {
+      cavetet = (triface *) fastlookup(cavebdrylist, i);
+      // 'cavetet' is an exterior tet adjacent to the cavity.
+      // Check if its neighbor is inside C(p).
+      fsym(*cavetet, neightet);
+      if (infected(neightet)) {        
+        if (apex(*cavetet) != dummypoint) {
+          // It is a cavity boundary face. Check its visibility.
+          if (oppo(neightet) != dummypoint) {
+            // Check if this face is visible by the new point.
+            if (issubface(neightet)) {
+              // We should only create a new tet that has a reasonable volume.
+              // Re-use 'volume' and 'attrib'.
+              pa = org(*cavetet);
+              pb = dest(*cavetet);
+              pc = apex(*cavetet);
+              volume = orient3dfast(pa, pb, pc, insertpt);
+              attrib = distance(pa, pb) * distance(pb, pc) * distance(pc, pa);
+              if ((fabs(volume) / attrib) < b->epsilon) {
+                ori = 0.0;
+              } else {
+                ori = orient3d(pa, pb, pc, insertpt); 
+              }
+            } else {
+              ori = orient3d(org(*cavetet), dest(*cavetet), apex(*cavetet),
+                             insertpt); 
+            }
+            enqflag = (ori > 0);
+            // Comment: if ori == 0 (coplanar case), we also cut the tet.
+          } else {
+            // It is a hull face. And its adjacent tet (at inside of the 
+            //   domain) has been cut from the cavity. Cut it as well.
+            //assert(nonconvex);
+            enqflag = false;
+          }
+        } else {
+          enqflag = true; // A hull edge.
+        }
+        if (enqflag) {
+          // This face is valid, save it.
+          cavetetlist->newindex((void **) &parytet);
+          *parytet = *cavetet; 
+        } else {
+          uninfect(neightet);
+          unmarktest(neightet);
+          cutcount++;
+          // Add three new faces to find new boundaries.
+          for (j = 0; j < 3; j++) {
+            esym(neightet, neineitet);
+            neineitet.ver = epivot[neineitet.ver];
+            cavebdrylist->newindex((void **) &parytet);
+            *parytet = neineitet;
+            enextself(neightet);
+          }
+          // 'cavetet' is not on the cavity boundary anymore.
+          unmarktest(*cavetet);
+        }
+      } else {
+        // 'cavetet' is not on the cavity boundary anymore.
+        unmarktest(*cavetet);
+      }
+    } // i
+
+    if (cutcount > 0) {
+      // The cavity has been updated.
+      // Update the cavity boundary faces.
+      cavebdrylist->restart();
+      for (i = 0; i < cavetetlist->objects; i++) {
+        cavetet = (triface *) fastlookup(cavetetlist, i);
+        // 'cavetet' was an exterior tet adjacent to the cavity.
+        fsym(*cavetet, neightet);
+        if (infected(neightet)) {
+          // It is a cavity boundary face.
+          cavebdrylist->newindex((void **) &parytet);
+          *parytet = *cavetet;
+        } else {
+          // Not a cavity boundary face.
+          unmarktest(*cavetet);
+        }
+      }
+
+      // Update the list of old tets.
+      cavetetlist->restart();
+      for (i = 0; i < caveoldtetlist->objects; i++) {
+        cavetet = (triface *) fastlookup(caveoldtetlist, i);
+        if (infected(*cavetet)) {
+          cavetetlist->newindex((void **) &parytet);
+          *parytet = *cavetet;
+        }
+      }
+      // Swap 'cavetetlist' and 'caveoldtetlist'.
+      swaplist = caveoldtetlist;
+      caveoldtetlist = cavetetlist;
+      cavetetlist = swaplist;
+
+      // The cavity should contain at least one tet.
+      if (caveoldtetlist->objects == 0l) {
+        insertpoint_abort(splitseg, ivf);
+        ivf->iloc = (int) BADELEMENT;
+        return 0;
+      }
+
+      if (ivf->splitbdflag) { 
+        int cutshcount = 0;
+        // Update the sub-cavity sC(p).
+        for (i = 0; i < caveshlist->objects; i++) {
+          parysh = (face *) fastlookup(caveshlist, i);
+          if (smarktested(*parysh)) {
+            enqflag = false;
+            stpivot(*parysh, neightet);
+            if (infected(neightet)) {
+              fsymself(neightet);
+              if (infected(neightet)) {
+                enqflag = true;
+              }
+            }
+            if (!enqflag) {
+              sunmarktest(*parysh);
+              // Use the last entry of this array to fill this entry.
+              j = caveshlist->objects - 1;
+              checksh = * (face *) fastlookup(caveshlist, j);
+              *parysh = checksh;
+              cutshcount++;
+              caveshlist->objects--; // The list is shrinked.
+              i--;
+            }
+          }
+        }
+
+        if (cutshcount > 0) {
+          i = 0; // Count the number of invalid subfaces/segments.
+          // Valid the updated sub-cavity sC(p).
+          if (loc == ONFACE) {
+            if ((splitsh != NULL) && (splitsh->sh != NULL)) {
+              // The to-be split subface should be in sC(p).
+              if (!smarktested(*splitsh)) i++;
+            }
+          } else if (loc == ONEDGE) {
+            if ((splitseg != NULL) && (splitseg->sh != NULL)) {
+              // The to-be split segment should be in sC(p).
+              if (!smarktested(*splitseg)) i++;
+            }
+            if ((splitsh != NULL) && (splitsh->sh != NULL)) {
+              // All subfaces at this edge should be in sC(p).
+              pa = sorg(*splitsh);
+              neighsh = *splitsh;
+              while (1) {
+                // Adjust the origin of its edge to be 'pa'.
+                if (sorg(neighsh) != pa) {
+                  sesymself(neighsh);
+                }
+                // Add this face into list (in B-W cavity).
+                if (!smarktested(neighsh)) i++;
+                // Go to the next face at the edge.
+                spivotself(neighsh);
+                // Stop if all faces at the edge have been visited.
+                if (neighsh.sh == splitsh->sh) break;
+                if (neighsh.sh == NULL) break;
+              } // while (1)
+            }
+          }
+
+          if (i > 0) {
+            // The updated sC(p) is invalid. Do not insert this vertex.
+            insertpoint_abort(splitseg, ivf);
+            ivf->iloc = (int) BADELEMENT;
+            return 0;
+          }
+        } // if (cutshcount > 0)
+      } // if (ivf->splitbdflag)
+    } // if (cutcount > 0)
+
+  } // if (ivf->validflag)
+
+  if (ivf->refineflag) {
+    // The new point is inserted by Delaunay refinement, i.e., it is the 
+    //   circumcenter of a tetrahedron, or a subface, or a segment.
+    //   Do not insert this point if the tetrahedron, or subface, or segment
+    //   is not inside the final cavity.
+    if (((ivf->refineflag == 1) && !infected(ivf->refinetet)) ||
+        ((ivf->refineflag == 2) && !smarktested(ivf->refinesh))) {
+      insertpoint_abort(splitseg, ivf);
+      ivf->iloc = (int) BADELEMENT;
+      return 0;
+    }
+  } // if (ivf->refineflag)
+
+  if (b->plc && (loc != INSTAR)) {
+    // Reject the new point if it lies too close to an existing point (b->plc),
+    // or it lies inside a protecting ball of near vertex (ivf->rejflag & 4).
+    // Collect the list of vertices of the initial cavity.
+    if (loc == OUTSIDE) {
+      pts = (point *) &(searchtet->tet[4]);
+      for (i = 0; i < 3; i++) {
+        cavetetvertlist->newindex((void **) &parypt);
+        *parypt = pts[i];
+      }
+    } else if (loc == INTETRAHEDRON) {
+      pts = (point *) &(searchtet->tet[4]);
+      for (i = 0; i < 4; i++) {
+        cavetetvertlist->newindex((void **) &parypt);
+        *parypt = pts[i];
+      } 
+    } else if (loc == ONFACE) {
+      pts = (point *) &(searchtet->tet[4]);
+      for (i = 0; i < 3; i++) {
+        cavetetvertlist->newindex((void **) &parypt);
+        *parypt = pts[i];
+      }
+      if (pts[3] != dummypoint) {
+        cavetetvertlist->newindex((void **) &parypt);
+        *parypt = pts[3];
+      }
+      fsym(*searchtet, spintet);
+      if (oppo(spintet) != dummypoint) {
+        cavetetvertlist->newindex((void **) &parypt);
+        *parypt = oppo(spintet);
+      }
+    } else if (loc == ONEDGE) {
+      spintet = *searchtet;
+      cavetetvertlist->newindex((void **) &parypt);
+      *parypt = org(spintet);
+      cavetetvertlist->newindex((void **) &parypt);
+      *parypt = dest(spintet);
+      while (1) {
+        if (apex(spintet) != dummypoint) {
+          cavetetvertlist->newindex((void **) &parypt);
+          *parypt = apex(spintet);
+        }
+        fnextself(spintet);
+        if (spintet.tet == searchtet->tet) break;
+      }
+    }
+
+    int rejptflag = (ivf->rejflag & 4);
+    REAL rd;
+    pts = NULL;
+
+    for (i = 0; i < cavetetvertlist->objects; i++) {
+      parypt = (point *) fastlookup(cavetetvertlist, i);
+      rd = distance(*parypt, insertpt);
+      // Is the point very close to an existing point?
+      if (rd < minedgelength) {
+        pts = parypt; 
+        loc = NEARVERTEX;
+        break;
+      }
+      if (rejptflag) {
+        // Is the point encroaches upon an existing point?
+        if (rd < (0.5 * (*parypt)[pointmtrindex])) {
+          pts = parypt;
+          loc = ENCVERTEX; 
+          break;
+        }
+      }
+    }
+    cavetetvertlist->restart(); // Clear the work list.
+
+    if (pts != NULL) {
+      // The point is either too close to an existing vertex (NEARVERTEX)
+      //   or encroaches upon (inside the protecting ball) of that vertex.
+      if (loc == NEARVERTEX) {
+        if (!issteinerpoint(insertpt) && b->nomergevertex) { // -M0/1 option.
+          // 'insertpt' is an input vertex. 
+          // In this case, we still insert this vertex. Issue a warning.
+          if (!b->quiet) {
+            printf("Warning:  Two points, %d and %d, are very close.\n",
+                   pointmark(insertpt), pointmark(*pts));
+            printf("  Creating a very short edge (len = %g) (< %g).\n",
+                   rd, minedgelength);
+            printf("  You may try a smaller tolerance (-T) (current is %g)\n", 
+                   b->epsilon);
+            printf("  to avoid this warning.\n");
+          }
+        } else {
+          point2tetorg(*pts, *searchtet);
+          insertpoint_abort(splitseg, ivf);
+          ivf->iloc = (int) loc;
+          return 0;
+        }
+      } else { // loc == ENCVERTEX
+        // The point lies inside the protection ball.
+        point2tetorg(*pts, *searchtet); 
+        insertpoint_abort(splitseg, ivf);
+        ivf->iloc = (int) loc;
+        return 0;
+      }
+    }
+  } // if (b->plc && (loc != INSTAR))
+
+  if (b->weighted || ivf->cdtflag || ivf->smlenflag
+      ) {
+    // There may be other vertices inside C(p). We need to find them.
+    // Collect all vertices of C(p).
+    for (i = 0; i < caveoldtetlist->objects; i++) {
+      cavetet = (triface *) fastlookup(caveoldtetlist, i);
+      //assert(infected(*cavetet));
+      pts = (point *) &(cavetet->tet[4]);
+      for (j = 0; j < 4; j++) {
+        if (pts[j] != dummypoint) {
+          if (!pinfected(pts[j])) {
+            pinfect(pts[j]);
+            cavetetvertlist->newindex((void **) &parypt);
+            *parypt = pts[j];
+          }
+        }
+      } // j
+    } // i
+    // Uninfect all collected (cavity) vertices.
+    for (i = 0; i < cavetetvertlist->objects; i++) {
+      parypt = (point *) fastlookup(cavetetvertlist, i);
+      puninfect(*parypt);
+    }
+    if (ivf->smlenflag) {
+      REAL len;
+      // Get the length of the shortest edge connecting to 'newpt'.
+      parypt = (point *) fastlookup(cavetetvertlist, 0);
+      ivf->smlen = distance(*parypt, insertpt);
+      ivf->parentpt = *parypt;
+      for (i = 1; i < cavetetvertlist->objects; i++) {
+        parypt = (point *) fastlookup(cavetetvertlist, i);
+        len = distance(*parypt, insertpt);
+        if (len < ivf->smlen) {
+          ivf->smlen = len;
+          ivf->parentpt = *parypt;
+        }
+      } 
+    }
+  }
+
+
+  if (ivf->cdtflag) {
+    // Unmark tets.
+    for (i = 0; i < caveoldtetlist->objects; i++) {
+      cavetet = (triface *) fastlookup(caveoldtetlist, i);
+      unmarktest(*cavetet);
+    }
+    for (i = 0; i < cavebdrylist->objects; i++) {
+      cavetet = (triface *) fastlookup(cavebdrylist, i);
+      unmarktest(*cavetet);
+    }
+    // Clean up arrays which are not needed.
+    cavetetlist->restart();
+    if (checksubsegflag) {
+      cavetetseglist->restart();
+    }
+    if (checksubfaceflag) {
+      cavetetshlist->restart();
+    }
+    return 1;
+  }
+
+  // Before re-mesh C(p). Process the segments and subfaces which are on the
+  //   boundary of C(p). Make sure that each such segment or subface is
+  //   connecting to a tet outside C(p). So we can re-connect them to the
+  //   new tets inside the C(p) later.
+
+  if (checksubsegflag) {
+    for (i = 0; i < cavetetseglist->objects; i++) {
+      paryseg = (face *) fastlookup(cavetetseglist, i);
+      // Operate on it if it is not the splitting segment, i.e., in sC(p).
+      if (!smarktested(*paryseg)) {
+        // Check if the segment is inside the cavity.
+        //   'j' counts the num of adjacent tets of this seg.
+        //   'k' counts the num of adjacent tets which are 'sinfected'.
+        j = k = 0;
+        sstpivot1(*paryseg, neightet);
+        spintet = neightet;
+        while (1) {
+          j++;
+          if (!infected(spintet)) {
+            neineitet = spintet; // An outer tet. Remember it.
+          } else {
+            k++; // An in tet.
+          }
+          fnextself(spintet);
+          if (spintet.tet == neightet.tet) break;
+        }
+        // assert(j > 0);
+        if (k == 0) {
+          // The segment is not connect to C(p) anymore. Remove it by
+          //   Replacing it by the last entry of this list.
+          s = cavetetseglist->objects - 1;
+          checkseg = * (face *) fastlookup(cavetetseglist, s);
+          *paryseg = checkseg;
+          cavetetseglist->objects--;
+          i--;
+        } else if (k < j) {
+          // The segment is on the boundary of C(p).
+          sstbond1(*paryseg, neineitet);
+        } else { // k == j
+          // The segment is inside C(p).
+          if (!ivf->splitbdflag) {
+            checkseg = *paryseg;
+            sinfect(checkseg); // Flag it as an interior segment.
+            caveencseglist->newindex((void **) &paryseg);
+            *paryseg = checkseg;
+          } else {
+            //assert(0); // Not possible.
+            terminatetetgen(this, 2);
+          }
+        }
+      } else { 
+        // assert(smarktested(*paryseg));
+        // Flag it as an interior segment. Do not queue it, since it will
+        //   be deleted after the segment splitting.
+        sinfect(*paryseg);
+      }
+    } // i
+  } // if (checksubsegflag)
+
+  if (checksubfaceflag) {
+    for (i = 0; i < cavetetshlist->objects; i++) {
+      parysh = (face *) fastlookup(cavetetshlist, i);
+      // Operate on it if it is not inside the sub-cavity sC(p).
+      if (!smarktested(*parysh)) {
+        // Check if this subface is inside the cavity.
+        k = 0;
+        for (j = 0; j < 2; j++) {
+          stpivot(*parysh, neightet);
+          if (!infected(neightet)) {
+            checksh = *parysh; // Remember this side.
+          } else {
+            k++;
+          }
+          sesymself(*parysh);
+        }
+        if (k == 0) {
+          // The subface is not connected to C(p). Remove it.
+          s = cavetetshlist->objects - 1;
+          checksh = * (face *) fastlookup(cavetetshlist, s);
+          *parysh = checksh;
+          cavetetshlist->objects--;
+          i--;
+        } else if (k == 1) {
+          // This side is the outer boundary of C(p).
+          *parysh = checksh;
+        } else { // k == 2
+          if (!ivf->splitbdflag) {
+            checksh = *parysh;
+            sinfect(checksh); // Flag it.
+            caveencshlist->newindex((void **) &parysh);
+            *parysh = checksh;
+          } else {
+            //assert(0); // Not possible.
+            terminatetetgen(this, 2);
+          }
+        }
+      } else {
+        // assert(smarktested(*parysh));
+        // Flag it as an interior subface. Do not queue it. It will be
+        //   deleted after the facet point insertion.
+        sinfect(*parysh);
+      }
+    } // i
+  } // if (checksubfaceflag)
+
+  // Create new tetrahedra to fill the cavity.
+
+  for (i = 0; i < cavebdrylist->objects; i++) {
+    cavetet = (triface *) fastlookup(cavebdrylist, i);
+    neightet = *cavetet;
+    unmarktest(neightet); // Unmark it.
+    // Get the oldtet (inside the cavity).
+    fsym(neightet, oldtet);
+    if (apex(neightet) != dummypoint) {
+      // Create a new tet in the cavity.
+      maketetrahedron(&newtet);
+      setorg(newtet, dest(neightet));
+      setdest(newtet, org(neightet));
+      setapex(newtet, apex(neightet));
+      setoppo(newtet, insertpt);
+    } else {
+      // Create a new hull tet.
+      hullsize++; 
+      maketetrahedron(&newtet);
+      setorg(newtet, org(neightet));
+      setdest(newtet, dest(neightet));
+      setapex(newtet, insertpt);
+      setoppo(newtet, dummypoint); // It must opposite to face 3.
+      // Adjust back to the cavity bounday face.
+      esymself(newtet);
+    }
+    // The new tet inherits attribtes from the old tet.
+    for (j = 0; j < numelemattrib; j++) {
+      attrib = elemattribute(oldtet.tet, j);
+      setelemattribute(newtet.tet, j, attrib);
+    }
+    if (b->varvolume) {
+      volume = volumebound(oldtet.tet);
+      setvolumebound(newtet.tet, volume);
+    }
+    // Connect newtet <==> neightet, this also disconnect the old bond.
+    bond(newtet, neightet);
+    // oldtet still connects to neightet.
+    *cavetet = oldtet; // *cavetet = newtet;
+  } // i
+
+  // Set a handle for speeding point location.
+  recenttet = newtet;
+  //setpoint2tet(insertpt, encode(newtet));
+  setpoint2tet(insertpt, (tetrahedron) (newtet.tet));
+
+  // Re-use this list to save new interior cavity faces.
+  cavetetlist->restart();
+
+  // Connect adjacent new tetrahedra together.
+  for (i = 0; i < cavebdrylist->objects; i++) {
+    cavetet = (triface *) fastlookup(cavebdrylist, i);
+    // cavtet is an oldtet, get the newtet at this face.
+    oldtet = *cavetet;
+    fsym(oldtet, neightet);
+    fsym(neightet, newtet);
+    // Comment: oldtet and newtet must be at the same directed edge.
+    // Connect the three other faces of this newtet.
+    for (j = 0; j < 3; j++) {
+      esym(newtet, neightet); // Go to the face.
+      if (neightet.tet[neightet.ver & 3] == NULL) {
+        // Find the adjacent face of this newtet.
+        spintet = oldtet;
+        while (1) {
+          fnextself(spintet);
+          if (!infected(spintet)) break;
+        }
+        fsym(spintet, newneitet);
+        esymself(newneitet);
+        bond(neightet, newneitet);
+        if (ivf->lawson > 1) { 
+          cavetetlist->newindex((void **) &parytet);
+          *parytet = neightet;
+        }
+      }
+      //setpoint2tet(org(newtet), encode(newtet));
+      setpoint2tet(org(newtet), (tetrahedron) (newtet.tet));
+      enextself(newtet);
+      enextself(oldtet);
+    }
+    *cavetet = newtet; // Save the new tet.
+  } // i
+
+  if (checksubfaceflag) {
+    // Connect subfaces on the boundary of the cavity to the new tets.
+    for (i = 0; i < cavetetshlist->objects; i++) {
+      parysh = (face *) fastlookup(cavetetshlist, i);
+      // Connect it if it is not a missing subface.
+      if (!sinfected(*parysh)) {
+        stpivot(*parysh, neightet);
+        fsym(neightet, spintet);
+        sesymself(*parysh);
+        tsbond(spintet, *parysh);
+      }
+    }
+  }
+
+  if (checksubsegflag) {
+    // Connect segments on the boundary of the cavity to the new tets.
+    for (i = 0; i < cavetetseglist->objects; i++) {
+      paryseg = (face *) fastlookup(cavetetseglist, i);
+      // Connect it if it is not a missing segment.
+      if (!sinfected(*paryseg)) {
+        sstpivot1(*paryseg, neightet);
+        spintet = neightet;
+        while (1) {
+          tssbond1(spintet, *paryseg);
+          fnextself(spintet);
+          if (spintet.tet == neightet.tet) break;
+        }
+      }
+    }
+  }
+
+  if (((splitsh != NULL) && (splitsh->sh != NULL)) ||
+      ((splitseg != NULL) && (splitseg->sh != NULL))) {
+    // Split a subface or a segment.
+    sinsertvertex(insertpt, splitsh, splitseg, ivf->sloc, ivf->sbowywat, 0);
+  }
+
+  if (checksubfaceflag) {
+    if (ivf->splitbdflag) {
+      // Recover new subfaces in C(p).
+      for (i = 0; i < caveshbdlist->objects; i++) {
+        // Get an old subface at edge [a, b].
+        parysh = (face *) fastlookup(caveshbdlist, i);
+        spivot(*parysh, checksh); // The new subface [a, b, p].
+        // Do not recover a deleted new face (degenerated).
+        if (checksh.sh[3] != NULL) {
+          // Note that the old subface still connects to adjacent old tets 
+          //   of C(p), which still connect to the tets outside C(p).
+          stpivot(*parysh, neightet);
+          // Find the adjacent tet containing the edge [a,b] outside C(p).
+          spintet = neightet;
+          while (1) {
+            fnextself(spintet);
+            if (!infected(spintet)) break;
+          }
+          // The adjacent tet connects to a new tet in C(p).
+          fsym(spintet, neightet);
+          // Find the tet containing the face [a, b, p].
+          spintet = neightet;
+          while (1) {
+            fnextself(spintet);
+            if (apex(spintet) == insertpt) break;
+          }
+          // Adjust the edge direction in spintet and checksh.
+          if (sorg(checksh) != org(spintet)) {
+            sesymself(checksh);
+          }
+          // Connect the subface to two adjacent tets.
+          tsbond(spintet, checksh);
+          fsymself(spintet);
+          sesymself(checksh);
+          tsbond(spintet, checksh);
+        } // if (checksh.sh[3] != NULL)
+      }
+    } else { 
+      // The Boundary recovery phase.
+      // Put all new subfaces into stack for recovery.
+      for (i = 0; i < caveshbdlist->objects; i++) {
+        // Get an old subface at edge [a, b].
+        parysh = (face *) fastlookup(caveshbdlist, i);
+        spivot(*parysh, checksh); // The new subface [a, b, p].
+        // Do not recover a deleted new face (degenerated).
+        if (checksh.sh[3] != NULL) {
+          subfacstack->newindex((void **) &parysh);
+          *parysh = checksh;
+        }
+      }
+      // Put all interior subfaces into stack for recovery.
+      for (i = 0; i < caveencshlist->objects; i++) {
+        parysh = (face *) fastlookup(caveencshlist, i);
+        // Some subfaces inside C(p) might be split in sinsertvertex().
+        //   Only queue those faces which are not split.
+        if (!smarktested(*parysh)) {
+          checksh = *parysh;
+          suninfect(checksh);
+          stdissolve(checksh); // Detach connections to old tets.
+          subfacstack->newindex((void **) &parysh);
+          *parysh = checksh;
+        }
+      }
+    }
+  } // if (checksubfaceflag)
+
+  if (checksubsegflag) {
+    if (ivf->splitbdflag) {
+      if (splitseg != NULL) {
+        // Recover the two new subsegments in C(p).
+        for (i = 0; i < cavesegshlist->objects; i++) {
+          paryseg = (face *) fastlookup(cavesegshlist, i);
+          // Insert this subsegment into C(p).
+          checkseg = *paryseg;
+          // Get the adjacent new subface.
+          checkseg.shver = 0;
+          spivot(checkseg, checksh);
+          if (checksh.sh != NULL) {
+            // Get the adjacent new tetrahedron.
+            stpivot(checksh, neightet);
+          } else {
+            // It's a dangling segment.
+            point2tetorg(sorg(checkseg), neightet);
+            finddirection(&neightet, sdest(checkseg));
+          }
+          sstbond1(checkseg, neightet);
+          spintet = neightet;
+          while (1) {
+            tssbond1(spintet, checkseg);
+            fnextself(spintet);
+            if (spintet.tet == neightet.tet) break;
+          }
+        }
+      } // if (splitseg != NULL)
+    } else {
+      // The Boundary Recovery Phase.  
+      // Queue missing segments in C(p) for recovery.
+      if (splitseg != NULL) {
+        // Queue two new subsegments in C(p) for recovery.
+        for (i = 0; i < cavesegshlist->objects; i++) {
+          paryseg = (face *) fastlookup(cavesegshlist, i);
+          checkseg = *paryseg;
+          //sstdissolve1(checkseg); // It has not been connected yet.
+          s = randomnation(subsegstack->objects + 1);
+          subsegstack->newindex((void **) &paryseg);
+          *paryseg = * (face *) fastlookup(subsegstack, s); 
+          paryseg = (face *) fastlookup(subsegstack, s);
+          *paryseg = checkseg;
+        }
+      } // if (splitseg != NULL)
+      for (i = 0; i < caveencseglist->objects; i++) {
+        paryseg = (face *) fastlookup(caveencseglist, i);
+        if (!smarktested(*paryseg)) { // It may be split.
+          checkseg = *paryseg;
+          suninfect(checkseg);
+          sstdissolve1(checkseg); // Detach connections to old tets.
+          s = randomnation(subsegstack->objects + 1);
+          subsegstack->newindex((void **) &paryseg);
+          *paryseg = * (face *) fastlookup(subsegstack, s); 
+          paryseg = (face *) fastlookup(subsegstack, s);
+          *paryseg = checkseg;
+        }
+      }
+    }
+  } // if (checksubsegflag)
+
+  if (b->weighted
+      ) {
+    // Some vertices may be completed inside the cavity. They must be
+    //   detected and added to recovering list.
+    // Since every "live" vertex must contain a pointer to a non-dead
+    //   tetrahedron, we can check for each vertex this pointer.
+    for (i = 0; i < cavetetvertlist->objects; i++) {
+      pts = (point *) fastlookup(cavetetvertlist, i);
+      decode(point2tet(*pts), *searchtet);
+      if (infected(*searchtet)) {
+        if (b->weighted) {
+          if (b->verbose > 1) {
+            printf("    Point #%d is non-regular after the insertion of #%d.\n",
+                   pointmark(*pts), pointmark(insertpt));
+          }
+          setpointtype(*pts, NREGULARVERTEX);
+          nonregularcount++;
+        }
+      }
+    }
+  }
+
+  if (ivf->chkencflag & 1) {
+    // Queue all segment outside C(p).
+    for (i = 0; i < cavetetseglist->objects; i++) {
+      paryseg = (face *) fastlookup(cavetetseglist, i);
+      // Skip if it is the split segment.
+      if (!sinfected(*paryseg)) {
+        enqueuesubface(badsubsegs, paryseg);
+      }
+    }
+    if (splitseg != NULL) {
+      // Queue the two new subsegments inside C(p).
+      for (i = 0; i < cavesegshlist->objects; i++) {
+        paryseg = (face *) fastlookup(cavesegshlist, i);
+        enqueuesubface(badsubsegs, paryseg);
+      }
+    }
+  } // if (chkencflag & 1)
+
+  if (ivf->chkencflag & 2) {
+    // Queue all subfaces outside C(p).
+    for (i = 0; i < cavetetshlist->objects; i++) {
+      parysh = (face *) fastlookup(cavetetshlist, i);
+      // Skip if it is a split subface.
+      if (!sinfected(*parysh)) {
+        enqueuesubface(badsubfacs, parysh);
+      }
+    }
+    // Queue all new subfaces inside C(p).
+    for (i = 0; i < caveshbdlist->objects; i++) {
+      // Get an old subface at edge [a, b].
+      parysh = (face *) fastlookup(caveshbdlist, i);
+      spivot(*parysh, checksh); // checksh is a new subface [a, b, p].
+      // Do not recover a deleted new face (degenerated).
+      if (checksh.sh[3] != NULL) {
+        enqueuesubface(badsubfacs, &checksh);
+      }
+    }
+  } // if (chkencflag & 2)
+
+  if (ivf->chkencflag & 4) {
+    // Queue all new tetrahedra in C(p).
+    for (i = 0; i < cavebdrylist->objects; i++) {
+      cavetet = (triface *) fastlookup(cavebdrylist, i);
+      enqueuetetrahedron(cavetet);
+    }
+  }
+
+  // C(p) is re-meshed successfully.
+
+  // Delete the old tets in C(p).
+  for (i = 0; i < caveoldtetlist->objects; i++) {
+    searchtet = (triface *) fastlookup(caveoldtetlist, i);
+    if (ishulltet(*searchtet)) {
+      hullsize--;
+    }
+    tetrahedrondealloc(searchtet->tet);
+  }
+
+  if (((splitsh != NULL) && (splitsh->sh != NULL)) ||
+      ((splitseg != NULL) && (splitseg->sh != NULL))) {
+    // Delete the old subfaces in sC(p).
+    for (i = 0; i < caveshlist->objects; i++) {
+      parysh = (face *) fastlookup(caveshlist, i);
+      if (checksubfaceflag) {//if (bowywat == 2) {
+        // It is possible that this subface still connects to adjacent
+        //   tets which are not in C(p). If so, clear connections in the
+        //   adjacent tets at this subface.
+        stpivot(*parysh, neightet);
+        if (neightet.tet != NULL) {
+          if (neightet.tet[4] != NULL) {
+            // Found an adjacent tet. It must be not in C(p).
+            tsdissolve(neightet);
+            fsymself(neightet);
+            tsdissolve(neightet);
+          }
+        }
+      }
+      shellfacedealloc(subfaces, parysh->sh);
+    }
+    if ((splitseg != NULL) && (splitseg->sh != NULL)) {
+      // Delete the old segment in sC(p).
+      shellfacedealloc(subsegs, splitseg->sh);
+    }
+  }
+
+  if (ivf->lawson) {
+    for (i = 0; i < cavebdrylist->objects; i++) {
+      searchtet = (triface *) fastlookup(cavebdrylist, i);
+      flippush(flipstack, searchtet);
+    }
+    if (ivf->lawson > 1) {
+      for (i = 0; i < cavetetlist->objects; i++) {
+        searchtet = (triface *) fastlookup(cavetetlist, i);
+        flippush(flipstack, searchtet);
+      }
+    }
+  }
+
+
+  // Clean the working lists.
+
+  caveoldtetlist->restart();
+  cavebdrylist->restart();
+  cavetetlist->restart();
+
+  if (checksubsegflag) {
+    cavetetseglist->restart();
+    caveencseglist->restart();
+  }
+
+  if (checksubfaceflag) {
+    cavetetshlist->restart();
+    caveencshlist->restart();
+  }
+  
+  if (b->weighted || ivf->smlenflag
+      ) { 
+    cavetetvertlist->restart();
+  }
+  
+  if (((splitsh != NULL) && (splitsh->sh != NULL)) ||
+      ((splitseg != NULL) && (splitseg->sh != NULL))) {
+    caveshlist->restart();
+    caveshbdlist->restart();
+    cavesegshlist->restart();
+  }
+
+  return 1; // Point is inserted.
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// insertpoint_abort()    Abort the insertion of a new vertex.               //
+//                                                                           //
+// The cavity will be restored.  All working lists are cleared.              //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::insertpoint_abort(face *splitseg, insertvertexflags *ivf)
+{
+  triface *cavetet;
+  face *parysh;
+  int i;
+
+  for (i = 0; i < caveoldtetlist->objects; i++) {
+    cavetet = (triface *) fastlookup(caveoldtetlist, i);
+    uninfect(*cavetet);
+    unmarktest(*cavetet);
+  }
+  for (i = 0; i < cavebdrylist->objects; i++) {
+    cavetet = (triface *) fastlookup(cavebdrylist, i);
+    unmarktest(*cavetet); 
+  }
+  cavetetlist->restart();
+  cavebdrylist->restart();
+  caveoldtetlist->restart();
+  cavetetseglist->restart();
+  cavetetshlist->restart();
+  if (ivf->splitbdflag) { 
+    if ((splitseg != NULL) && (splitseg->sh != NULL)) {
+      sunmarktest(*splitseg);
+    }
+    for (i = 0; i < caveshlist->objects; i++) {
+      parysh = (face *) fastlookup(caveshlist, i);
+      sunmarktest(*parysh);
+    }
+    caveshlist->restart();
+    cavesegshlist->restart();
+  }
+}
+
+////                                                                       ////
+////                                                                       ////
+//// flip_cxx /////////////////////////////////////////////////////////////////
+
+//// delaunay_cxx /////////////////////////////////////////////////////////////
+////                                                                       ////
+////                                                                       ////
+
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// hilbert_init()    Initialize the Gray code permutation table.             //
+//                                                                           //
+// The table 'transgc' has 8 x 3 x 8 entries. It contains all possible Gray  //
+// code sequences traveled by the 1st order Hilbert curve in 3 dimensions.   //
+// The first column is the Gray code of the entry point of the curve, and    //
+// the second column is the direction (0, 1, or 2, 0 means the x-axis) where //
+// the exit point of curve lies.                                             //
+//                                                                           //
+// The table 'tsb1mod3' contains the numbers of trailing set '1' bits of the //
+// indices from 0 to 7, modulo by '3'. The code for generating this table is //
+// from: http://graphics.stanford.edu/~seander/bithacks.html.                //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::hilbert_init(int n)
+{
+  int gc[8], N, mask, travel_bit;
+  int e, d, f, k, g;
+  int v, c;
+  int i;
+
+  N = (n == 2) ? 4 : 8;
+  mask = (n == 2) ? 3 : 7;
+
+  // Generate the Gray code sequence.
+  for (i = 0; i < N; i++) {
+    gc[i] = i ^ (i >> 1);
+  }
+
+  for (e = 0; e < N; e++) {
+    for (d = 0; d < n; d++) {
+      // Calculate the end point (f).
+      f = e ^ (1 << d);  // Toggle the d-th bit of 'e'.
+      // travel_bit = 2**p, the bit we want to travel. 
+      travel_bit = e ^ f;
+      for (i = 0; i < N; i++) {
+        // // Rotate gc[i] left by (p + 1) % n bits.
+        k = gc[i] * (travel_bit * 2);
+        g = ((k | (k / N)) & mask);
+        // Calculate the permuted Gray code by xor with the start point (e).
+        transgc[e][d][i] = (g ^ e);
+      }
+    } // d
+  } // e
+
+  // Count the consecutive '1' bits (trailing) on the right.
+  tsb1mod3[0] = 0;
+  for (i = 1; i < N; i++) {
+    v = ~i; // Count the 0s.
+    v = (v ^ (v - 1)) >> 1; // Set v's trailing 0s to 1s and zero rest
+    for (c = 0; v; c++) {
+      v >>= 1;
+    }
+    tsb1mod3[i] = c % n;
+  }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// hilbert_sort3()    Sort points using the 3d Hilbert curve.                //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+int tetgenmesh::hilbert_split(point* vertexarray,int arraysize,int gc0,int gc1,
+                              REAL bxmin, REAL bxmax, REAL bymin, REAL bymax, 
+                              REAL bzmin, REAL bzmax)
+{
+  point swapvert;
+  int axis, d;
+  REAL split;
+  int i, j;
+
+
+  // Find the current splitting axis. 'axis' is a value 0, or 1, or 2, which 
+  //   correspoding to x-, or y- or z-axis.
+  axis = (gc0 ^ gc1) >> 1; 
+
+  // Calulate the split position along the axis.
+  if (axis == 0) {
+    split = 0.5 * (bxmin + bxmax);
+  } else if (axis == 1) {
+    split = 0.5 * (bymin + bymax);
+  } else { // == 2
+    split = 0.5 * (bzmin + bzmax);
+  }
+
+  // Find the direction (+1 or -1) of the axis. If 'd' is +1, the direction
+  //   of the axis is to the positive of the axis, otherwise, it is -1.
+  d = ((gc0 & (1<<axis)) == 0) ? 1 : -1;
+
+
+  // Partition the vertices into left- and right-arrays such that left points
+  //   have Hilbert indices lower than the right points.
+  i = 0;
+  j = arraysize - 1;
+
+  // Partition the vertices into left- and right-arrays.
+  if (d > 0) {
+    do {
+      for (; i < arraysize; i++) {      
+        if (vertexarray[i][axis] >= split) break;
+      }
+      for (; j >= 0; j--) {
+        if (vertexarray[j][axis] < split) break;
+      }
+      // Is the partition finished?
+      if (i == (j + 1)) break;
+      // Swap i-th and j-th vertices.
+      swapvert = vertexarray[i];
+      vertexarray[i] = vertexarray[j];
+      vertexarray[j] = swapvert;
+      // Continue patitioning the array;
+    } while (true);
+  } else {
+    do {
+      for (; i < arraysize; i++) {      
+        if (vertexarray[i][axis] <= split) break;
+      }
+      for (; j >= 0; j--) {
+        if (vertexarray[j][axis] > split) break;
+      }
+      // Is the partition finished?
+      if (i == (j + 1)) break;
+      // Swap i-th and j-th vertices.
+      swapvert = vertexarray[i];
+      vertexarray[i] = vertexarray[j];
+      vertexarray[j] = swapvert;
+      // Continue patitioning the array;
+    } while (true);
+  }
+
+  return i;
+}
+
+void tetgenmesh::hilbert_sort3(point* vertexarray, int arraysize, int e, int d, 
+                               REAL bxmin, REAL bxmax, REAL bymin, REAL bymax, 
+                               REAL bzmin, REAL bzmax, int depth)
+{
+  REAL x1, x2, y1, y2, z1, z2;
+  int p[9], w, e_w, d_w, k, ei, di;
+  int n = 3, mask = 7;
+
+  p[0] = 0;
+  p[8] = arraysize;
+
+  // Sort the points according to the 1st order Hilbert curve in 3d.
+  p[4] = hilbert_split(vertexarray, p[8], transgc[e][d][3], transgc[e][d][4], 
+                       bxmin, bxmax, bymin, bymax, bzmin, bzmax);
+  p[2] = hilbert_split(vertexarray, p[4], transgc[e][d][1], transgc[e][d][2], 
+                       bxmin, bxmax, bymin, bymax, bzmin, bzmax);
+  p[1] = hilbert_split(vertexarray, p[2], transgc[e][d][0], transgc[e][d][1], 
+                       bxmin, bxmax, bymin, bymax, bzmin, bzmax);
+  p[3] = hilbert_split(&(vertexarray[p[2]]), p[4] - p[2], 
+                       transgc[e][d][2], transgc[e][d][3], 
+                       bxmin, bxmax, bymin, bymax, bzmin, bzmax) + p[2];
+  p[6] = hilbert_split(&(vertexarray[p[4]]), p[8] - p[4], 
+                       transgc[e][d][5], transgc[e][d][6], 
+                       bxmin, bxmax, bymin, bymax, bzmin, bzmax) + p[4];
+  p[5] = hilbert_split(&(vertexarray[p[4]]), p[6] - p[4], 
+                       transgc[e][d][4], transgc[e][d][5], 
+                       bxmin, bxmax, bymin, bymax, bzmin, bzmax) + p[4];
+  p[7] = hilbert_split(&(vertexarray[p[6]]), p[8] - p[6], 
+                       transgc[e][d][6], transgc[e][d][7], 
+                       bxmin, bxmax, bymin, bymax, bzmin, bzmax) + p[6];
+
+  if (b->hilbert_order > 0) {
+    // A maximum order is prescribed. 
+    if ((depth + 1) == b->hilbert_order) {
+      // The maximum prescribed order is reached.
+      return;
+    }
+  }
+
+  // Recursively sort the points in sub-boxes.
+  for (w = 0; w < 8; w++) {
+    // w is the local Hilbert index (NOT Gray code).
+    // Sort into the sub-box either there are more than 2 points in it, or
+    //   the prescribed order of the curve is not reached yet.
+    //if ((p[w+1] - p[w] > b->hilbert_limit) || (b->hilbert_order > 0)) {
+    if ((p[w+1] - p[w]) > b->hilbert_limit) {
+      // Calculcate the start point (ei) of the curve in this sub-box.
+      //   update e = e ^ (e(w) left_rotate (d+1)).
+      if (w == 0) {
+        e_w = 0;
+      } else {
+        //   calculate e(w) = gc(2 * floor((w - 1) / 2)).
+        k = 2 * ((w - 1) / 2); 
+        e_w = k ^ (k >> 1); // = gc(k).
+      }
+      k = e_w;
+      e_w = ((k << (d+1)) & mask) | ((k >> (n-d-1)) & mask);
+      ei = e ^ e_w;
+      // Calulcate the direction (di) of the curve in this sub-box.
+      //   update d = (d + d(w) + 1) % n
+      if (w == 0) {
+        d_w = 0;
+      } else {
+        d_w = ((w % 2) == 0) ? tsb1mod3[w - 1] : tsb1mod3[w];
+      }
+      di = (d + d_w + 1) % n;
+      // Calculate the bounding box of the sub-box.
+      if (transgc[e][d][w] & 1) { // x-axis
+        x1 = 0.5 * (bxmin + bxmax);
+        x2 = bxmax;
+      } else {
+        x1 = bxmin;
+        x2 = 0.5 * (bxmin + bxmax);
+      }
+      if (transgc[e][d][w] & 2) { // y-axis
+        y1 = 0.5 * (bymin + bymax);
+        y2 = bymax;
+      } else {
+        y1 = bymin;
+        y2 = 0.5 * (bymin + bymax);
+      }
+      if (transgc[e][d][w] & 4) { // z-axis
+        z1 = 0.5 * (bzmin + bzmax);
+        z2 = bzmax;
+      } else {
+        z1 = bzmin;
+        z2 = 0.5 * (bzmin + bzmax);
+      }
+      hilbert_sort3(&(vertexarray[p[w]]), p[w+1] - p[w], ei, di, 
+                    x1, x2, y1, y2, z1, z2, depth+1);
+    } // if (p[w+1] - p[w] > 1)
+  } // w
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// brio_multiscale_sort()    Sort the points using BRIO and Hilbert curve.   //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::brio_multiscale_sort(point* vertexarray, int arraysize, 
+                                      int threshold, REAL ratio, int *depth)
+{
+  int middle;
+
+  middle = 0;
+  if (arraysize >= threshold) {
+    (*depth)++;
+    middle = arraysize * ratio;
+    brio_multiscale_sort(vertexarray, middle, threshold, ratio, depth);
+  }
+  // Sort the right-array (rnd-th round) using the Hilbert curve.
+  hilbert_sort3(&(vertexarray[middle]), arraysize - middle, 0, 0, // e, d
+                xmin, xmax, ymin, ymax, zmin, zmax, 0); // depth.
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// randomnation()    Generate a random number between 0 and 'choices' - 1.   //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+unsigned long tetgenmesh::randomnation(unsigned int choices)
+{
+  unsigned long newrandom;
+
+  if (choices >= 714025l) {
+    newrandom = (randomseed * 1366l + 150889l) % 714025l;
+    randomseed = (newrandom * 1366l + 150889l) % 714025l;
+    newrandom = newrandom * (choices / 714025l) + randomseed;
+    if (newrandom >= choices) {
+      return newrandom - choices;
+    } else {
+      return newrandom;
+    }
+  } else {
+    randomseed = (randomseed * 1366l + 150889l) % 714025l;
+    return randomseed % choices;
+  }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// randomsample()    Randomly sample the tetrahedra for point loation.       //
+//                                                                           //
+// Searching begins from one of handles:  the input 'searchtet', a recently  //
+// encountered tetrahedron 'recenttet',  or from one chosen from a random    //
+// sample.  The choice is made by determining which one's origin is closest  //
+// to the point we are searching for.                                        //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::randomsample(point searchpt,triface *searchtet)
+{
+  tetrahedron *firsttet, *tetptr;
+  point torg;
+  void **sampleblock;
+  uintptr_t alignptr;
+  long sampleblocks, samplesperblock, samplenum;
+  long tetblocks, i, j;
+  REAL searchdist, dist;
+
+  if (b->verbose > 2) {
+    printf("      Random sampling tetrahedra for searching point %d.\n",
+           pointmark(searchpt));
+  }
+
+  if (!nonconvex) {
+    if (searchtet->tet == NULL) {
+      // A null tet. Choose the recenttet as the starting tet.
+      *searchtet = recenttet;
+    }
+
+    // 'searchtet' should be a valid tetrahedron. Choose the base face
+    //   whose vertices must not be 'dummypoint'.
+    searchtet->ver = 3;
+    // Record the distance from its origin to the searching point.
+    torg = org(*searchtet);
+    searchdist = (searchpt[0] - torg[0]) * (searchpt[0] - torg[0]) +
+                 (searchpt[1] - torg[1]) * (searchpt[1] - torg[1]) +
+                 (searchpt[2] - torg[2]) * (searchpt[2] - torg[2]);
+
+    // If a recently encountered tetrahedron has been recorded and has not
+    //   been deallocated, test it as a good starting point.
+    if (recenttet.tet != searchtet->tet) {
+      recenttet.ver = 3;
+      torg = org(recenttet);
+      dist = (searchpt[0] - torg[0]) * (searchpt[0] - torg[0]) +
+             (searchpt[1] - torg[1]) * (searchpt[1] - torg[1]) +
+             (searchpt[2] - torg[2]) * (searchpt[2] - torg[2]);
+      if (dist < searchdist) {
+        *searchtet = recenttet;
+        searchdist = dist;
+      }
+    }
+  } else {
+    // The mesh is non-convex. Do not use 'recenttet'.
+    searchdist = longest;
+  }
+
+  // Select "good" candidate using k random samples, taking the closest one.
+  //   The number of random samples taken is proportional to the fourth root
+  //   of the number of tetrahedra in the mesh. 
+  while (samples * samples * samples * samples < tetrahedrons->items) {
+    samples++;
+  }
+  // Find how much blocks in current tet pool.
+  tetblocks = (tetrahedrons->maxitems + b->tetrahedraperblock - 1) 
+            / b->tetrahedraperblock;
+  // Find the average samples per block. Each block at least have 1 sample.
+  samplesperblock = 1 + (samples / tetblocks);
+  sampleblocks = samples / samplesperblock;
+  sampleblock = tetrahedrons->firstblock;
+  for (i = 0; i < sampleblocks; i++) {
+    alignptr = (uintptr_t) (sampleblock + 1);
+    firsttet = (tetrahedron *)
+               (alignptr + (uintptr_t) tetrahedrons->alignbytes
+               - (alignptr % (uintptr_t) tetrahedrons->alignbytes));
+    for (j = 0; j < samplesperblock; j++) {
+      if (i == tetblocks - 1) {
+        // This is the last block.
+        samplenum = randomnation((int)
+                      (tetrahedrons->maxitems - (i * b->tetrahedraperblock)));
+      } else {
+        samplenum = randomnation(b->tetrahedraperblock);
+      }
+      tetptr = (tetrahedron *)
+               (firsttet + (samplenum * tetrahedrons->itemwords));
+      torg = (point) tetptr[4];
+      if (torg != (point) NULL) {
+        dist = (searchpt[0] - torg[0]) * (searchpt[0] - torg[0]) +
+               (searchpt[1] - torg[1]) * (searchpt[1] - torg[1]) +
+               (searchpt[2] - torg[2]) * (searchpt[2] - torg[2]);
+        if (dist < searchdist) {
+          searchtet->tet = tetptr;
+          searchtet->ver = 11; // torg = org(t);
+          searchdist = dist;
+        }
+      } else {
+        // A dead tet. Re-sample it.
+        if (i != tetblocks - 1) j--;
+      }
+    }
+    sampleblock = (void **) *sampleblock;
+  }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// locate()    Find a tetrahedron containing a given point.                  //
+//                                                                           //
+// Begins its search from 'searchtet', assume there is a line segment L from //
+// a vertex of 'searchtet' to the query point 'searchpt', and simply walk    //
+// towards 'searchpt' by traversing all faces intersected by L.              //
+//                                                                           //
+// On completion, 'searchtet' is a tetrahedron that contains 'searchpt'. The //
+// returned value indicates one of the following cases:                      //
+//   - ONVERTEX, the search point lies on the origin of 'searchtet'.         //
+//   - ONEDGE, the search point lies on an edge of 'searchtet'.              //
+//   - ONFACE, the search point lies on a face of 'searchtet'.               //
+//   - INTET, the search point lies in the interior of 'searchtet'.          //
+//   - OUTSIDE, the search point lies outside the mesh. 'searchtet' is a     //
+//              hull face which is visible by the search point.              //
+//                                                                           //
+// WARNING: This routine is designed for convex triangulations, and will not //
+// generally work after the holes and concavities have been carved.          //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+enum tetgenmesh::locateresult 
+  tetgenmesh::locate(point searchpt, triface* searchtet, int chkencflag)
+{
+  point torg, tdest, tapex, toppo;
+  enum {ORGMOVE, DESTMOVE, APEXMOVE} nextmove;
+  REAL ori, oriorg, oridest, oriapex;
+  enum locateresult loc = OUTSIDE;
+  int t1ver;
+  int s;
+
+  if (searchtet->tet == NULL) {
+    // A null tet. Choose the recenttet as the starting tet.
+    searchtet->tet = recenttet.tet;
+  }
+
+  // Check if we are in the outside of the convex hull.
+  if (ishulltet(*searchtet)) {
+    // Get its adjacent tet (inside the hull).
+    searchtet->ver = 3;
+    fsymself(*searchtet);
+  }
+
+  // Let searchtet be the face such that 'searchpt' lies above to it.
+  for (searchtet->ver = 0; searchtet->ver < 4; searchtet->ver++) {
+    torg = org(*searchtet);
+    tdest = dest(*searchtet);
+    tapex = apex(*searchtet);
+    ori = orient3d(torg, tdest, tapex, searchpt); 
+    if (ori < 0.0) break;
+  }
+  if (searchtet->ver == 4) {
+    terminatetetgen(this, 2);
+  }
+
+  // Walk through tetrahedra to locate the point.
+  while (true) {
+
+    toppo = oppo(*searchtet);
+    
+    // Check if the vertex is we seek.
+    if (toppo == searchpt) {
+      // Adjust the origin of searchtet to be searchpt.
+      esymself(*searchtet);
+      eprevself(*searchtet);
+      loc = ONVERTEX; // return ONVERTEX;
+      break;
+    }
+
+    // We enter from one of serarchtet's faces, which face do we exit?
+    oriorg = orient3d(tdest, tapex, toppo, searchpt); 
+    oridest = orient3d(tapex, torg, toppo, searchpt);
+    oriapex = orient3d(torg, tdest, toppo, searchpt);
+
+    // Now decide which face to move. It is possible there are more than one
+    //   faces are viable moves. If so, randomly choose one.
+    if (oriorg < 0) {
+      if (oridest < 0) {
+        if (oriapex < 0) {
+          // All three faces are possible.
+          s = randomnation(3); // 's' is in {0,1,2}.
+          if (s == 0) {
+            nextmove = ORGMOVE;
+          } else if (s == 1) {
+            nextmove = DESTMOVE;
+          } else {
+            nextmove = APEXMOVE;
+          }
+        } else {
+          // Two faces, opposite to origin and destination, are viable.
+          //s = randomnation(2); // 's' is in {0,1}.
+          if (randomnation(2)) {
+            nextmove = ORGMOVE;
+          } else {
+            nextmove = DESTMOVE;
+          }
+        }
+      } else {
+        if (oriapex < 0) {
+          // Two faces, opposite to origin and apex, are viable.
+          //s = randomnation(2); // 's' is in {0,1}.
+          if (randomnation(2)) {
+            nextmove = ORGMOVE;
+          } else {
+            nextmove = APEXMOVE;
+          }
+        } else {
+          // Only the face opposite to origin is viable.
+          nextmove = ORGMOVE;
+        }
+      }
+    } else {
+      if (oridest < 0) {
+        if (oriapex < 0) {
+          // Two faces, opposite to destination and apex, are viable.
+          //s = randomnation(2); // 's' is in {0,1}.
+          if (randomnation(2)) {
+            nextmove = DESTMOVE;
+          } else {
+            nextmove = APEXMOVE;
+          }
+        } else {
+          // Only the face opposite to destination is viable.
+          nextmove = DESTMOVE;
+        }
+      } else {
+        if (oriapex < 0) {
+          // Only the face opposite to apex is viable.
+          nextmove = APEXMOVE;
+        } else {
+          // The point we seek must be on the boundary of or inside this
+          //   tetrahedron. Check for boundary cases.
+          if (oriorg == 0) {
+            // Go to the face opposite to origin.
+            enextesymself(*searchtet);
+            if (oridest == 0) {
+              eprevself(*searchtet); // edge oppo->apex
+              if (oriapex == 0) {
+                // oppo is duplicated with p.
+                loc = ONVERTEX; // return ONVERTEX;
+                break;
+              }
+              loc = ONEDGE; // return ONEDGE;
+              break;
+            }
+            if (oriapex == 0) {
+              enextself(*searchtet); // edge dest->oppo
+              loc = ONEDGE; // return ONEDGE;
+              break;
+            }
+            loc = ONFACE; // return ONFACE;
+            break;
+          }
+          if (oridest == 0) {
+            // Go to the face opposite to destination.
+            eprevesymself(*searchtet);
+            if (oriapex == 0) {
+              eprevself(*searchtet); // edge oppo->org
+              loc = ONEDGE; // return ONEDGE;
+              break;
+            }
+            loc = ONFACE; // return ONFACE;
+            break;
+          }
+          if (oriapex == 0) {
+            // Go to the face opposite to apex
+            esymself(*searchtet);
+            loc = ONFACE; // return ONFACE;
+            break;
+          }
+          loc = INTETRAHEDRON; // return INTETRAHEDRON;
+          break;
+        }
+      }
+    }
+    
+    // Move to the selected face.
+    if (nextmove == ORGMOVE) {
+      enextesymself(*searchtet);
+    } else if (nextmove == DESTMOVE) {
+      eprevesymself(*searchtet);
+    } else {
+      esymself(*searchtet);
+    }
+    if (chkencflag) {
+      // Check if we are walking across a subface.
+      if (issubface(*searchtet)) {
+        loc = ENCSUBFACE;
+        break;
+      }
+    }
+    // Move to the adjacent tetrahedron (maybe a hull tetrahedron).
+    fsymself(*searchtet);
+    if (oppo(*searchtet) == dummypoint) {
+      loc = OUTSIDE; // return OUTSIDE;
+      break;
+    }
+
+    // Retreat the three vertices of the base face.
+    torg = org(*searchtet);
+    tdest = dest(*searchtet);
+    tapex = apex(*searchtet);
+
+  } // while (true)
+
+  return loc;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// flippush()    Push a face (possibly will be flipped) into flipstack.      //
+//                                                                           //
+// The face is marked. The flag is used to check the validity of the face on //
+// its popup.  Some other flips may change it already.                       //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::flippush(badface*& fstack, triface* flipface)
+{
+  if (!facemarked(*flipface)) {
+    badface *newflipface = (badface *) flippool->alloc();
+    newflipface->tt = *flipface;
+    markface(newflipface->tt);
+    // Push this face into stack.
+    newflipface->nextitem = fstack;
+    fstack = newflipface;
+  }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// incrementalflip()    Incrementally flipping to construct DT.              //
+//                                                                           //
+// Faces need to be checked for flipping are already queued in 'flipstack'.  //
+// Return the total number of performed flips.                               //
+//                                                                           //
+// Comment:  This routine should be only used in the incremental Delaunay    //
+// construction.  In other cases, lawsonflip3d() should be used.             // 
+//                                                                           //
+// If the new point lies outside of the convex hull ('hullflag' is set). The //
+// incremental flip algorithm still works as usual.  However, we must ensure //
+// that every flip (2-to-3 or 3-to-2) does not create a duplicated (existing)//
+// edge or face. Otherwise, the underlying space of the triangulation becomes//
+// non-manifold and it is not possible to flip further.                      //
+// Thanks to Joerg Rambau and Frank Lutz for helping in this issue.          //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+int tetgenmesh::incrementalflip(point newpt, int hullflag, flipconstraints *fc)
+{
+  badface *popface;
+  triface fliptets[5], *parytet;
+  point *pts, *parypt, pe;
+  REAL sign, ori;
+  int flipcount = 0;
+  int t1ver;
+  int i;
+
+  if (b->verbose > 2) {
+    printf("      Lawson flip (%ld faces).\n", flippool->items);
+  }
+
+  if (hullflag) {
+    // 'newpt' lies in the outside of the convex hull. 
+    // Mark all hull vertices which are connecting to it.
+    popface = flipstack;
+    while (popface != NULL) {
+      pts = (point *) popface->tt.tet;
+      for (i = 4; i < 8; i++) {
+        if ((pts[i] != newpt) && (pts[i] != dummypoint)) {
+          if (!pinfected(pts[i])) {
+            pinfect(pts[i]);
+            cavetetvertlist->newindex((void **) &parypt);
+            *parypt = pts[i];
+          }
+        } 
+      }
+      popface = popface->nextitem;
+    }
+  }
+
+  // Loop until the queue is empty.
+  while (flipstack != NULL) {
+
+    // Pop a face from the stack.
+    popface = flipstack;
+    fliptets[0] = popface->tt;
+    flipstack = flipstack->nextitem; // The next top item in stack.
+    flippool->dealloc((void *) popface);
+
+    // Skip it if it is a dead tet (destroyed by previous flips).
+    if (isdeadtet(fliptets[0])) continue;
+    // Skip it if it is not the same tet as we saved.
+    if (!facemarked(fliptets[0])) continue;
+
+    unmarkface(fliptets[0]);    
+
+    if ((point) fliptets[0].tet[7] == dummypoint) {
+      // It must be a hull edge.
+      fliptets[0].ver = epivot[fliptets[0].ver];
+      // A hull edge. The current convex hull may be enlarged.
+      fsym(fliptets[0], fliptets[1]);
+      pts = (point *) fliptets[1].tet;
+      ori = orient3d(pts[4], pts[5], pts[6], newpt);
+      if (ori < 0) {
+        // Visible. The convex hull will be enlarged.
+        // Decide which flip (2-to-3, 3-to-2, or 4-to-1) to use.
+        // Check if the tet [a,c,e,d] or [c,b,e,d] exists.
+        enext(fliptets[1], fliptets[2]); 
+        eprev(fliptets[1], fliptets[3]); 
+        fnextself(fliptets[2]); // [a,c,e,*]
+        fnextself(fliptets[3]); // [c,b,e,*]
+        if (oppo(fliptets[2]) == newpt) {
+          if (oppo(fliptets[3]) == newpt) {
+            // Both tets exist! A 4-to-1 flip is found.
+            terminatetetgen(this, 2); // Report a bug.
+          } else {
+            esym(fliptets[2], fliptets[0]);
+            fnext(fliptets[0], fliptets[1]); 
+            fnext(fliptets[1], fliptets[2]); 
+            // Perform a 3-to-2 flip. Replace edge [c,a] by face [d,e,b].
+            // This corresponds to my standard labels, where edge [e,d] is
+            //   repalced by face [a,b,c], and a is the new vertex. 
+            //   [0] [c,a,d,e] (d = newpt)
+            //   [1] [c,a,e,b] (c = dummypoint)
+            //   [2] [c,a,b,d]
+            flip32(fliptets, 1, fc);
+          }
+        } else {
+          if (oppo(fliptets[3]) == newpt) {
+            fnext(fliptets[3], fliptets[0]);
+            fnext(fliptets[0], fliptets[1]); 
+            fnext(fliptets[1], fliptets[2]); 
+            // Perform a 3-to-2 flip. Replace edge [c,b] by face [d,a,e].
+            //   [0] [c,b,d,a] (d = newpt)
+            //   [1] [c,b,a,e] (c = dummypoint)
+            //   [2] [c,b,e,d]
+            flip32(fliptets, 1, fc);
+          } else {
+            if (hullflag) {
+              // Reject this flip if pe is already marked.
+              pe = oppo(fliptets[1]);
+              if (!pinfected(pe)) {
+                pinfect(pe);
+                cavetetvertlist->newindex((void **) &parypt);
+                *parypt = pe;
+                // Perform a 2-to-3 flip.
+                flip23(fliptets, 1, fc);
+              } else {
+                // Reject this flip.
+                flipcount--;
+              }
+            } else {
+              // Perform a 2-to-3 flip. Replace face [a,b,c] by edge [e,d].
+              //   [0] [a,b,c,d], d = newpt.
+              //   [1] [b,a,c,e], c = dummypoint.
+              flip23(fliptets, 1, fc);
+            }
+          }
+        }
+        flipcount++;
+      } 
+      continue;
+    } // if (dummypoint)
+
+    fsym(fliptets[0], fliptets[1]);
+    if ((point) fliptets[1].tet[7] == dummypoint) {
+      // A hull face is locally Delaunay.
+      continue;
+    }
+    // Check if the adjacent tet has already been tested.
+    if (marktested(fliptets[1])) {
+      // It has been tested and it is Delaunay.
+      continue;
+    }
+
+    // Test whether the face is locally Delaunay or not.
+    pts = (point *) fliptets[1].tet; 
+    if (b->weighted) {
+      sign = orient4d_s(pts[4], pts[5], pts[6], pts[7], newpt,
+                        pts[4][3], pts[5][3], pts[6][3], pts[7][3],
+                        newpt[3]);
+    } else {
+      sign = insphere_s(pts[4], pts[5], pts[6], pts[7], newpt);
+    }
+
+
+    if (sign < 0) {
+      point pd = newpt;
+      point pe = oppo(fliptets[1]);
+      // Check the convexity of its three edges. Stop checking either a
+      //   locally non-convex edge (ori < 0) or a flat edge (ori = 0) is
+      //   encountered, and 'fliptet' represents that edge.
+      for (i = 0; i < 3; i++) {
+        ori = orient3d(org(fliptets[0]), dest(fliptets[0]), pd, pe);
+        if (ori <= 0) break;
+        enextself(fliptets[0]);
+      }
+      if (ori > 0) {
+        // A 2-to-3 flip is found.
+        //   [0] [a,b,c,d], 
+        //   [1] [b,a,c,e]. no dummypoint.
+        flip23(fliptets, 0, fc);
+        flipcount++;
+      } else { // ori <= 0
+        // The edge ('fliptets[0]' = [a',b',c',d]) is non-convex or flat,
+        //   where the edge [a',b'] is one of [a,b], [b,c], and [c,a].
+        // Check if there are three or four tets sharing at this edge.        
+        esymself(fliptets[0]); // [b,a,d,c]
+        for (i = 0; i < 3; i++) {
+          fnext(fliptets[i], fliptets[i+1]);
+        }
+        if (fliptets[3].tet == fliptets[0].tet) {
+          // A 3-to-2 flip is found. (No hull tet.)
+          flip32(fliptets, 0, fc); 
+          flipcount++;
+        } else {
+          // There are more than 3 tets at this edge.
+          fnext(fliptets[3], fliptets[4]);
+          if (fliptets[4].tet == fliptets[0].tet) {
+            if (ori == 0) {
+              // A 4-to-4 flip is found. (Two hull tets may be involved.)
+              // Current tets in 'fliptets':
+              //   [0] [b,a,d,c] (d may be newpt)
+              //   [1] [b,a,c,e]
+              //   [2] [b,a,e,f] (f may be dummypoint)
+              //   [3] [b,a,f,d]
+              esymself(fliptets[0]); // [a,b,c,d] 
+              // A 2-to-3 flip replaces face [a,b,c] by edge [e,d].
+              //   This creates a degenerate tet [e,d,a,b] (tmpfliptets[0]).
+              //   It will be removed by the followed 3-to-2 flip.
+              flip23(fliptets, 0, fc); // No hull tet.
+              fnext(fliptets[3], fliptets[1]);
+              fnext(fliptets[1], fliptets[2]);
+              // Current tets in 'fliptets':
+              //   [0] [...]
+              //   [1] [b,a,d,e] (degenerated, d may be new point).
+              //   [2] [b,a,e,f] (f may be dummypoint)
+              //   [3] [b,a,f,d]
+              // A 3-to-2 flip replaces edge [b,a] by face [d,e,f].
+              //   Hull tets may be involved (f may be dummypoint).
+              flip32(&(fliptets[1]), (apex(fliptets[3]) == dummypoint), fc);
+              flipcount++;
+            }
+          }
+        }
+      } // ori
+    } else {
+      // The adjacent tet is Delaunay. Mark it to avoid testing it again.
+      marktest(fliptets[1]);
+      // Save it for unmarking it later.
+      cavebdrylist->newindex((void **) &parytet);
+      *parytet = fliptets[1];
+    }
+
+  } // while (flipstack)
+
+  // Unmark saved tetrahedra.
+  for (i = 0; i < cavebdrylist->objects; i++) {
+    parytet = (triface *) fastlookup(cavebdrylist, i);
+    unmarktest(*parytet);
+  }
+  cavebdrylist->restart();
+
+  if (hullflag) {
+    // Unmark infected vertices.
+    for (i = 0; i < cavetetvertlist->objects; i++) {
+      parypt = (point *) fastlookup(cavetetvertlist, i);
+      puninfect(*parypt);
+    }
+    cavetetvertlist->restart();
+  }
+
+
+  return flipcount;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// initialdelaunay()    Create an initial Delaunay tetrahedralization.       //
+//                                                                           //
+// The tetrahedralization contains only one tetrahedron abcd, and four hull  //
+// tetrahedra. The points pa, pb, pc, and pd must be linearly independent.   //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::initialdelaunay(point pa, point pb, point pc, point pd)
+{
+  triface firsttet, tetopa, tetopb, tetopc, tetopd;
+  triface worktet, worktet1;
+
+  if (b->verbose > 2) {
+    printf("      Create init tet (%d, %d, %d, %d)\n", pointmark(pa),
+           pointmark(pb), pointmark(pc), pointmark(pd));
+  }
+
+  // Create the first tetrahedron.
+  maketetrahedron(&firsttet);
+  setvertices(firsttet, pa, pb, pc, pd);
+  // Create four hull tetrahedra.
+  maketetrahedron(&tetopa);
+  setvertices(tetopa, pb, pc, pd, dummypoint);
+  maketetrahedron(&tetopb);
+  setvertices(tetopb, pc, pa, pd, dummypoint);
+  maketetrahedron(&tetopc);
+  setvertices(tetopc, pa, pb, pd, dummypoint);
+  maketetrahedron(&tetopd);
+  setvertices(tetopd, pb, pa, pc, dummypoint);
+  hullsize += 4;
+
+  // Connect hull tetrahedra to firsttet (at four faces of firsttet).
+  bond(firsttet, tetopd);
+  esym(firsttet, worktet);
+  bond(worktet, tetopc); // ab
+  enextesym(firsttet, worktet);
+  bond(worktet, tetopa); // bc 
+  eprevesym(firsttet, worktet);
+  bond(worktet, tetopb); // ca
+
+  // Connect hull tetrahedra together (at six edges of firsttet).
+  esym(tetopc, worktet); 
+  esym(tetopd, worktet1);
+  bond(worktet, worktet1); // ab
+  esym(tetopa, worktet);
+  eprevesym(tetopd, worktet1);
+  bond(worktet, worktet1); // bc
+  esym(tetopb, worktet);
+  enextesym(tetopd, worktet1);
+  bond(worktet, worktet1); // ca
+  eprevesym(tetopc, worktet);
+  enextesym(tetopb, worktet1);
+  bond(worktet, worktet1); // da
+  eprevesym(tetopa, worktet);
+  enextesym(tetopc, worktet1);
+  bond(worktet, worktet1); // db
+  eprevesym(tetopb, worktet);
+  enextesym(tetopa, worktet1);
+  bond(worktet, worktet1); // dc
+
+  // Set the vertex type.
+  if (pointtype(pa) == UNUSEDVERTEX) {
+    setpointtype(pa, VOLVERTEX);
+  }
+  if (pointtype(pb) == UNUSEDVERTEX) {
+    setpointtype(pb, VOLVERTEX);
+  }
+  if (pointtype(pc) == UNUSEDVERTEX) {
+    setpointtype(pc, VOLVERTEX);
+  }
+  if (pointtype(pd) == UNUSEDVERTEX) {
+    setpointtype(pd, VOLVERTEX);
+  }
+
+  setpoint2tet(pa, encode(firsttet));
+  setpoint2tet(pb, encode(firsttet));
+  setpoint2tet(pc, encode(firsttet));
+  setpoint2tet(pd, encode(firsttet));
+
+  // Remember the first tetrahedron.
+  recenttet = firsttet;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// incrementaldelaunay()    Create a Delaunay tetrahedralization by          //
+//                          the incremental approach.                        //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+
+void tetgenmesh::incrementaldelaunay(clock_t& tv)
+{
+  triface searchtet;
+  point *permutarray, swapvertex;
+  REAL v1[3], v2[3], n[3];
+  REAL bboxsize, bboxsize2, bboxsize3, ori;
+  int randindex; 
+  int ngroup = 0;
+  int i, j;
+
+  if (!b->quiet) {
+    printf("Delaunizing vertices...\n");
+  }
+
+  // Form a random permuation (uniformly at random) of the set of vertices.
+  permutarray = new point[in->numberofpoints];
+  points->traversalinit();
+
+  if (b->no_sort) {
+    if (b->verbose) {
+      printf("  Using the input order.\n"); 
+    }
+    for (i = 0; i < in->numberofpoints; i++) {
+      permutarray[i] = (point) points->traverse();
+    }
+  } else {
+    if (b->verbose) {
+      printf("  Permuting vertices.\n"); 
+    }
+    srand(in->numberofpoints);
+    for (i = 0; i < in->numberofpoints; i++) {
+      randindex = rand() % (i + 1); // randomnation(i + 1);
+      permutarray[i] = permutarray[randindex];
+      permutarray[randindex] = (point) points->traverse();
+    }
+    if (b->brio_hilbert) { // -b option
+      if (b->verbose) {
+        printf("  Sorting vertices.\n"); 
+      }
+      hilbert_init(in->mesh_dim);
+      brio_multiscale_sort(permutarray, in->numberofpoints, b->brio_threshold, 
+                           b->brio_ratio, &ngroup);
+    }
+  }
+
+  tv = clock(); // Remember the time for sorting points.
+
+  // Calculate the diagonal size of its bounding box.
+  bboxsize = sqrt(norm2(xmax - xmin, ymax - ymin, zmax - zmin));
+  bboxsize2 = bboxsize * bboxsize;
+  bboxsize3 = bboxsize2 * bboxsize;
+
+  // Make sure the second vertex is not identical with the first one.
+  i = 1;
+  while ((distance(permutarray[0],permutarray[i])/bboxsize)<b->epsilon) {
+    i++;
+    if (i == in->numberofpoints - 1) {
+      printf("Exception:  All vertices are (nearly) identical (Tol = %g).\n",
+             b->epsilon);
+      terminatetetgen(this, 10);
+    }
+  }
+  if (i > 1) {
+    // Swap to move the non-identical vertex from index i to index 1.
+    swapvertex = permutarray[i];
+    permutarray[i] = permutarray[1];
+    permutarray[1] = swapvertex;
+  }
+
+  // Make sure the third vertex is not collinear with the first two.
+  // Acknowledgement:  Thanks Jan Pomplun for his correction by using 
+  //   epsilon^2 and epsilon^3 (instead of epsilon). 2013-08-15.
+  i = 2;
+  for (j = 0; j < 3; j++) {
+    v1[j] = permutarray[1][j] - permutarray[0][j];
+    v2[j] = permutarray[i][j] - permutarray[0][j];
+  }
+  cross(v1, v2, n);
+  while ((sqrt(norm2(n[0], n[1], n[2])) / bboxsize2) < 
+         (b->epsilon * b->epsilon)) {
+    i++;
+    if (i == in->numberofpoints - 1) {
+      printf("Exception:  All vertices are (nearly) collinear (Tol = %g).\n",
+             b->epsilon);
+      terminatetetgen(this, 10);
+    }
+    for (j = 0; j < 3; j++) {
+      v2[j] = permutarray[i][j] - permutarray[0][j];
+    }
+    cross(v1, v2, n);
+  }
+  if (i > 2) {
+    // Swap to move the non-identical vertex from index i to index 1.
+    swapvertex = permutarray[i];
+    permutarray[i] = permutarray[2];
+    permutarray[2] = swapvertex;
+  }
+
+  // Make sure the fourth vertex is not coplanar with the first three.
+  i = 3;
+  ori = orient3dfast(permutarray[0], permutarray[1], permutarray[2], 
+                     permutarray[i]);
+  while ((fabs(ori) / bboxsize3) < (b->epsilon * b->epsilon * b->epsilon)) {
+    i++;
+    if (i == in->numberofpoints) {
+      printf("Exception:  All vertices are coplanar (Tol = %g).\n",
+             b->epsilon);
+      terminatetetgen(this, 10);
+    }
+    ori = orient3dfast(permutarray[0], permutarray[1], permutarray[2], 
+                       permutarray[i]);
+  }
+  if (i > 3) {
+    // Swap to move the non-identical vertex from index i to index 1.
+    swapvertex = permutarray[i];
+    permutarray[i] = permutarray[3];
+    permutarray[3] = swapvertex;
+  }
+
+  // Orient the first four vertices in permutarray so that they follow the
+  //   right-hand rule.
+  if (ori > 0.0) {
+    // Swap the first two vertices.
+    swapvertex = permutarray[0];
+    permutarray[0] = permutarray[1];
+    permutarray[1] = swapvertex;
+  }
+
+  // Create the initial Delaunay tetrahedralization.
+  initialdelaunay(permutarray[0], permutarray[1], permutarray[2],
+                  permutarray[3]);
+
+  if (b->verbose) {
+    printf("  Incrementally inserting vertices.\n");
+  }
+  insertvertexflags ivf;
+  flipconstraints fc;
+
+  // Choose algorithm: Bowyer-Watson (default) or Incremental Flip
+  if (b->incrflip) {
+    ivf.bowywat = 0;
+    ivf.lawson = 1;
+    fc.enqflag = 1;
+  } else {
+    ivf.bowywat = 1;
+    ivf.lawson = 0;
+  }
+
+
+  for (i = 4; i < in->numberofpoints; i++) {
+    if (pointtype(permutarray[i]) == UNUSEDVERTEX) {
+      setpointtype(permutarray[i], VOLVERTEX);
+    }
+    if (b->brio_hilbert || b->no_sort) { // -b or -b/1
+      // Start the last updated tet.
+      searchtet.tet = recenttet.tet;
+    } else { // -b0
+      // Randomly choose the starting tet for point location.
+      searchtet.tet = NULL;
+    }
+    ivf.iloc = (int) OUTSIDE;
+    // Insert the vertex.
+    if (insertpoint(permutarray[i], &searchtet, NULL, NULL, &ivf)) {
+      if (flipstack != NULL) {
+        // Perform flip to recover Delaunayness.
+        incrementalflip(permutarray[i], (ivf.iloc == (int) OUTSIDE), &fc);
+      }
+    } else {
+      if (ivf.iloc == (int) ONVERTEX) {
+        // The point already exists. Mark it and do nothing on it.
+        swapvertex = org(searchtet);
+        if (b->object != tetgenbehavior::STL) {
+          if (!b->quiet) {
+            printf("Warning:  Point #%d is coincident with #%d. Ignored!\n",
+                   pointmark(permutarray[i]), pointmark(swapvertex));
+          }
+        }
+        setpoint2ppt(permutarray[i], swapvertex);
+        setpointtype(permutarray[i], DUPLICATEDVERTEX);
+        dupverts++;
+      } else if (ivf.iloc == (int) NEARVERTEX) {
+        swapvertex = org(searchtet);
+        if (!b->quiet) {
+          printf("Warning:  Point %d is replaced by point %d.\n",
+                 pointmark(permutarray[i]), pointmark(swapvertex));
+          printf("  Avoid creating a very short edge (len = %g) (< %g).\n",
+                 permutarray[i][3], minedgelength);
+          printf("  You may try a smaller tolerance (-T) (current is %g)\n", 
+                 b->epsilon);
+          printf("  or use the option -M0/1 to avoid such replacement.\n");
+        }
+        // Remember it is a duplicated point.
+        setpoint2ppt(permutarray[i], swapvertex);
+        setpointtype(permutarray[i], DUPLICATEDVERTEX);
+        dupverts++;
+      } else if (ivf.iloc == (int) NONREGULAR) {
+        // The point is non-regular. Skipped.
+        if (b->verbose) {
+          printf("  Point #%d is non-regular, skipped.\n",
+                 pointmark(permutarray[i]));
+        }
+        setpointtype(permutarray[i], NREGULARVERTEX);
+        nonregularcount++;
+      }
+    }
+  }
+
+
+
+  delete [] permutarray;
+}
+
+////                                                                       ////
+////                                                                       ////
+//// delaunay_cxx /////////////////////////////////////////////////////////////
+
+//// surface_cxx //////////////////////////////////////////////////////////////
+////                                                                       ////
+////                                                                       ////
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// flipshpush()    Push a facet edge into flip stack.                        //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::flipshpush(face* flipedge)
+{
+  badface *newflipface;
+
+  newflipface = (badface *) flippool->alloc();
+  newflipface->ss = *flipedge;
+  newflipface->forg = sorg(*flipedge);
+  newflipface->fdest = sdest(*flipedge);
+  newflipface->nextitem = flipstack;
+  flipstack = newflipface;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// flip22()    Perform a 2-to-2 flip in surface mesh.                        //
+//                                                                           //
+// 'flipfaces' is an array of two subfaces. On input, they are [a,b,c] and   //
+// [b,a,d]. On output, they are [c,d,b] and [d,c,a]. As a result, edge [a,b] //
+// is replaced by edge [c,d].                                                //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::flip22(face* flipfaces, int flipflag, int chkencflag)
+{
+  face bdedges[4], outfaces[4], infaces[4];
+  face bdsegs[4];
+  face checkface;
+  point pa, pb, pc, pd;
+  int i;
+
+  pa = sorg(flipfaces[0]);
+  pb = sdest(flipfaces[0]);
+  pc = sapex(flipfaces[0]);
+  pd = sapex(flipfaces[1]);
+
+  if (sorg(flipfaces[1]) != pb) {
+    sesymself(flipfaces[1]);
+  }
+
+  flip22count++;
+
+  // Collect the four boundary edges.
+  senext(flipfaces[0], bdedges[0]);
+  senext2(flipfaces[0], bdedges[1]);
+  senext(flipfaces[1], bdedges[2]);
+  senext2(flipfaces[1], bdedges[3]);
+
+  // Collect outer boundary faces.
+  for (i = 0; i < 4; i++) {
+    spivot(bdedges[i], outfaces[i]);
+    infaces[i] = outfaces[i];
+    sspivot(bdedges[i], bdsegs[i]);
+    if (outfaces[i].sh != NULL) {
+      if (isshsubseg(bdedges[i])) {
+        spivot(infaces[i], checkface);
+        while (checkface.sh != bdedges[i].sh) {
+          infaces[i] = checkface;
+          spivot(infaces[i], checkface);
+        }
+      }
+    }
+  }
+
+  // The flags set in these two subfaces do not change.
+  // Shellmark does not change.
+  // area constraint does not change.
+
+  // Transform [a,b,c] -> [c,d,b].
+  setshvertices(flipfaces[0], pc, pd, pb);
+  // Transform [b,a,d] -> [d,c,a].
+  setshvertices(flipfaces[1], pd, pc, pa);
+
+  // Update the point-to-subface map.
+  if (pointtype(pa) == FREEFACETVERTEX) {
+    setpoint2sh(pa, sencode(flipfaces[1]));
+  }
+  if (pointtype(pb) == FREEFACETVERTEX) {
+    setpoint2sh(pb, sencode(flipfaces[0]));
+  }
+  if (pointtype(pc) == FREEFACETVERTEX) {
+    setpoint2sh(pc, sencode(flipfaces[0]));
+  }
+  if (pointtype(pd) == FREEFACETVERTEX) {
+    setpoint2sh(pd, sencode(flipfaces[0]));
+  }
+
+  // Reconnect boundary edges to outer boundary faces.
+  for (i = 0; i < 4; i++) {
+    if (outfaces[(3 + i) % 4].sh != NULL) {
+      // Make sure that the subface has the ori as the segment.
+      if (bdsegs[(3 + i) % 4].sh != NULL) {
+        bdsegs[(3 + i) % 4].shver = 0;
+        if (sorg(bdedges[i]) != sorg(bdsegs[(3 + i) % 4])) {
+          sesymself(bdedges[i]);
+        }
+      }
+      sbond1(bdedges[i], outfaces[(3 + i) % 4]);
+      sbond1(infaces[(3 + i) % 4], bdedges[i]);
+    } else {
+      sdissolve(bdedges[i]);
+    }
+    if (bdsegs[(3 + i) % 4].sh != NULL) {
+      ssbond(bdedges[i], bdsegs[(3 + i) % 4]);
+      if (chkencflag & 1) {
+        // Queue this segment for encroaching check.
+        enqueuesubface(badsubsegs, &(bdsegs[(3 + i) % 4]));
+      }
+    } else {
+      ssdissolve(bdedges[i]);
+    }
+  }
+
+  if (chkencflag & 2) {
+    // Queue the flipped subfaces for quality/encroaching checks.
+    for (i = 0; i < 2; i++) {
+      enqueuesubface(badsubfacs, &(flipfaces[i]));
+    }
+  }
+
+  recentsh = flipfaces[0];
+
+  if (flipflag) {
+    // Put the boundary edges into flip stack.
+    for (i = 0; i < 4; i++) {
+      flipshpush(&(bdedges[i]));
+    }
+  }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// flip31()    Remove a vertex by transforming 3-to-1 subfaces.              //
+//                                                                           //
+// 'flipfaces' is an array of subfaces. Its length is at least 4.  On input, //
+// the first three faces are: [p,a,b], [p,b,c], and [p,c,a]. This routine    //
+// replaces them by one face [a,b,c], it is returned in flipfaces[3].        //
+//                                                                           //
+// NOTE: The three old subfaces are not deleted within this routine.  They   //
+// still hold pointers to their adjacent subfaces. These informations are    //
+// needed by the routine 'sremovevertex()' for recovering a segment.         //
+// The caller of this routine must delete the old subfaces after their uses. //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::flip31(face* flipfaces, int flipflag)
+{
+  face bdedges[3], outfaces[3], infaces[3];
+  face bdsegs[3];
+  face checkface;
+  point pa, pb, pc;
+  int i;
+
+  pa = sdest(flipfaces[0]);
+  pb = sdest(flipfaces[1]);
+  pc = sdest(flipfaces[2]);
+
+  flip31count++;
+
+  // Collect all infos at the three boundary edges.
+  for (i = 0; i < 3; i++) {
+    senext(flipfaces[i], bdedges[i]);
+    spivot(bdedges[i], outfaces[i]);
+    infaces[i] = outfaces[i];
+    sspivot(bdedges[i], bdsegs[i]);
+    if (outfaces[i].sh != NULL) {
+      if (isshsubseg(bdedges[i])) {
+        spivot(infaces[i], checkface);
+        while (checkface.sh != bdedges[i].sh) {
+          infaces[i] = checkface;
+          spivot(infaces[i], checkface);
+        }
+      }
+    }
+  } // i
+
+  // Create a new subface.
+  makeshellface(subfaces, &(flipfaces[3]));
+  setshvertices(flipfaces[3], pa, pb,pc);
+  setshellmark(flipfaces[3], shellmark(flipfaces[0]));
+  if (checkconstraints) {
+    //area = areabound(flipfaces[0]);
+    setareabound(flipfaces[3], areabound(flipfaces[0]));
+  }
+  if (useinsertradius) {
+    setfacetindex(flipfaces[3], getfacetindex(flipfaces[0]));
+  }
+
+  // Update the point-to-subface map.
+  if (pointtype(pa) == FREEFACETVERTEX) {
+    setpoint2sh(pa, sencode(flipfaces[3]));
+  }
+  if (pointtype(pb) == FREEFACETVERTEX) {
+    setpoint2sh(pb, sencode(flipfaces[3]));
+  }
+  if (pointtype(pc) == FREEFACETVERTEX) {
+    setpoint2sh(pc, sencode(flipfaces[3]));
+  }
+
+  // Update the three new boundary edges.
+  bdedges[0] = flipfaces[3];         // [a,b]
+  senext(flipfaces[3], bdedges[1]);  // [b,c]
+  senext2(flipfaces[3], bdedges[2]); // [c,a]
+
+  // Reconnect boundary edges to outer boundary faces.
+  for (i = 0; i < 3; i++) {
+    if (outfaces[i].sh != NULL) {
+      // Make sure that the subface has the ori as the segment.
+      if (bdsegs[i].sh != NULL) {
+        bdsegs[i].shver = 0;
+        if (sorg(bdedges[i]) != sorg(bdsegs[i])) {
+          sesymself(bdedges[i]);
+        }
+      }
+      sbond1(bdedges[i], outfaces[i]);
+      sbond1(infaces[i], bdedges[i]);
+    }
+    if (bdsegs[i].sh != NULL) {
+      ssbond(bdedges[i], bdsegs[i]);
+    }
+  }
+
+  recentsh = flipfaces[3];
+
+  if (flipflag) {
+    // Put the boundary edges into flip stack.
+    for (i = 0; i < 3; i++) {
+      flipshpush(&(bdedges[i]));
+    }
+  }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// lawsonflip()    Flip non-locally Delaunay edges.                          //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+long tetgenmesh::lawsonflip()
+{
+  badface *popface;
+  face flipfaces[2];
+  point pa, pb, pc, pd;
+  REAL sign;
+  long flipcount = 0;
+
+  if (b->verbose > 2) {
+    printf("      Lawson flip %ld edges.\n", flippool->items);
+  }
+
+  while (flipstack != (badface *) NULL) {
+
+    // Pop an edge from the stack.
+    popface = flipstack;
+    flipfaces[0] = popface->ss;
+    pa = popface->forg;
+    pb = popface->fdest;
+    flipstack = popface->nextitem; // The next top item in stack.
+    flippool->dealloc((void *) popface);
+
+    // Skip it if it is dead.
+    if (flipfaces[0].sh[3] == NULL) continue;
+    // Skip it if it is not the same edge as we saved.
+    if ((sorg(flipfaces[0]) != pa) || (sdest(flipfaces[0]) != pb)) continue;
+    // Skip it if it is a subsegment.
+    if (isshsubseg(flipfaces[0])) continue;
+
+    // Get the adjacent face.
+    spivot(flipfaces[0], flipfaces[1]);
+    if (flipfaces[1].sh == NULL) continue; // Skip a hull edge.
+    pc = sapex(flipfaces[0]);
+    pd = sapex(flipfaces[1]);
+
+    sign = incircle3d(pa, pb, pc, pd);
+
+    if (sign < 0) {
+      // It is non-locally Delaunay. Flip it.
+      flip22(flipfaces, 1, 0);
+      flipcount++;
+    }
+  }
+
+  if (b->verbose > 2) {
+    printf("      Performed %ld flips.\n", flipcount);
+  }
+
+  return flipcount;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// sinsertvertex()    Insert a vertex into a triangulation of a facet.       //
+//                                                                           //
+// This function uses three global arrays: 'caveshlist', 'caveshbdlist', and //
+// 'caveshseglist'. On return, 'caveshlist' contains old subfaces in C(p),   //
+// 'caveshbdlist' contains new subfaces in C(p). If the new point lies on a  //
+// segment, 'cavesegshlist' returns the two new subsegments.                 //
+//                                                                           //
+// 'iloc' suggests the location of the point. If it is OUTSIDE, this routine //
+// will first locate the point. It starts searching from 'searchsh' or 'rec- //
+// entsh' if 'searchsh' is NULL.                                             //
+//                                                                           //
+// If 'bowywat' is set (1), the Bowyer-Watson algorithm is used to insert    //
+// the vertex. Otherwise, only insert the vertex in the initial cavity.      // 
+//                                                                           //
+// If 'iloc' is 'INSTAR', this means the cavity of this vertex was already   //
+// provided in the list 'caveshlist'.                                        //
+//                                                                           //
+// If 'splitseg' is not NULL, the new vertex lies on the segment and it will //
+// be split. 'iloc' must be either 'ONEDGE' or 'INSTAR'.                     //
+//                                                                           //
+// 'rflag' (rounding) is a parameter passed to slocate() function.  If it is //
+// set, after the location of the point is found, either ONEDGE or ONFACE,   //
+// round the result using an epsilon.                                        //
+//                                                                           //
+// NOTE: the old subfaces in C(p) are not deleted. They're needed in case we //
+// want to remove the new point immediately.                                 //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+int tetgenmesh::sinsertvertex(point insertpt, face *searchsh, face *splitseg,
+                              int iloc, int bowywat, int rflag)
+{
+  face cavesh, neighsh, *parysh;
+  face newsh, casout, casin;
+  face checkseg;
+  point pa, pb;
+  enum locateresult loc = OUTSIDE;
+  REAL sign, ori;
+  int i, j;
+
+  if (b->verbose > 2) {
+    printf("      Insert facet point %d.\n", pointmark(insertpt));
+  }
+
+  if (bowywat == 3) {
+    loc = INSTAR;
+  }
+
+  if ((splitseg != NULL) && (splitseg->sh != NULL)) {
+    // A segment is going to be split, no point location.
+    spivot(*splitseg, *searchsh);
+    if (loc != INSTAR) loc = ONEDGE;
+  } else {
+    if (loc != INSTAR) loc = (enum locateresult) iloc;
+    if (loc == OUTSIDE) {
+      // Do point location in surface mesh.
+      if (searchsh->sh == NULL) {
+        *searchsh = recentsh;
+      }
+      // Search the vertex. An above point must be provided ('aflag' = 1).
+      loc = slocate(insertpt, searchsh, 1, 1, rflag);
+    }
+  }
+
+
+  // Form the initial sC(p).
+  if (loc == ONFACE) {
+    // Add the face into list (in B-W cavity).
+    smarktest(*searchsh);
+    caveshlist->newindex((void **) &parysh);
+    *parysh = *searchsh;
+  } else if (loc == ONEDGE) {
+    if ((splitseg != NULL) && (splitseg->sh != NULL)) {
+      splitseg->shver = 0;
+      pa = sorg(*splitseg);
+    } else {
+      pa = sorg(*searchsh);
+    }
+    if (searchsh->sh != NULL) {
+      // Collect all subfaces share at this edge.
+      neighsh = *searchsh;
+      while (1) {
+        // Adjust the origin of its edge to be 'pa'.
+        if (sorg(neighsh) != pa) sesymself(neighsh);
+        // Add this face into list (in B-W cavity).
+        smarktest(neighsh);
+        caveshlist->newindex((void **) &parysh);
+        *parysh = neighsh;
+        // Add this face into face-at-splitedge list.
+        cavesegshlist->newindex((void **) &parysh);
+        *parysh = neighsh;
+        // Go to the next face at the edge.
+        spivotself(neighsh);
+        // Stop if all faces at the edge have been visited.
+        if (neighsh.sh == searchsh->sh) break;
+        if (neighsh.sh == NULL) break;
+      }
+    } // If (not a non-dangling segment).
+  } else if (loc == ONVERTEX) {
+    return (int) loc;
+  } else if (loc == OUTSIDE) {
+    // Comment: This should only happen during the surface meshing step.
+    // Enlarge the convex hull of the triangulation by including p.
+    // An above point of the facet is set in 'dummypoint' to replace
+    // orient2d tests by orient3d tests.
+    // Imagine that the current edge a->b (in 'searchsh') is horizontal in a
+    //   plane, and a->b is directed from left to right, p lies above a->b.  
+    //   Find the right-most edge of the triangulation which is visible by p.
+    neighsh = *searchsh;
+    while (1) {
+      senext2self(neighsh);
+      spivot(neighsh, casout);
+      if (casout.sh == NULL) {
+        // A convex hull edge. Is it visible by p.
+        ori = orient3d(sorg(neighsh), sdest(neighsh), dummypoint, insertpt);
+        if (ori < 0) {
+          *searchsh = neighsh; // Visible, update 'searchsh'.
+        } else {
+          break; // 'searchsh' is the right-most visible edge.
+        }
+      } else {
+        if (sorg(casout) != sdest(neighsh)) sesymself(casout);
+        neighsh = casout;
+      }
+    }
+    // Create new triangles for all visible edges of p (from right to left).
+    casin.sh = NULL;  // No adjacent face at right.
+    pa = sorg(*searchsh);
+    pb = sdest(*searchsh);
+    while (1) {
+      // Create a new subface on top of the (visible) edge.
+      makeshellface(subfaces, &newsh); 
+      setshvertices(newsh, pb, pa, insertpt);
+      setshellmark(newsh, shellmark(*searchsh));
+      if (checkconstraints) {
+        //area = areabound(*searchsh);
+        setareabound(newsh, areabound(*searchsh));
+      }
+      if (useinsertradius) {
+        setfacetindex(newsh, getfacetindex(*searchsh));
+      }
+      // Connect the new subface to the bottom subfaces.
+      sbond1(newsh, *searchsh);
+      sbond1(*searchsh, newsh);
+      // Connect the new subface to its right-adjacent subface.
+      if (casin.sh != NULL) {
+        senext(newsh, casout);
+        sbond1(casout, casin);
+        sbond1(casin, casout);
+      }
+      // The left-adjacent subface has not been created yet.
+      senext2(newsh, casin);
+      // Add the new face into list (inside the B-W cavity).
+      smarktest(newsh);
+      caveshlist->newindex((void **) &parysh);
+      *parysh = newsh;
+      // Move to the convex hull edge at the left of 'searchsh'.
+      neighsh = *searchsh;
+      while (1) {
+        senextself(neighsh);
+        spivot(neighsh, casout);
+        if (casout.sh == NULL) {
+          *searchsh = neighsh;
+          break;
+        }
+        if (sorg(casout) != sdest(neighsh)) sesymself(casout);
+        neighsh = casout;
+      }
+      // A convex hull edge. Is it visible by p.
+      pa = sorg(*searchsh);
+      pb = sdest(*searchsh);
+      ori = orient3d(pa, pb, dummypoint, insertpt);
+      // Finish the process if p is not visible by the hull edge.
+      if (ori >= 0) break;
+    }
+  } else if (loc == INSTAR) {
+    // Under this case, the sub-cavity sC(p) has already been formed in
+    //   insertvertex().
+  }
+
+  // Form the Bowyer-Watson cavity sC(p).
+  for (i = 0; i < caveshlist->objects; i++) {
+    cavesh = * (face *) fastlookup(caveshlist, i);
+    for (j = 0; j < 3; j++) {
+      if (!isshsubseg(cavesh)) {
+        spivot(cavesh, neighsh);
+        if (neighsh.sh != NULL) {
+          // The adjacent face exists.
+          if (!smarktested(neighsh)) {
+            if (bowywat) {
+              if (loc == INSTAR) { // if (bowywat > 2) {
+                // It must be a boundary edge.
+                sign = 1;
+              } else {
+                // Check if this subface is connected to adjacent tet(s).
+                if (!isshtet(neighsh)) {
+                  // Check if the subface is non-Delaunay wrt. the new pt.
+                  sign = incircle3d(sorg(neighsh), sdest(neighsh), 
+                                    sapex(neighsh), insertpt);
+                } else {
+                  // It is connected to an adjacent tet. A boundary edge.
+                  sign = 1;
+                }
+              }
+              if (sign < 0) {
+                // Add the adjacent face in list (in B-W cavity).
+                smarktest(neighsh);
+                caveshlist->newindex((void **) &parysh);
+                *parysh = neighsh;
+              }
+            } else {
+              sign = 1; // A boundary edge.
+            }
+          } else {
+            sign = -1; // Not a boundary edge.
+          }
+        } else {
+          // No adjacent face. It is a hull edge.
+          if (loc == OUTSIDE) {
+            // It is a boundary edge if it does not contain p.
+            if ((sorg(cavesh) == insertpt) || (sdest(cavesh) == insertpt)) {
+              sign = -1; // Not a boundary edge.
+            } else {
+              sign = 1; // A boundary edge.
+            }
+          } else {
+            sign = 1; // A boundary edge.
+          }
+        }
+      } else {
+        // Do not across a segment. It is a boundary edge.
+        sign = 1;
+      }
+      if (sign >= 0) {
+        // Add a boundary edge.
+        caveshbdlist->newindex((void **) &parysh);
+        *parysh = cavesh;
+      }
+      senextself(cavesh);
+    } // j
+  } // i
+
+
+  // Creating new subfaces.
+  for (i = 0; i < caveshbdlist->objects; i++) {
+    parysh = (face *) fastlookup(caveshbdlist, i);
+    sspivot(*parysh, checkseg);
+    if ((parysh->shver & 01) != 0) sesymself(*parysh);
+    pa = sorg(*parysh);
+    pb = sdest(*parysh);
+    // Create a new subface.
+    makeshellface(subfaces, &newsh); 
+    setshvertices(newsh, pa, pb, insertpt);
+    setshellmark(newsh, shellmark(*parysh));
+    if (checkconstraints) {
+      //area = areabound(*parysh);
+      setareabound(newsh, areabound(*parysh));
+    }
+    if (useinsertradius) {
+      setfacetindex(newsh, getfacetindex(*parysh));
+    }
+    // Update the point-to-subface map.
+    if (pointtype(pa) == FREEFACETVERTEX) {
+      setpoint2sh(pa, sencode(newsh));
+    }
+    if (pointtype(pb) == FREEFACETVERTEX) {
+      setpoint2sh(pb, sencode(newsh));
+    }
+    // Connect newsh to outer subfaces.
+    spivot(*parysh, casout);
+    if (casout.sh != NULL) {
+      casin = casout;
+      if (checkseg.sh != NULL) {
+        // Make sure that newsh has the right ori at this segment.
+        checkseg.shver = 0;
+        if (sorg(newsh) != sorg(checkseg)) {
+          sesymself(newsh);
+          sesymself(*parysh); // This side should also be inverse.
+        }
+        spivot(casin, neighsh);
+        while (neighsh.sh != parysh->sh) {
+          casin = neighsh;
+          spivot(casin, neighsh);
+        }
+      }
+      sbond1(newsh, casout);
+      sbond1(casin, newsh);
+    }
+    if (checkseg.sh != NULL) {
+      ssbond(newsh, checkseg);
+    }
+    // Connect oldsh <== newsh (for connecting adjacent new subfaces).
+    //   *parysh and newsh point to the same edge and the same ori.
+    sbond1(*parysh, newsh);
+  }
+
+  if (newsh.sh != NULL) {
+    // Set a handle for searching.
+    recentsh = newsh;
+  }
+
+  // Update the point-to-subface map.
+  if (pointtype(insertpt) == FREEFACETVERTEX) {
+    setpoint2sh(insertpt, sencode(newsh));
+  }
+
+  // Connect adjacent new subfaces together.
+  for (i = 0; i < caveshbdlist->objects; i++) {
+    // Get an old subface at edge [a, b].
+    parysh = (face *) fastlookup(caveshbdlist, i);
+    spivot(*parysh, newsh); // The new subface [a, b, p].
+    senextself(newsh); // At edge [b, p].
+    spivot(newsh, neighsh);
+    if (neighsh.sh == NULL) {
+      // Find the adjacent new subface at edge [b, p].
+      pb = sdest(*parysh);
+      neighsh = *parysh;
+      while (1) {
+        senextself(neighsh);
+        spivotself(neighsh);
+        if (neighsh.sh == NULL) break;
+        if (!smarktested(neighsh)) break;
+        if (sdest(neighsh) != pb) sesymself(neighsh);
+      }
+      if (neighsh.sh != NULL) {
+        // Now 'neighsh' is a new subface at edge [b, #].
+        if (sorg(neighsh) != pb) sesymself(neighsh);
+        senext2self(neighsh); // Go to the open edge [p, b].
+        sbond(newsh, neighsh);
+      }
+    }
+    spivot(*parysh, newsh); // The new subface [a, b, p].
+    senext2self(newsh); // At edge [p, a].
+    spivot(newsh, neighsh);
+    if (neighsh.sh == NULL) {
+      // Find the adjacent new subface at edge [p, a].
+      pa = sorg(*parysh);
+      neighsh = *parysh;
+      while (1) {
+        senext2self(neighsh);
+        spivotself(neighsh);
+        if (neighsh.sh == NULL) break;
+        if (!smarktested(neighsh)) break;
+        if (sorg(neighsh) != pa) sesymself(neighsh);
+      }
+      if (neighsh.sh != NULL) {
+        // Now 'neighsh' is a new subface at edge [#, a].
+        if (sdest(neighsh) != pa) sesymself(neighsh);
+        senextself(neighsh); // Go to the open edge [a, p].
+        sbond(newsh, neighsh);
+      }
+    }
+  }
+
+  if ((loc == ONEDGE) || ((splitseg != NULL) && (splitseg->sh != NULL))
+      || (cavesegshlist->objects > 0l)) {
+    // An edge is being split. We distinguish two cases:
+    //   (1) the edge is not on the boundary of the cavity;
+    //   (2) the edge is on the boundary of the cavity.
+    // In case (2), the edge is either a segment or a hull edge. There are
+    //   degenerated new faces in the cavity. They must be removed.
+    face aseg, bseg, aoutseg, boutseg;
+
+    for (i = 0; i < cavesegshlist->objects; i++) {
+      // Get the saved old subface.
+      parysh = (face *) fastlookup(cavesegshlist, i);
+      // Get a possible new degenerated subface.
+      spivot(*parysh, cavesh);
+      if (sapex(cavesh) == insertpt) {
+        // Found a degenerated new subface, i.e., case (2).
+        if (cavesegshlist->objects > 1) {
+          // There are more than one subface share at this edge.
+          j = (i + 1) % (int) cavesegshlist->objects;
+          parysh = (face *) fastlookup(cavesegshlist, j);
+          spivot(*parysh, neighsh);
+          // Adjust cavesh and neighsh both at edge a->b, and has p as apex.
+          if (sorg(neighsh) != sorg(cavesh)) {
+            sesymself(neighsh);
+          }
+          // Connect adjacent faces at two other edges of cavesh and neighsh.
+          //   As a result, the two degenerated new faces are squeezed from the
+          //   new triangulation of the cavity. Note that the squeezed faces
+          //   still hold the adjacent informations which will be used in 
+          //   re-connecting subsegments (if they exist). 
+          for (j = 0; j < 2; j++) { 
+            senextself(cavesh);
+            senextself(neighsh);
+            spivot(cavesh, newsh);
+            spivot(neighsh, casout);
+            sbond1(newsh, casout); // newsh <- casout.
+          }
+        } else {
+          // There is only one subface containing this edge [a,b]. Squeeze the
+          //   degenerated new face [a,b,c] by disconnecting it from its two 
+          //   adjacent subfaces at edges [b,c] and [c,a]. Note that the face
+          //   [a,b,c] still hold the connection to them.
+          for (j = 0; j < 2; j++) {
+            senextself(cavesh);
+            spivot(cavesh, newsh);
+            sdissolve(newsh);
+          }
+        }
+        //recentsh = newsh;
+        // Update the point-to-subface map.
+        if (pointtype(insertpt) == FREEFACETVERTEX) {
+          setpoint2sh(insertpt, sencode(newsh));
+        }
+      }
+    }
+
+    if ((splitseg != NULL) && (splitseg->sh != NULL)) {
+      if (loc != INSTAR) { // if (bowywat < 3) {
+        smarktest(*splitseg); // Mark it as being processed.
+      }
+      
+      aseg = *splitseg;
+      pa = sorg(*splitseg);
+      pb = sdest(*splitseg);
+
+      // Insert the new point p.
+      makeshellface(subsegs, &aseg);
+      makeshellface(subsegs, &bseg);
+
+      setshvertices(aseg, pa, insertpt, NULL);
+      setshvertices(bseg, insertpt, pb, NULL);
+      setshellmark(aseg, shellmark(*splitseg));
+      setshellmark(bseg, shellmark(*splitseg));
+      if (checkconstraints) {
+        setareabound(aseg, areabound(*splitseg));
+        setareabound(bseg, areabound(*splitseg));
+      }
+      if (useinsertradius) {
+        setfacetindex(aseg, getfacetindex(*splitseg));
+        setfacetindex(bseg, getfacetindex(*splitseg));
+      }
+
+      // Connect [#, a]<->[a, p].
+      senext2(*splitseg, boutseg); // Temporarily use boutseg.
+      spivotself(boutseg);
+      if (boutseg.sh != NULL) {
+        senext2(aseg, aoutseg);
+        sbond(boutseg, aoutseg);
+      }
+      // Connect [p, b]<->[b, #].
+      senext(*splitseg, aoutseg);
+      spivotself(aoutseg);
+      if (aoutseg.sh != NULL) {
+        senext(bseg, boutseg);
+        sbond(boutseg, aoutseg);
+      }
+      // Connect [a, p] <-> [p, b].
+      senext(aseg, aoutseg);
+      senext2(bseg, boutseg);
+      sbond(aoutseg, boutseg);
+
+      // Connect subsegs [a, p] and [p, b] to adjacent new subfaces.
+      // Although the degenerated new faces have been squeezed. They still
+      //   hold the connections to the actual new faces. 
+      for (i = 0; i < cavesegshlist->objects; i++) {        
+        parysh = (face *) fastlookup(cavesegshlist, i);
+        spivot(*parysh, neighsh);
+        // neighsh is a degenerated new face.
+        if (sorg(neighsh) != pa) {
+          sesymself(neighsh);
+        }
+        senext2(neighsh, newsh);
+        spivotself(newsh); // The edge [p, a] in newsh
+        ssbond(newsh, aseg);
+        senext(neighsh, newsh);
+        spivotself(newsh); // The edge [b, p] in newsh
+        ssbond(newsh, bseg);
+      }
+
+
+      // Let the point remember the segment it lies on.
+      if (pointtype(insertpt) == FREESEGVERTEX) {
+        setpoint2sh(insertpt, sencode(aseg));
+      }
+      // Update the point-to-seg map.
+      if (pointtype(pa) == FREESEGVERTEX) {
+        setpoint2sh(pa, sencode(aseg));
+      }
+      if (pointtype(pb) == FREESEGVERTEX) {
+        setpoint2sh(pb, sencode(bseg));
+      }
+    } // if ((splitseg != NULL) && (splitseg->sh != NULL)) 
+
+    // Delete all degenerated new faces.
+    for (i = 0; i < cavesegshlist->objects; i++) {
+      parysh = (face *) fastlookup(cavesegshlist, i);
+      spivotself(*parysh);
+      if (sapex(*parysh) == insertpt) {
+        shellfacedealloc(subfaces, parysh->sh);
+      }
+    }
+    cavesegshlist->restart();
+
+    if ((splitseg != NULL) && (splitseg->sh != NULL)) {
+      // Return the two new subsegments (for further process).
+      //   Re-use 'cavesegshlist'.
+      cavesegshlist->newindex((void **) &parysh);
+      *parysh = aseg;
+      cavesegshlist->newindex((void **) &parysh);
+      *parysh = bseg;
+    }
+  } // if (loc == ONEDGE)
+
+
+  return (int) loc;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// sremovevertex()    Remove a vertex from the surface mesh.                 //
+//                                                                           //
+// 'delpt' (p) is the vertex to be removed. If 'parentseg' is not NULL, p is //
+// a segment vertex, and the origin of 'parentseg' is p. Otherwise, p is a   //
+// facet vertex, and the origin of 'parentsh' is p.                          //
+//                                                                           //
+// Within each facet, we first use a sequence of 2-to-2 flips to flip any    //
+// edge at p, finally use a 3-to-1 flip to remove p.                         //
+//                                                                           //
+// All new created subfaces are returned in the global array 'caveshbdlist'. //
+// The new segment (when p is on segment) is returned in 'parentseg'.        //
+//                                                                           //
+// If 'lawson' > 0, the Lawson flip algorithm is used to recover Delaunay-   //
+// ness after p is removed.                                                  //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+int tetgenmesh::sremovevertex(point delpt, face* parentsh, face* parentseg,
+                              int lawson)
+{
+  face flipfaces[4], spinsh, *parysh;
+  point pa, pb, pc, pd;
+  REAL ori1, ori2;
+  int it, i, j;
+
+  if (parentseg != NULL) {
+    // 'delpt' (p) should be a Steiner point inserted in a segment [a,b],
+    //   where 'parentseg' should be [p,b]. Find the segment [a,p].
+    face startsh, neighsh, nextsh;
+    face abseg, prevseg, checkseg;
+    face adjseg1, adjseg2;
+    face fakesh;
+    senext2(*parentseg, prevseg);
+    spivotself(prevseg);
+    prevseg.shver = 0;
+    // Restore the original segment [a,b].
+    pa = sorg(prevseg);
+    pb = sdest(*parentseg);
+    if (b->verbose > 2) {
+      printf("      Remove vertex %d from segment [%d, %d].\n", 
+             pointmark(delpt), pointmark(pa), pointmark(pb));
+    }
+    makeshellface(subsegs, &abseg);
+    setshvertices(abseg, pa, pb, NULL);
+    setshellmark(abseg, shellmark(*parentseg));
+    if (checkconstraints) {
+      setareabound(abseg, areabound(*parentseg));
+    }
+    if (useinsertradius) {
+      setfacetindex(abseg, getfacetindex(*parentseg));
+    }
+    // Connect [#, a]<->[a, b].
+    senext2(prevseg, adjseg1);
+    spivotself(adjseg1);
+    if (adjseg1.sh != NULL) {
+      adjseg1.shver = 0;
+      senextself(adjseg1);
+      senext2(abseg, adjseg2);
+      sbond(adjseg1, adjseg2);
+    }
+    // Connect [a, b]<->[b, #].
+    senext(*parentseg, adjseg1);
+    spivotself(adjseg1);
+    if (adjseg1.sh != NULL) {
+      adjseg1.shver = 0;
+      senext2self(adjseg1);
+      senext(abseg, adjseg2);
+      sbond(adjseg1, adjseg2);
+    }
+    // Update the point-to-segment map.
+    setpoint2sh(pa, sencode(abseg));
+    setpoint2sh(pb, sencode(abseg));
+
+    // Get the faces in face ring at segment [p, b].
+    //   Re-use array 'caveshlist'.
+    spivot(*parentseg, *parentsh);
+    if (parentsh->sh != NULL) {
+      spinsh = *parentsh;
+      while (1) {
+        // Save this face in list.
+        caveshlist->newindex((void **) &parysh);
+        *parysh = spinsh;
+        // Go to the next face in the ring.
+        spivotself(spinsh);
+        if (spinsh.sh == NULL) {
+          break; // It is possible there is only one facet.
+        }
+        if (spinsh.sh == parentsh->sh) break;
+      }
+    }
+
+    // Create the face ring of the new segment [a,b]. Each face in the ring
+    //   is [a,b,p] (degenerated!). It will be removed (automatically).
+    for (i = 0; i < caveshlist->objects; i++) {
+      parysh = (face *) fastlookup(caveshlist, i);
+      startsh = *parysh;
+      if (sorg(startsh) != delpt) {
+        sesymself(startsh);
+      }      
+      // startsh is [p, b, #1], find the subface [a, p, #2].
+      neighsh = startsh;
+      while (1) {
+        senext2self(neighsh);
+        sspivot(neighsh, checkseg);
+        if (checkseg.sh != NULL) {
+          // It must be the segment [a, p].
+          break;
+        }
+        spivotself(neighsh);
+        if (sorg(neighsh) != delpt) sesymself(neighsh);
+      }
+      // Now neighsh is [a, p, #2].
+      if (neighsh.sh != startsh.sh) {
+        // Detach the two subsegments [a,p] and [p,b] from subfaces.
+        ssdissolve(startsh);
+        ssdissolve(neighsh);
+        // Create a degenerated subface [a,b,p]. It is used to: (1) hold the
+        //   new segment [a,b]; (2) connect to the two adjacent subfaces
+        //   [p,b,#] and [a,p,#].
+        makeshellface(subfaces, &fakesh);
+        setshvertices(fakesh, pa, pb, delpt);
+        setshellmark(fakesh, shellmark(startsh));
+        // Connect fakesh to the segment [a,b].
+        ssbond(fakesh, abseg);
+        // Connect fakesh to adjacent subfaces: [p,b,#1] and [a,p,#2].
+        senext(fakesh, nextsh);
+        sbond(nextsh, startsh);
+        senext2(fakesh, nextsh);
+        sbond(nextsh, neighsh);
+        smarktest(fakesh); // Mark it as faked.
+      } else {
+        // Special case. There exists already a degenerated face [a,b,p]!
+        //   There is no need to create a faked subface here.
+        senext2self(neighsh); // [a,b,p]
+        // Since we will re-connect the face ring using the faked subfaces.
+        //   We put the adjacent face of [a,b,p] to the list.
+        spivot(neighsh, startsh); // The original adjacent subface.
+        if (sorg(startsh) != pa) sesymself(startsh);
+        sdissolve(startsh);
+        // Connect fakesh to the segment [a,b].
+        ssbond(startsh, abseg);
+        fakesh = startsh; // Do not mark it!
+        // Delete the degenerated subface.
+        shellfacedealloc(subfaces, neighsh.sh);
+      }
+      // Save the fakesh in list (for re-creating the face ring).
+      cavesegshlist->newindex((void **) &parysh);
+      *parysh = fakesh;
+    } // i
+    caveshlist->restart();
+
+    // Re-create the face ring.
+    if (cavesegshlist->objects > 1) {
+      for (i = 0; i < cavesegshlist->objects; i++) {
+        parysh = (face *) fastlookup(cavesegshlist, i);
+        fakesh = *parysh;
+        // Get the next face in the ring.
+        j = (i + 1) % cavesegshlist->objects;
+        parysh = (face *) fastlookup(cavesegshlist, j);
+        nextsh = *parysh;
+        sbond1(fakesh, nextsh);
+      }
+    }
+
+    // Delete the two subsegments containing p.
+    shellfacedealloc(subsegs, parentseg->sh);
+    shellfacedealloc(subsegs, prevseg.sh);
+    // Return the new segment.
+    *parentseg = abseg;
+  } else {
+    // p is inside the surface.
+    if (b->verbose > 2) {
+      printf("      Remove vertex %d from surface.\n", pointmark(delpt));
+    }
+    // Let 'delpt' be its apex.
+    senextself(*parentsh);
+    // For unifying the code, we add parentsh to list.
+    cavesegshlist->newindex((void **) &parysh);
+    *parysh = *parentsh;
+  }
+
+  // Remove the point (p).
+
+  for (it = 0; it < cavesegshlist->objects; it++) {
+    parentsh = (face *) fastlookup(cavesegshlist, it); // [a,b,p]
+    senextself(*parentsh); // [b,p,a].
+    spivotself(*parentsh);
+    if (sorg(*parentsh) != delpt) sesymself(*parentsh);
+    // now parentsh is [p,b,#].
+    if (sorg(*parentsh) != delpt) {
+      // The vertex has already been removed in above special case.
+      continue;
+    }
+
+    while (1) {      
+      // Initialize the flip edge list. Re-use 'caveshlist'.
+      spinsh = *parentsh; // [p, b, #]
+      while (1) {
+        caveshlist->newindex((void **) &parysh);
+        *parysh = spinsh;
+        senext2self(spinsh);
+        spivotself(spinsh);
+        if (spinsh.sh == parentsh->sh) break;
+        if (sorg(spinsh) != delpt) sesymself(spinsh);
+      } // while (1)
+
+      if (caveshlist->objects == 3) {
+        // Delete the point by a 3-to-1 flip.
+        for (i = 0; i < 3; i++) {
+          parysh = (face *) fastlookup(caveshlist, i);
+          flipfaces[i] = *parysh;
+        }
+        flip31(flipfaces, lawson);
+        for (i = 0; i < 3; i++) { 
+          shellfacedealloc(subfaces, flipfaces[i].sh);
+        }
+        caveshlist->restart();
+        // Save the new subface.
+        caveshbdlist->newindex((void **) &parysh);
+        *parysh = flipfaces[3];
+        // The vertex is removed.
+        break;
+      }
+
+      // Search an edge to flip.
+      for (i = 0; i < caveshlist->objects; i++) {
+        parysh = (face *) fastlookup(caveshlist, i);
+        flipfaces[0] = *parysh;
+        spivot(flipfaces[0], flipfaces[1]);
+        if (sorg(flipfaces[0]) != sdest(flipfaces[1])) 
+          sesymself(flipfaces[1]);
+        // Skip this edge if it belongs to a faked subface.
+        if (!smarktested(flipfaces[0]) && !smarktested(flipfaces[1])) {
+          pa = sorg(flipfaces[0]);
+          pb = sdest(flipfaces[0]);
+          pc = sapex(flipfaces[0]);
+          pd = sapex(flipfaces[1]);
+          calculateabovepoint4(pa, pb, pc, pd);
+          // Check if a 2-to-2 flip is possible.
+          ori1 = orient3d(pc, pd, dummypoint, pa);
+          ori2 = orient3d(pc, pd, dummypoint, pb);
+          if (ori1 * ori2 < 0) {
+            // A 2-to-2 flip is found.
+            flip22(flipfaces, lawson, 0);
+            // The i-th edge is flipped. The i-th and (i-1)-th subfaces are
+            //   changed. The 'flipfaces[1]' contains p as its apex.
+            senext2(flipfaces[1], *parentsh);
+            // Save the new subface.
+            caveshbdlist->newindex((void **) &parysh);
+            *parysh = flipfaces[0];
+            break;
+          }
+        } //
+      } // i
+
+      if (i == caveshlist->objects) {
+        // Do a flip22 and a flip31 to remove p.
+        parysh = (face *) fastlookup(caveshlist, 0);
+        flipfaces[0] = *parysh;
+        spivot(flipfaces[0], flipfaces[1]);
+        if (sorg(flipfaces[0]) != sdest(flipfaces[1])) {
+          sesymself(flipfaces[1]);
+        }
+        flip22(flipfaces, lawson, 0);
+        senext2(flipfaces[1], *parentsh);
+        // Save the new subface.
+        caveshbdlist->newindex((void **) &parysh);
+        *parysh = flipfaces[0];
+      }
+
+      // The edge list at p are changed.
+      caveshlist->restart();
+    } // while (1)
+
+  } // it
+
+  cavesegshlist->restart();
+
+  if (b->verbose > 2) {
+    printf("      Created %ld new subfaces.\n", caveshbdlist->objects);
+  }
+
+
+  if (lawson) {
+    lawsonflip();
+  }
+
+  return 0;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// slocate()    Locate a point in a surface triangulation.                   //
+//                                                                           //
+// Staring the search from 'searchsh'(it should not be NULL). Perform a line //
+// walk search for a subface containing the point (p).                       //
+//                                                                           //
+// If 'aflag' is set, the 'dummypoint' is pre-calculated so that it lies     //
+// above the 'searchsh' in its current orientation. The test if c is CCW to  //
+// the line a->b can be done by the test if c is below the oriented plane    //
+// a->b->dummypoint.                                                         //
+//                                                                           //
+// If 'cflag' is not TRUE, the triangulation may not be convex.  Stop search //
+// when a segment is met and return OUTSIDE.                                 //
+//                                                                           //
+// If 'rflag' (rounding) is set, after the location of the point is found,   //
+// either ONEDGE or ONFACE, round the result using an epsilon.               //
+//                                                                           //
+// The returned value indicates the following cases:                         //
+//   - ONVERTEX, p is the origin of 'searchsh'.                              //
+//   - ONEDGE, p lies on the edge of 'searchsh'.                             //
+//   - ONFACE, p lies in the interior of 'searchsh'.                         //
+//   - OUTSIDE, p lies outside of the triangulation, p is on the left-hand   //
+//     side of the edge 'searchsh'(s), i.e., org(s), dest(s), p are CW.      //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+enum tetgenmesh::locateresult tetgenmesh::slocate(point searchpt, 
+  face* searchsh, int aflag, int cflag, int rflag)
+{
+  face neighsh;
+  point pa, pb, pc;
+  enum locateresult loc;
+  enum {MOVE_BC, MOVE_CA} nextmove;
+  REAL ori, ori_bc, ori_ca;
+  int i;
+
+  pa = sorg(*searchsh);
+  pb = sdest(*searchsh);
+  pc = sapex(*searchsh);
+
+  if (!aflag) {
+    // No above point is given. Calculate an above point for this facet.
+    calculateabovepoint4(pa, pb, pc, searchpt);
+  }
+
+  // 'dummypoint' is given. Make sure it is above [a,b,c]
+  ori = orient3d(pa, pb, pc, dummypoint);
+  if (ori > 0) {
+    sesymself(*searchsh); // Reverse the face orientation.
+  } else if (ori == 0.0) {
+    // This case should not happen theoretically. But... 
+    return UNKNOWN; 
+  }
+
+  // Find an edge of the face s.t. p lies on its right-hand side (CCW).
+  for (i = 0; i < 3; i++) {
+    pa = sorg(*searchsh);
+    pb = sdest(*searchsh);
+    ori = orient3d(pa, pb, dummypoint, searchpt);
+    if (ori > 0) break;
+    senextself(*searchsh);
+  }
+  if (i == 3) {
+    return UNKNOWN;
+  }
+
+  pc = sapex(*searchsh);
+
+  if (pc == searchpt) {
+    senext2self(*searchsh);
+    return ONVERTEX;
+  }
+
+  while (1) {
+
+    ori_bc = orient3d(pb, pc, dummypoint, searchpt);
+    ori_ca = orient3d(pc, pa, dummypoint, searchpt);
+
+    if (ori_bc < 0) {
+      if (ori_ca < 0) { // (--)
+        // Any of the edges is a viable move.
+        if (randomnation(2)) {
+          nextmove = MOVE_CA;
+        } else {
+          nextmove = MOVE_BC;
+        }
+      } else { // (-#)
+        // Edge [b, c] is viable.
+        nextmove = MOVE_BC;
+      }
+    } else {
+      if (ori_ca < 0) { // (#-)
+        // Edge [c, a] is viable.
+        nextmove = MOVE_CA;
+      } else {
+        if (ori_bc > 0) {
+          if (ori_ca > 0) { // (++)
+            loc = ONFACE;  // Inside [a, b, c].
+            break;
+          } else { // (+0)
+            senext2self(*searchsh); // On edge [c, a].
+            loc = ONEDGE;
+            break;
+          }
+        } else { // ori_bc == 0
+          if (ori_ca > 0) { // (0+)
+            senextself(*searchsh); // On edge [b, c].
+            loc = ONEDGE;
+            break;
+          } else { // (00)
+            // p is coincident with vertex c. 
+            senext2self(*searchsh);
+            return ONVERTEX;
+          }
+        }
+      }
+    }
+
+    // Move to the next face.
+    if (nextmove == MOVE_BC) {
+      senextself(*searchsh);
+    } else {
+      senext2self(*searchsh);
+    }
+    if (!cflag) {
+      // NON-convex case. Check if we will cross a boundary.
+      if (isshsubseg(*searchsh)) {
+        return ENCSEGMENT;
+      }
+    }
+    spivot(*searchsh, neighsh);
+    if (neighsh.sh == NULL) {
+      return OUTSIDE; // A hull edge.
+    }
+    // Adjust the edge orientation.
+    if (sorg(neighsh) != sdest(*searchsh)) {
+      sesymself(neighsh);
+    }
+
+    // Update the newly discovered face and its endpoints.
+    *searchsh = neighsh;
+    pa = sorg(*searchsh);
+    pb = sdest(*searchsh);
+    pc = sapex(*searchsh);
+
+    if (pc == searchpt) {
+      senext2self(*searchsh);
+      return ONVERTEX;
+    }
+
+  } // while (1)
+
+  // assert(loc == ONFACE || loc == ONEDGE);
+
+
+  if (rflag) {
+    // Round the locate result before return.
+    REAL n[3], area_abc, area_abp, area_bcp, area_cap;
+
+    pa = sorg(*searchsh);
+    pb = sdest(*searchsh);
+    pc = sapex(*searchsh);
+
+    facenormal(pa, pb, pc, n, 1, NULL);
+    area_abc = sqrt(dot(n, n));
+
+    facenormal(pb, pc, searchpt, n, 1, NULL);
+    area_bcp = sqrt(dot(n, n));
+    if ((area_bcp / area_abc) < b->epsilon) {
+      area_bcp = 0; // Rounding.
+    }
+
+    facenormal(pc, pa, searchpt, n, 1, NULL);
+    area_cap = sqrt(dot(n, n));
+    if ((area_cap / area_abc) < b->epsilon) {
+      area_cap = 0; // Rounding
+    }
+
+    if ((loc == ONFACE) || (loc == OUTSIDE)) {
+      facenormal(pa, pb, searchpt, n, 1, NULL);
+      area_abp = sqrt(dot(n, n));
+      if ((area_abp / area_abc) < b->epsilon) {
+        area_abp = 0; // Rounding
+      }
+    } else { // loc == ONEDGE
+      area_abp = 0;
+    }
+
+    if (area_abp == 0) {
+      if (area_bcp == 0) {
+        senextself(*searchsh); 
+        loc = ONVERTEX; // p is close to b.
+      } else {
+        if (area_cap == 0) {
+          loc = ONVERTEX; // p is close to a.
+        } else {
+          loc = ONEDGE; // p is on edge [a,b].
+        }
+      }
+    } else if (area_bcp == 0) {
+      if (area_cap == 0) {
+        senext2self(*searchsh); 
+        loc = ONVERTEX; // p is close to c.
+      } else {
+        senextself(*searchsh);
+        loc = ONEDGE; // p is on edge [b,c].
+      }
+    } else if (area_cap == 0) {
+      senext2self(*searchsh);
+      loc = ONEDGE; // p is on edge [c,a].
+    } else {
+      loc = ONFACE; // p is on face [a,b,c].
+    }
+  } // if (rflag)
+
+  return loc;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// sscoutsegment()    Look for a segment in the surface triangulation.       //
+//                                                                           //
+// The segment is given by the origin of 'searchsh' and 'endpt'.             //
+//                                                                           //
+// If an edge in T is found matching this segment, the segment is "locked"   //
+// in T at the edge.  Otherwise, flip the first edge in T that the segment   //
+// crosses. Continue the search from the flipped face.                       //
+//                                                                           //
+// This routine uses 'orisent3d' to determine the search direction. It uses  //
+// 'dummypoint' as the 'lifted point' in 3d, and it assumes that it (dummy-  //
+// point) lies above the 'searchsh' (w.r.t the Right-hand rule).             //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+enum tetgenmesh::interresult tetgenmesh::sscoutsegment(face *searchsh, 
+  point endpt, int insertsegflag, int reporterrorflag, int chkencflag)
+{
+  face flipshs[2], neighsh;
+  point startpt, pa, pb, pc, pd;
+  enum interresult dir;
+  enum {MOVE_AB, MOVE_CA} nextmove;
+  REAL ori_ab, ori_ca, len;
+
+  // The origin of 'searchsh' is fixed.
+  startpt = sorg(*searchsh); 
+  nextmove = MOVE_AB; // Avoid compiler warning.
+
+  if (b->verbose > 2) {
+    printf("      Scout segment (%d, %d).\n", pointmark(startpt),
+           pointmark(endpt));
+  }
+  len = distance(startpt, endpt);
+
+  // Search an edge in 'searchsh' on the path of this segment.
+  while (1) {
+
+    pb = sdest(*searchsh);
+    if (pb == endpt) {
+      dir = SHAREEDGE; // Found!
+      break;
+    }
+
+    pc = sapex(*searchsh);
+    if (pc == endpt) {
+      senext2self(*searchsh);
+      sesymself(*searchsh);
+      dir = SHAREEDGE; // Found!
+      break;
+    }
+
+
+    // Round the results.
+    if ((sqrt(triarea(startpt, pb, endpt)) / len) < b->epsilon) {
+      ori_ab = 0.0;
+    } else {
+      ori_ab = orient3d(startpt, pb, dummypoint, endpt);
+    }
+    if ((sqrt(triarea(pc, startpt, endpt)) / len) < b->epsilon) {
+      ori_ca = 0.0;
+    } else {
+      ori_ca = orient3d(pc, startpt, dummypoint, endpt);
+    }
+
+    if (ori_ab < 0) {
+      if (ori_ca < 0) { // (--)
+        // Both sides are viable moves.
+        if (randomnation(2)) {
+          nextmove = MOVE_CA;
+        } else {
+          nextmove = MOVE_AB;
+        }
+      } else { // (-#)
+        nextmove = MOVE_AB;
+      }
+    } else {
+      if (ori_ca < 0) { // (#-)
+        nextmove = MOVE_CA;
+      } else {
+        if (ori_ab > 0) {
+          if (ori_ca > 0) { // (++)
+            // The segment intersects with edge [b, c].
+            dir = ACROSSEDGE;
+            break;
+          } else { // (+0)
+            // The segment collinear with edge [c, a].
+            senext2self(*searchsh);
+            sesymself(*searchsh); 
+            dir = ACROSSVERT;
+            break;
+          }
+        } else {
+          if (ori_ca > 0) { // (0+)
+            // The segment is collinear with edge [a, b].
+            dir = ACROSSVERT;
+            break;
+          } else { // (00)
+            // startpt == endpt. Not possible.
+            terminatetetgen(this, 2);
+          }
+        }
+      }
+    }
+
+    // Move 'searchsh' to the next face, keep the origin unchanged.
+    if (nextmove == MOVE_AB) {
+      if (chkencflag) {
+        // Do not cross boundary.
+        if (isshsubseg(*searchsh)) {
+          return ACROSSEDGE; // ACROSS_SEG
+        }
+      }
+      spivot(*searchsh, neighsh);
+      if (neighsh.sh != NULL) {
+        if (sorg(neighsh) != pb) sesymself(neighsh);
+        senext(neighsh, *searchsh);
+      } else {
+        // This side (startpt->pb) is outside. It is caused by rounding error.
+        // Try the next side, i.e., (pc->startpt).
+        senext2(*searchsh, neighsh);
+        if (chkencflag) {
+          // Do not cross boundary.
+          if (isshsubseg(neighsh)) {
+            *searchsh = neighsh;
+            return ACROSSEDGE; // ACROSS_SEG
+          }
+        }
+        spivotself(neighsh);
+        if (sdest(neighsh) != pc) sesymself(neighsh);
+        *searchsh = neighsh;
+      }
+    } else { // MOVE_CA
+      senext2(*searchsh, neighsh);
+      if (chkencflag) {
+        // Do not cross boundary.
+        if (isshsubseg(neighsh)) {
+          *searchsh = neighsh;
+          return ACROSSEDGE; // ACROSS_SEG
+        }
+      }
+      spivotself(neighsh);
+      if (neighsh.sh != NULL) {
+        if (sdest(neighsh) != pc) sesymself(neighsh);
+        *searchsh = neighsh;
+      } else {
+        // The same reason as above. 
+        // Try the next side, i.e., (startpt->pb).
+        if (chkencflag) {
+          // Do not cross boundary.
+          if (isshsubseg(*searchsh)) {
+            return ACROSSEDGE; // ACROSS_SEG
+          }
+        }
+        spivot(*searchsh, neighsh);
+        if (sorg(neighsh) != pb) sesymself(neighsh);
+        senext(neighsh, *searchsh);
+      }
+    }
+  } // while
+
+  if (dir == SHAREEDGE) {
+    if (insertsegflag) {
+      // Insert the segment into the triangulation.
+      face newseg;
+      makeshellface(subsegs, &newseg);
+      setshvertices(newseg, startpt, endpt, NULL);
+      // Set the default segment marker.
+      setshellmark(newseg, -1);
+      ssbond(*searchsh, newseg);
+      spivot(*searchsh, neighsh);
+      if (neighsh.sh != NULL) {
+        ssbond(neighsh, newseg);
+      }
+    }
+    return dir;
+  }
+
+  if (dir == ACROSSVERT) {
+    // A point is found collinear with this segment.
+    if (reporterrorflag) {
+      point pp = sdest(*searchsh);
+      printf("PLC Error:  A vertex lies in a segment in facet #%d.\n",
+             shellmark(*searchsh));
+      printf("  Vertex:  [%d] (%g,%g,%g).\n",pointmark(pp),pp[0],pp[1],pp[2]);
+      printf("  Segment: [%d, %d]\n", pointmark(startpt), pointmark(endpt));
+    }
+    return dir;
+  }
+
+  if (dir == ACROSSEDGE) {
+    // Edge [b, c] intersects with the segment.
+    senext(*searchsh, flipshs[0]);
+    if (isshsubseg(flipshs[0])) {
+      if (reporterrorflag) {
+        REAL P[3], Q[3], tp = 0, tq = 0;
+        linelineint(startpt, endpt, pb, pc, P, Q, &tp, &tq);
+        printf("PLC Error:  Two segments intersect at point (%g,%g,%g),", 
+               P[0], P[1], P[2]);
+        printf(" in facet #%d.\n", shellmark(*searchsh));
+        printf("  Segment 1: [%d, %d]\n", pointmark(pb), pointmark(pc));
+        printf("  Segment 2: [%d, %d]\n", pointmark(startpt),pointmark(endpt));
+      }
+      return dir; // ACROSS_SEG
+    }
+    // Flip edge [b, c], queue unflipped edges (for Delaunay checks).
+    spivot(flipshs[0], flipshs[1]);
+    if (sorg(flipshs[1]) != sdest(flipshs[0])) sesymself(flipshs[1]);
+    flip22(flipshs, 1, 0);
+    // The flip may create an inverted triangle, check it.
+    pa = sapex(flipshs[1]);
+    pb = sapex(flipshs[0]);
+    pc = sorg(flipshs[0]);
+    pd = sdest(flipshs[0]);
+    // Check if pa and pb are on the different sides of [pc, pd]. 
+    // Re-use ori_ab, ori_ca for the tests.
+    ori_ab = orient3d(pc, pd, dummypoint, pb);
+    ori_ca = orient3d(pd, pc, dummypoint, pa);
+    if (ori_ab <= 0) {
+      flipshpush(&(flipshs[0])); 
+    } else if (ori_ca <= 0) {
+      flipshpush(&(flipshs[1])); 
+    }
+    // Set 'searchsh' s.t. its origin is 'startpt'.
+    *searchsh = flipshs[0];
+  }
+
+  return sscoutsegment(searchsh, endpt, insertsegflag, reporterrorflag, 
+                       chkencflag);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// scarveholes()    Remove triangles not in the facet.                       //
+//                                                                           //
+// This routine re-uses the two global arrays: caveshlist and caveshbdlist.  //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::scarveholes(int holes, REAL* holelist)
+{
+  face *parysh, searchsh, neighsh;
+  enum locateresult loc;
+  int i, j;
+
+  // Get all triangles. Infect unprotected convex hull triangles. 
+  smarktest(recentsh);
+  caveshlist->newindex((void **) &parysh);
+  *parysh = recentsh;
+  for (i = 0; i < caveshlist->objects; i++) {
+    parysh = (face *) fastlookup(caveshlist, i);
+    searchsh = *parysh;
+    searchsh.shver = 0;
+    for (j = 0; j < 3; j++) {
+      spivot(searchsh, neighsh);
+      // Is this side on the convex hull?
+      if (neighsh.sh != NULL) {
+        if (!smarktested(neighsh)) {
+          smarktest(neighsh);
+          caveshlist->newindex((void **) &parysh);
+          *parysh = neighsh;
+        }
+      } else {
+        // A hull side. Check if it is protected by a segment.
+        if (!isshsubseg(searchsh)) {
+          // Not protected. Save this face.
+          if (!sinfected(searchsh)) {
+            sinfect(searchsh);
+            caveshbdlist->newindex((void **) &parysh);
+            *parysh = searchsh;
+          }
+        }
+      }
+      senextself(searchsh);
+    }
+  }
+
+  // Infect the triangles in the holes.
+  for (i = 0; i < 3 * holes; i += 3) {
+    searchsh = recentsh;
+    loc = slocate(&(holelist[i]), &searchsh, 1, 1, 0);
+    if (loc != OUTSIDE) {
+      sinfect(searchsh);
+      caveshbdlist->newindex((void **) &parysh);
+      *parysh = searchsh;
+    }
+  }
+
+  // Find and infect all exterior triangles.
+  for (i = 0; i < caveshbdlist->objects; i++) {
+    parysh = (face *) fastlookup(caveshbdlist, i);
+    searchsh = *parysh;
+    searchsh.shver = 0;
+    for (j = 0; j < 3; j++) {
+      spivot(searchsh, neighsh);
+      if (neighsh.sh != NULL) {
+        if (!isshsubseg(searchsh)) {
+          if (!sinfected(neighsh)) {
+            sinfect(neighsh);
+            caveshbdlist->newindex((void **) &parysh);
+            *parysh = neighsh;
+          }
+        } else {
+          sdissolve(neighsh); // Disconnect a protected face.
+        }
+      }
+      senextself(searchsh);
+    }
+  }
+
+  // Delete exterior triangles, unmark interior triangles.
+  for (i = 0; i < caveshlist->objects; i++) {
+    parysh = (face *) fastlookup(caveshlist, i);
+    if (sinfected(*parysh)) {
+      shellfacedealloc(subfaces, parysh->sh);
+    } else {
+      sunmarktest(*parysh);
+    }
+  }
+
+  caveshlist->restart();
+  caveshbdlist->restart();
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// unifysegments()    Remove redundant segments and create face links.       //
+//                                                                           //
+// After this routine, although segments are unique, but some of them may be //
+// removed later by mergefacet().  All vertices still have type FACETVERTEX. //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::unifysegments()
+{
+  badface *facelink = NULL, *newlinkitem, *f1, *f2;
+  face *facperverlist, sface;
+  face subsegloop, testseg;
+  point torg, tdest;
+  REAL ori1, ori2, ori3;
+  REAL n1[3], n2[3];
+  REAL cosang, ang, ang_tol;
+  int *idx2faclist;
+  int idx, k, m;
+
+  if (b->verbose > 1) {
+    printf("  Unifying segments.\n");
+  }
+  // The limit dihedral angle that two facets are not overlapping.
+  ang_tol = b->facet_overlap_ang_tol / 180.0 * PI;
+  if (ang_tol < 0.0) ang_tol = 0.0;
+
+  // Create a mapping from vertices to subfaces.
+  makepoint2submap(subfaces, idx2faclist, facperverlist);
+
+
+  subsegloop.shver = 0;
+  subsegs->traversalinit();
+  subsegloop.sh = shellfacetraverse(subsegs);
+  while (subsegloop.sh != (shellface *) NULL) {
+    torg = sorg(subsegloop);
+    tdest = sdest(subsegloop);
+
+    idx = pointmark(torg) - in->firstnumber;
+    // Loop through the set of subfaces containing 'torg'.  Get all the
+    //   subfaces containing the edge (torg, tdest). Save and order them
+    //   in 'sfacelist', the ordering is defined by the right-hand rule
+    //   with thumb points from torg to tdest.
+    for (k = idx2faclist[idx]; k < idx2faclist[idx + 1]; k++) {
+      sface = facperverlist[k];
+      // The face may be deleted if it is a duplicated face.
+      if (sface.sh[3] == NULL) continue;
+      // Search the edge torg->tdest.
+      if (sdest(sface) != tdest) {
+        senext2self(sface);
+        sesymself(sface);
+      }
+      if (sdest(sface) != tdest) continue;
+
+      // Save the face f in facelink.
+      if (flippool->items >= 2) {
+        f1 = facelink;
+        for (m = 0; m < flippool->items - 1; m++) {
+          f2 = f1->nextitem;
+          ori1 = orient3d(torg, tdest, sapex(f1->ss), sapex(f2->ss));
+          ori2 = orient3d(torg, tdest, sapex(f1->ss), sapex(sface));
+          if (ori1 > 0) {
+            // apex(f2) is below f1.
+            if (ori2 > 0) {
+              // apex(f) is below f1 (see Fig.1). 
+              ori3 = orient3d(torg, tdest, sapex(f2->ss), sapex(sface));
+              if (ori3 > 0) {
+                // apex(f) is below f2, insert it.
+                break; 
+              } else if (ori3 < 0) {
+                // apex(f) is above f2, continue.
+              } else { // ori3 == 0; 
+                // f is coplanar and codirection with f2.
+                report_overlapping_facets(&(f2->ss), &sface);
+                break;
+              }
+            } else if (ori2 < 0) {
+              // apex(f) is above f1 below f2, inset it (see Fig. 2).
+              break;
+            } else { // ori2 == 0;
+              // apex(f) is coplanar with f1 (see Fig. 5).
+              ori3 = orient3d(torg, tdest, sapex(f2->ss), sapex(sface));
+              if (ori3 > 0) {
+                // apex(f) is below f2, insert it.
+                break; 
+              } else {
+                // f is coplanar and codirection with f1.
+                report_overlapping_facets(&(f1->ss), &sface);
+                break;
+              }
+            }
+          } else if (ori1 < 0) {
+            // apex(f2) is above f1.
+            if (ori2 > 0) {
+              // apex(f) is below f1, continue (see Fig. 3).
+            } else if (ori2 < 0) {
+              // apex(f) is above f1 (see Fig.4).
+              ori3 = orient3d(torg, tdest, sapex(f2->ss), sapex(sface));
+              if (ori3 > 0) {
+                // apex(f) is below f2, insert it.
+                break;
+              } else if (ori3 < 0) {
+                // apex(f) is above f2, continue.
+              } else { // ori3 == 0;
+                // f is coplanar and codirection with f2.
+                report_overlapping_facets(&(f2->ss), &sface);
+                break;
+              }
+            } else { // ori2 == 0;
+              // f is coplanar and with f1 (see Fig. 6).
+              ori3 = orient3d(torg, tdest, sapex(f2->ss), sapex(sface));
+              if (ori3 > 0) {
+                // f is also codirection with f1.
+                report_overlapping_facets(&(f1->ss), &sface);
+                break;
+              } else {
+                // f is above f2, continue.
+              }
+            }
+          } else { // ori1 == 0;
+            // apex(f2) is coplanar with f1. By assumption, f1 is not
+            //   coplanar and codirection with f2.
+            if (ori2 > 0) {
+              // apex(f) is below f1, continue (see Fig. 7).
+            } else if (ori2 < 0) {
+              // apex(f) is above f1, insert it (see Fig. 7).
+              break;
+            } else { // ori2 == 0.
+              // apex(f) is coplanar with f1 (see Fig. 8).
+              // f is either codirection with f1 or is codirection with f2. 
+              facenormal(torg, tdest, sapex(f1->ss), n1, 1, NULL);
+              facenormal(torg, tdest, sapex(sface), n2, 1, NULL);
+              if (dot(n1, n2) > 0) {
+                report_overlapping_facets(&(f1->ss), &sface);
+              } else {
+                report_overlapping_facets(&(f2->ss), &sface);
+              }
+              break;
+            }
+          }
+          // Go to the next item;
+          f1 = f2;
+        } // for (m = 0; ...)
+        if (sface.sh[3] != NULL) {
+          // Insert sface between f1 and f2.
+          newlinkitem = (badface *) flippool->alloc();
+          newlinkitem->ss = sface;
+          newlinkitem->nextitem = f1->nextitem;
+          f1->nextitem = newlinkitem;
+        }
+      } else if (flippool->items == 1) {
+        f1 = facelink;
+        // Make sure that f is not coplanar and codirection with f1.
+        ori1 = orient3d(torg, tdest, sapex(f1->ss), sapex(sface));
+        if (ori1 == 0) {
+          // f is coplanar with f1 (see Fig. 8).
+          facenormal(torg, tdest, sapex(f1->ss), n1, 1, NULL);
+          facenormal(torg, tdest, sapex(sface), n2, 1, NULL);
+          if (dot(n1, n2) > 0) {
+            // The two faces are codirectional as well.
+            report_overlapping_facets(&(f1->ss), &sface);
+          }
+        }
+        // Add this face to link if it is not deleted.
+        if (sface.sh[3] != NULL) {
+          // Add this face into link.
+          newlinkitem = (badface *) flippool->alloc();
+          newlinkitem->ss = sface;
+          newlinkitem->nextitem = NULL;
+          f1->nextitem = newlinkitem;
+        }
+      } else {
+        // The first face.
+        newlinkitem = (badface *) flippool->alloc();
+        newlinkitem->ss = sface;
+        newlinkitem->nextitem = NULL;
+        facelink = newlinkitem;
+      }
+    } // for (k = idx2faclist[idx]; ...)
+
+
+    // Set the connection between this segment and faces containing it,
+    //   at the same time, remove redundant segments.
+    f1 = facelink;
+    for (k = 0; k < flippool->items; k++) {
+      sspivot(f1->ss, testseg);
+      // If 'testseg' is not 'subsegloop' and is not dead, it is redundant.
+      if ((testseg.sh != subsegloop.sh) && (testseg.sh[3] != NULL)) {
+        shellfacedealloc(subsegs, testseg.sh);
+      }
+      // Bonds the subface and the segment together.
+      ssbond(f1->ss, subsegloop);
+      f1 = f1->nextitem;
+    }
+
+    // Create the face ring at the segment.
+    if (flippool->items > 1) {
+      f1 = facelink;
+      for (k = 1; k <= flippool->items; k++) {
+        k < flippool->items ? f2 = f1->nextitem : f2 = facelink;
+        // Calculate the dihedral angle between the two facet.
+        facenormal(torg, tdest, sapex(f1->ss), n1, 1, NULL);
+        facenormal(torg, tdest, sapex(f2->ss), n2, 1, NULL);
+        cosang = dot(n1, n2) / (sqrt(dot(n1, n1)) * sqrt(dot(n2, n2)));
+        // Rounding.
+        if (cosang > 1.0) cosang = 1.0;
+        else if (cosang < -1.0) cosang = -1.0;
+        ang = acos(cosang);
+        if (ang < ang_tol) {
+          // Two facets are treated as overlapping each other.
+          report_overlapping_facets(&(f1->ss), &(f2->ss), ang);
+        } else {
+          // Record the smallest input dihedral angle.
+          if (ang < minfacetdihed) {
+            minfacetdihed = ang;
+          }
+          sbond1(f1->ss, f2->ss);
+        }
+        f1 = f2;
+      }
+    }
+
+    flippool->restart();
+
+    // Are there length constraints?
+    if (b->quality && (in->segmentconstraintlist != (REAL *) NULL)) {
+      int e1, e2;
+      REAL len;
+      for (k = 0; k < in->numberofsegmentconstraints; k++) {
+        e1 = (int) in->segmentconstraintlist[k * 3];
+        e2 = (int) in->segmentconstraintlist[k * 3 + 1];
+        if (((pointmark(torg) == e1) && (pointmark(tdest) == e2)) ||
+            ((pointmark(torg) == e2) && (pointmark(tdest) == e1))) {
+          len = in->segmentconstraintlist[k * 3 + 2];
+          setareabound(subsegloop, len);
+          break;
+        }
+      }
+    }
+
+    subsegloop.sh = shellfacetraverse(subsegs);
+  }
+
+  delete [] idx2faclist;
+  delete [] facperverlist;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// identifyinputedges()    Identify input edges.                             //
+//                                                                           //
+// A set of input edges is provided in the 'in->edgelist'.  We find these    //
+// edges in the surface mesh and make them segments of the mesh.             //
+//                                                                           //
+// It is possible that an input edge is not in any facet, i.e.,it is a float-//
+// segment inside the volume.                                                //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::identifyinputedges(point *idx2verlist)
+{
+  face* shperverlist;
+  int* idx2shlist;
+  face searchsh, neighsh;
+  face segloop, checkseg, newseg;
+  point checkpt, pa = NULL, pb = NULL;
+  int *endpts;
+  int edgemarker;
+  int idx, i, j;
+
+  int e1, e2;
+  REAL len;
+
+  if (!b->quiet) {
+    printf("Inserting edges ...\n");
+  }
+
+  // Construct a map from points to subfaces.
+  makepoint2submap(subfaces, idx2shlist, shperverlist);
+
+  // Process the set of input edges.
+  for (i = 0; i < in->numberofedges; i++) {
+    endpts = &(in->edgelist[(i << 1)]);
+    if (endpts[0] == endpts[1]) {
+      if (!b->quiet) {
+        printf("Warning:  Edge #%d is degenerated. Skipped.\n", i);
+      }
+      continue; // Skip a degenerated edge.
+    }
+    // Recall that all existing segments have a default marker '-1'. 
+    // We assign all identified segments a default marker '-2'.
+    edgemarker = in->edgemarkerlist ? in->edgemarkerlist[i] : -2;
+
+    // Find a face contains the edge.
+    newseg.sh = NULL;
+    searchsh.sh = NULL;
+    idx = endpts[0] - in->firstnumber;
+    for (j = idx2shlist[idx]; j < idx2shlist[idx + 1]; j++) {
+      checkpt = sdest(shperverlist[j]);
+      if (pointmark(checkpt) == endpts[1]) {
+        searchsh = shperverlist[j];
+        break; // Found.
+      } else {
+        checkpt = sapex(shperverlist[j]);
+        if (pointmark(checkpt) == endpts[1]) {
+          senext2(shperverlist[j], searchsh);
+          sesymself(searchsh);
+          break;
+        }
+      }
+    } // j
+
+    if (searchsh.sh != NULL) {
+      // Check if this edge is already a segment of the mesh.
+      sspivot(searchsh, checkseg);
+      if (checkseg.sh != NULL) {
+        // This segment already exist.
+        newseg = checkseg;
+      } else {
+        // Create a new segment at this edge.
+        pa = sorg(searchsh);
+        pb = sdest(searchsh);
+        makeshellface(subsegs, &newseg);
+        setshvertices(newseg, pa, pb, NULL);
+        ssbond(searchsh, newseg);
+        spivot(searchsh, neighsh);
+        if (neighsh.sh != NULL) {
+          ssbond(neighsh, newseg);
+        }
+      }
+    } else {
+      // It is a dangling segment (not belong to any facets).
+      // Get the two endpoints of this segment.
+      pa = idx2verlist[endpts[0]];
+      pb = idx2verlist[endpts[1]];
+      if (pa == pb) {
+        if (!b->quiet) {
+          printf("Warning:  Edge #%d is degenerated. Skipped.\n", i);
+        }
+        continue;
+      }
+      // Check if segment [a,b] already exists.
+      // TODO: Change the brute-force search. Slow!
+      point *ppt;
+      subsegs->traversalinit();
+      segloop.sh = shellfacetraverse(subsegs);
+      while (segloop.sh != NULL) {
+        ppt = (point *) &(segloop.sh[3]);
+        if (((ppt[0] == pa) && (ppt[1] == pb)) ||
+            ((ppt[0] == pb) && (ppt[1] == pa))) {
+          // Found!
+          newseg = segloop; 
+          break;
+        }
+        segloop.sh = shellfacetraverse(subsegs);
+      }
+      if (newseg.sh == NULL) {
+        makeshellface(subsegs, &newseg);
+        setshvertices(newseg, pa, pb, NULL);
+      }
+    }
+
+    setshellmark(newseg, edgemarker);
+
+    if (b->quality && (in->segmentconstraintlist != (REAL *) NULL)) {
+      for (i = 0; i < in->numberofsegmentconstraints; i++) {
+        e1 = (int) in->segmentconstraintlist[i * 3];
+        e2 = (int) in->segmentconstraintlist[i * 3 + 1];
+        if (((pointmark(pa) == e1) && (pointmark(pb) == e2)) ||
+            ((pointmark(pa) == e2) && (pointmark(pb) == e1))) {
+          len = in->segmentconstraintlist[i * 3 + 2];
+          setareabound(newseg, len);
+          break;
+        }
+      }
+    }
+  } // i
+
+  delete [] shperverlist;
+  delete [] idx2shlist;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// mergefacets()    Merge adjacent facets.                                   //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::mergefacets()
+{
+  face parentsh, neighsh, neineish;
+  face segloop;
+  point pa, pb, pc, pd;
+  REAL n1[3], n2[3];
+  REAL cosang, cosang_tol;
+
+
+  // Allocate an array to save calcaulated dihedral angles at segments.
+  arraypool *dihedangarray = new arraypool(sizeof(double), 10);
+  REAL *paryang = NULL;
+
+  // First, remove coplanar segments.
+  // The dihedral angle bound for two different facets.
+  cosang_tol = cos(b->facet_separate_ang_tol / 180.0 * PI);
+
+  subsegs->traversalinit();
+  segloop.sh = shellfacetraverse(subsegs);
+  while (segloop.sh != (shellface *) NULL) {
+    // Only remove a segment if it has a marker '-1'.
+    if (shellmark(segloop) != -1) {
+      segloop.sh = shellfacetraverse(subsegs);
+      continue;
+    }
+    spivot(segloop, parentsh);
+    if (parentsh.sh != NULL) {
+      spivot(parentsh, neighsh);
+      if (neighsh.sh != NULL) {
+        spivot(neighsh, neineish);
+        if (neineish.sh == parentsh.sh) {
+          // Exactly two subfaces at this segment.
+          // Only merge them if they have the same boundary marker.
+          if (shellmark(parentsh) == shellmark(neighsh)) {
+            pa = sorg(segloop);
+            pb = sdest(segloop);
+            pc = sapex(parentsh);
+            pd = sapex(neighsh);
+            // Calculate the dihedral angle at the segment [a,b].
+            facenormal(pa, pb, pc, n1, 1, NULL);
+            facenormal(pa, pb, pd, n2, 1, NULL);
+            cosang = dot(n1, n2) / (sqrt(dot(n1, n1)) * sqrt(dot(n2, n2)));
+            if (cosang < cosang_tol) {
+              ssdissolve(parentsh);
+              ssdissolve(neighsh);
+              shellfacedealloc(subsegs, segloop.sh);
+              // Add the edge to flip stack.
+              flipshpush(&parentsh);
+            } else {
+              // Save 'cosang' to avoid re-calculate it.
+              // Re-use the pointer at the first segment.
+              dihedangarray->newindex((void **) &paryang);
+              *paryang = cosang;
+              segloop.sh[6] = (shellface) paryang;
+            }
+          } 
+        } // if (neineish.sh == parentsh.sh)
+      }
+    }
+    segloop.sh = shellfacetraverse(subsegs);
+  }
+
+  // Second, remove ridge segments at small angles.
+  // The dihedral angle bound for two different facets.
+  cosang_tol = cos(b->facet_small_ang_tol / 180.0 * PI);
+  REAL cosang_sep_tol = cos((b->facet_separate_ang_tol - 5.0) / 180.0 * PI);
+  face shloop;
+  face seg1, seg2;
+  REAL cosang1, cosang2;
+  int i, j;
+
+  subfaces->traversalinit();
+  shloop.sh = shellfacetraverse(subfaces);
+  while (shloop.sh != (shellface *) NULL) {
+    for (i = 0; i < 3; i++) {
+      if (isshsubseg(shloop)) {
+        senext(shloop, neighsh);
+        if (isshsubseg(neighsh)) {
+          // Found two segments sharing at one vertex.
+          // Check if they form a small angle.
+          pa = sorg(shloop);
+          pb = sdest(shloop);
+          pc = sapex(shloop);
+          for (j = 0; j < 3; j++) n1[j] = pa[j] - pb[j];
+          for (j = 0; j < 3; j++) n2[j] = pc[j] - pb[j];
+          cosang = dot(n1, n2) / (sqrt(dot(n1, n1)) * sqrt(dot(n2, n2)));
+          if (cosang > cosang_tol) {
+            // Found a small angle.
+            segloop.sh = NULL;
+            sspivot(shloop, seg1);
+            sspivot(neighsh, seg2);
+            if (seg1.sh[6] != NULL) {
+              paryang = (REAL *) (seg1.sh[6]);
+              cosang1 = *paryang;
+            } else {
+              cosang1 = 1.0; // 0 degree;
+            }
+            if (seg2.sh[6] != NULL) {
+              paryang = (REAL *) (seg2.sh[6]);
+              cosang2 = *paryang;
+            } else {
+              cosang2 = 1.0; // 0 degree;
+            }
+            if (cosang1 < cosang_sep_tol) {
+              if (cosang2 < cosang_sep_tol) {
+                if (cosang1 < cosang2) {
+                  segloop = seg1;
+                } else {
+                  segloop = seg2;
+                }
+              } else {
+                segloop = seg1;
+              }
+            } else {
+              if (cosang2 < cosang_sep_tol) {
+                segloop = seg2;
+              }
+            }
+            if (segloop.sh != NULL) {
+              // Remove this segment.
+              segloop.shver = 0;
+              spivot(segloop, parentsh);
+              spivot(parentsh, neighsh);
+              ssdissolve(parentsh);
+              ssdissolve(neighsh);
+              shellfacedealloc(subsegs, segloop.sh);
+              // Add the edge to flip stack.
+              flipshpush(&parentsh);
+              break;
+            }
+          }
+        } // if (isshsubseg)
+      } // if (isshsubseg)
+      senextself(shloop);
+    }
+    shloop.sh = shellfacetraverse(subfaces);
+  }
+
+  delete dihedangarray;
+
+  if (flipstack != NULL) {
+    lawsonflip(); // Recover Delaunayness.
+  }
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// finddirection()    Find the tet on the path from one point to another.    //
+//                                                                           //
+// The path starts from 'searchtet''s origin and ends at 'endpt'. On finish, //
+// 'searchtet' contains a tet on the path, its origin does not change.       //
+//                                                                           //
+// The return value indicates one of the following cases (let 'searchtet' be //
+// abcd, a is the origin of the path):                                       //
+//   - ACROSSVERT, edge ab is collinear with the path;                       //
+//   - ACROSSEDGE, edge bc intersects with the path;                         //
+//   - ACROSSFACE, face bcd intersects with the path.                        //
+//                                                                           //
+// WARNING: This routine is designed for convex triangulations, and will not //
+// generally work after the holes and concavities have been carved.          //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+enum tetgenmesh::interresult 
+  tetgenmesh::finddirection(triface* searchtet, point endpt)
+{
+  triface neightet;
+  point pa, pb, pc, pd;
+  enum {HMOVE, RMOVE, LMOVE} nextmove;
+  REAL hori, rori, lori;
+  int t1ver;
+  int s;
+
+  // The origin is fixed.
+  pa = org(*searchtet);
+  if ((point) searchtet->tet[7] == dummypoint) {
+    // A hull tet. Choose the neighbor of its base face.
+    decode(searchtet->tet[3], *searchtet);
+    // Reset the origin to be pa.
+    if ((point) searchtet->tet[4] == pa) {
+      searchtet->ver = 11;
+    } else if ((point) searchtet->tet[5] == pa) {
+      searchtet->ver = 3;
+    } else if ((point) searchtet->tet[6] == pa) {
+      searchtet->ver = 7;
+    } else {
+      searchtet->ver = 0;
+    }
+  }
+
+  pb = dest(*searchtet);
+  // Check whether the destination or apex is 'endpt'.
+  if (pb == endpt) {
+    // pa->pb is the search edge.
+    return ACROSSVERT;
+  }
+
+  pc = apex(*searchtet);
+  if (pc == endpt) {
+    // pa->pc is the search edge.
+    eprevesymself(*searchtet);
+    return ACROSSVERT;
+  }
+
+  // Walk through tets around pa until the right one is found.
+  while (1) {
+
+    pd = oppo(*searchtet);
+    // Check whether the opposite vertex is 'endpt'.
+    if (pd == endpt) {
+      // pa->pd is the search edge.
+      esymself(*searchtet);
+      enextself(*searchtet);
+      return ACROSSVERT;
+    }
+    // Check if we have entered outside of the domain.
+    if (pd == dummypoint) {
+      // This is possible when the mesh is non-convex.
+      if (nonconvex) {
+        return ACROSSFACE; // return ACROSSSUB; // Hit a bounday.
+      } else {
+        terminatetetgen(this, 2);
+      }
+    }
+
+    // Now assume that the base face abc coincides with the horizon plane,
+    //   and d lies above the horizon.  The search point 'endpt' may lie
+    //   above or below the horizon.  We test the orientations of 'endpt'
+    //   with respect to three planes: abc (horizon), bad (right plane),
+    //   and acd (left plane). 
+    hori = orient3d(pa, pb, pc, endpt);
+    rori = orient3d(pb, pa, pd, endpt);
+    lori = orient3d(pa, pc, pd, endpt);
+
+    // Now decide the tet to move.  It is possible there are more than one
+    //   tets are viable moves. Is so, randomly choose one. 
+    if (hori > 0) {
+      if (rori > 0) {
+        if (lori > 0) {
+          // Any of the three neighbors is a viable move.
+          s = randomnation(3); 
+          if (s == 0) {
+            nextmove = HMOVE;
+          } else if (s == 1) {
+            nextmove = RMOVE;
+          } else {
+            nextmove = LMOVE;
+          }
+        } else {
+          // Two tets, below horizon and below right, are viable.
+          if (randomnation(2)) {
+            nextmove = HMOVE;
+          } else {
+            nextmove = RMOVE;
+          }
+        }
+      } else {
+        if (lori > 0) {
+          // Two tets, below horizon and below left, are viable.
+          if (randomnation(2)) {
+            nextmove = HMOVE;
+          } else {
+            nextmove = LMOVE;
+          }
+        } else {
+          // The tet below horizon is chosen.
+          nextmove = HMOVE;
+        }
+      }
+    } else {
+      if (rori > 0) {
+        if (lori > 0) {
+          // Two tets, below right and below left, are viable.
+          if (randomnation(2)) {
+            nextmove = RMOVE;
+          } else {
+            nextmove = LMOVE;
+          }
+        } else {
+          // The tet below right is chosen.
+          nextmove = RMOVE;
+        }
+      } else {
+        if (lori > 0) {
+          // The tet below left is chosen.
+          nextmove = LMOVE;
+        } else {
+          // 'endpt' lies either on the plane(s) or across face bcd.
+          if (hori == 0) {
+            if (rori == 0) {
+              // pa->'endpt' is COLLINEAR with pa->pb.
+              return ACROSSVERT;
+            }
+            if (lori == 0) {
+              // pa->'endpt' is COLLINEAR with pa->pc.
+              eprevesymself(*searchtet); // [a,c,d]
+              return ACROSSVERT;
+            }
+            // pa->'endpt' crosses the edge pb->pc.
+            return ACROSSEDGE;
+          }
+          if (rori == 0) {
+            if (lori == 0) {
+              // pa->'endpt' is COLLINEAR with pa->pd.
+              esymself(*searchtet); // face bad.
+              enextself(*searchtet); // face [a,d,b]
+              return ACROSSVERT;
+            }
+            // pa->'endpt' crosses the edge pb->pd.
+            esymself(*searchtet); // face bad.
+            enextself(*searchtet); // face adb
+            return ACROSSEDGE;
+          }
+          if (lori == 0) {
+            // pa->'endpt' crosses the edge pc->pd.
+            eprevesymself(*searchtet); // [a,c,d]
+            return ACROSSEDGE;
+          }
+          // pa->'endpt' crosses the face bcd.
+          return ACROSSFACE;
+        }
+      }
+    }
+
+    // Move to the next tet, fix pa as its origin.
+    if (nextmove == RMOVE) {
+      fnextself(*searchtet);
+    } else if (nextmove == LMOVE) {
+      eprevself(*searchtet);
+      fnextself(*searchtet);
+      enextself(*searchtet);
+    } else { // HMOVE
+      fsymself(*searchtet);
+      enextself(*searchtet);
+    }
+    pb = dest(*searchtet);
+    pc = apex(*searchtet);
+
+  } // while (1)
+
+}
+
+
+//// steiner_cxx //////////////////////////////////////////////////////////////
+////                                                                       ////
+////                                                                       ////
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// checkflipeligibility()    A call back function for boundary recovery.     //
+//                                                                           //
+// 'fliptype' indicates which elementary flip will be performed: 1 : 2-to-3, //
+// and 2 : 3-to-2, respectively.                                             //
+//                                                                           //
+// 'pa, ..., pe' are the vertices involved in this flip, where [a,b,c] is    //
+// the flip face, and [d,e] is the flip edge. NOTE: 'pc' may be 'dummypoint',//
+// other points must not be 'dummypoint'.                                    //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+int tetgenmesh::checkflipeligibility(int fliptype, point pa, point pb, 
+                                     point pc, point pd, point pe,
+                                     int level, int edgepivot,
+                                     flipconstraints* fc)
+{
+  point tmppts[3];
+  enum interresult dir;
+  int types[2], poss[4];
+  int intflag;
+  int rejflag = 0;
+  int i;
+
+  if (fc->seg[0] != NULL) {
+    // A constraining edge is given (e.g., for edge recovery).
+    if (fliptype == 1) {
+      // A 2-to-3 flip: [a,b,c] => [e,d,a], [e,d,b], [e,d,c].
+      tmppts[0] = pa;
+      tmppts[1] = pb;
+      tmppts[2] = pc;
+      for (i = 0; i < 3 && !rejflag; i++) {
+        if (tmppts[i] != dummypoint) {
+          // Test if the face [e,d,#] intersects the edge.
+          intflag = tri_edge_test(pe, pd, tmppts[i], fc->seg[0], fc->seg[1], 
+                                  NULL, 1, types, poss);
+          if (intflag == 2) {
+            // They intersect at a single point.
+            dir = (enum interresult) types[0];
+            if (dir == ACROSSFACE) {
+              // The interior of [e,d,#] intersect the segment.
+              rejflag = 1;
+            } else if (dir == ACROSSEDGE) {
+              if (poss[0] == 0) {
+                // The interior of [e,d] intersect the segment.
+                // Since [e,d] is the newly created edge. Reject this flip.
+                rejflag = 1; 
+              }
+            }
+          } else if (intflag == 4) {
+            // They may intersect at either a point or a line segment.
+            dir = (enum interresult) types[0];
+            if (dir == ACROSSEDGE) {
+              if (poss[0] == 0) {
+                // The interior of [e,d] intersect the segment.
+                // Since [e,d] is the newly created edge. Reject this flip.
+                rejflag = 1;
+              }
+            }
+          }
+        } // if (tmppts[0] != dummypoint)
+      } // i
+    } else if (fliptype == 2) {
+      // A 3-to-2 flip: [e,d,a], [e,d,b], [e,d,c] => [a,b,c]
+      if (pc != dummypoint) {
+        // Check if the new face [a,b,c] intersect the edge in its interior.
+        intflag = tri_edge_test(pa, pb, pc, fc->seg[0], fc->seg[1], NULL, 
+                                1, types, poss);
+        if (intflag == 2) {
+          // They intersect at a single point.
+          dir = (enum interresult) types[0];
+          if (dir == ACROSSFACE) {
+            // The interior of [a,b,c] intersect the segment.
+            rejflag = 1; // Do not flip.
+          }
+        } else if (intflag == 4) {
+          // [a,b,c] is coplanar with the edge. 
+          dir = (enum interresult) types[0];
+          if (dir == ACROSSEDGE) {
+            // The boundary of [a,b,c] intersect the segment.            
+            rejflag = 1; // Do not flip.
+          }
+        }
+      } // if (pc != dummypoint)
+    }
+  } // if (fc->seg[0] != NULL)
+
+  if ((fc->fac[0] != NULL) && !rejflag) {
+    // A constraining face is given (e.g., for face recovery).
+    if (fliptype == 1) {
+      // A 2-to-3 flip.
+      // Test if the new edge [e,d] intersects the face.
+      intflag = tri_edge_test(fc->fac[0], fc->fac[1], fc->fac[2], pe, pd, 
+                              NULL, 1, types, poss);
+      if (intflag == 2) {
+        // They intersect at a single point.
+        dir = (enum interresult) types[0];
+        if (dir == ACROSSFACE) {
+          rejflag = 1;
+        } else if (dir == ACROSSEDGE) {
+          rejflag = 1;
+        } 
+      } else if (intflag == 4) {
+        // The edge [e,d] is coplanar with the face.
+        // There may be two intersections.
+        for (i = 0; i < 2 && !rejflag; i++) {
+          dir = (enum interresult) types[i];
+          if (dir == ACROSSFACE) {
+            rejflag = 1;
+          } else if (dir == ACROSSEDGE) {
+            rejflag = 1;
+          }
+        }
+      }
+    } // if (fliptype == 1)
+  } // if (fc->fac[0] != NULL)
+
+  if ((fc->remvert != NULL) && !rejflag) {
+    // The vertex is going to be removed. Do not create a new edge which
+    //   contains this vertex.
+    if (fliptype == 1) {
+      // A 2-to-3 flip.
+      if ((pd == fc->remvert) || (pe == fc->remvert)) {
+        rejflag = 1;
+      }
+    }
+  }
+
+  if (fc->remove_large_angle && !rejflag) {
+    // Remove a large dihedral angle. Do not create a new small angle.
+    REAL cosmaxd = 0, diff;
+    if (fliptype == 1) {
+      // We assume that neither 'a' nor 'b' is dummypoint.
+      // A 2-to-3 flip: [a,b,c] => [e,d,a], [e,d,b], [e,d,c].
+      // The new tet [e,d,a,b] will be flipped later. Only two new tets:
+      //   [e,d,b,c] and [e,d,c,a] need to be checked.
+      if ((pc != dummypoint) && (pe != dummypoint) && (pd != dummypoint)) {
+        // Get the largest dihedral angle of [e,d,b,c].
+        tetalldihedral(pe, pd, pb, pc, NULL, &cosmaxd, NULL);
+        diff = cosmaxd - fc->cosdihed_in;
+        if (fabs(diff/fc->cosdihed_in) < b->epsilon) diff = 0.0; // Rounding.
+        if (diff <= 0) { //if (cosmaxd <= fc->cosdihed_in) {
+          rejflag = 1;
+        } else {
+          // Record the largest new angle.
+          if (cosmaxd < fc->cosdihed_out) {
+            fc->cosdihed_out = cosmaxd; 
+          }
+          // Get the largest dihedral angle of [e,d,c,a].
+          tetalldihedral(pe, pd, pc, pa, NULL, &cosmaxd, NULL);
+          diff = cosmaxd - fc->cosdihed_in;
+          if (fabs(diff/fc->cosdihed_in) < b->epsilon) diff = 0.0; // Rounding.
+          if (diff <= 0) { //if (cosmaxd <= fc->cosdihed_in) {
+            rejflag = 1;
+          } else {
+            // Record the largest new angle.
+            if (cosmaxd < fc->cosdihed_out) {
+              fc->cosdihed_out = cosmaxd; 
+            }
+          }
+        }
+      } // if (pc != dummypoint && ...)
+    } else if (fliptype == 2) {
+      // A 3-to-2 flip: [e,d,a], [e,d,b], [e,d,c] => [a,b,c]
+      // We assume that neither 'e' nor 'd' is dummypoint.
+      if (level == 0) {
+        // Both new tets [a,b,c,d] and [b,a,c,e] are new tets.
+        if ((pa != dummypoint) && (pb != dummypoint) && (pc != dummypoint)) {
+          // Get the largest dihedral angle of [a,b,c,d].
+          tetalldihedral(pa, pb, pc, pd, NULL, &cosmaxd, NULL);
+          diff = cosmaxd - fc->cosdihed_in;
+          if (fabs(diff/fc->cosdihed_in) < b->epsilon) diff = 0.0; // Rounding
+          if (diff <= 0) { //if (cosmaxd <= fc->cosdihed_in) {
+            rejflag = 1;
+          } else {
+            // Record the largest new angle.
+            if (cosmaxd < fc->cosdihed_out) {
+              fc->cosdihed_out = cosmaxd; 
+            }
+            // Get the largest dihedral angle of [b,a,c,e].
+            tetalldihedral(pb, pa, pc, pe, NULL, &cosmaxd, NULL);
+            diff = cosmaxd - fc->cosdihed_in;
+            if (fabs(diff/fc->cosdihed_in) < b->epsilon) diff = 0.0;// Rounding
+            if (diff <= 0) { //if (cosmaxd <= fc->cosdihed_in) {
+              rejflag = 1;
+            } else {
+              // Record the largest new angle.
+              if (cosmaxd < fc->cosdihed_out) {
+                fc->cosdihed_out = cosmaxd; 
+              }
+            }
+          }
+        }
+      } else { // level > 0
+        if (edgepivot == 1) {
+          // The new tet [a,b,c,d] will be flipped. Only check [b,a,c,e].
+          if ((pa != dummypoint) && (pb != dummypoint) && (pc != dummypoint)) {
+            // Get the largest dihedral angle of [b,a,c,e].
+            tetalldihedral(pb, pa, pc, pe, NULL, &cosmaxd, NULL);
+            diff = cosmaxd - fc->cosdihed_in;
+            if (fabs(diff/fc->cosdihed_in) < b->epsilon) diff = 0.0;// Rounding
+            if (diff <= 0) { //if (cosmaxd <= fc->cosdihed_in) {
+              rejflag = 1;
+            } else {
+              // Record the largest new angle.
+              if (cosmaxd < fc->cosdihed_out) {
+                fc->cosdihed_out = cosmaxd; 
+              }
+            }
+          }
+        } else {
+          // The new tet [b,a,c,e] will be flipped. Only check [a,b,c,d].
+          if ((pa != dummypoint) && (pb != dummypoint) && (pc != dummypoint)) {
+            // Get the largest dihedral angle of [b,a,c,e].
+            tetalldihedral(pa, pb, pc, pd, NULL, &cosmaxd, NULL);
+            diff = cosmaxd - fc->cosdihed_in;
+            if (fabs(diff/fc->cosdihed_in) < b->epsilon) diff = 0.0;// Rounding
+            if (diff <= 0) { //if (cosmaxd <= fc->cosdihed_in) {
+              rejflag = 1;
+            } else {
+              // Record the largest new angle.
+              if (cosmaxd < fc->cosdihed_out) {
+                fc->cosdihed_out = cosmaxd; 
+              }
+            }
+          }
+        } // edgepivot
+      } // level
+    }
+  }
+
+  return rejflag;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// removeedgebyflips()    Attempt to remove an edge by flips.                //
+//                                                                           //
+// 'flipedge' is a non-convex or flat edge [a,b,#,#] to be removed.          //
+//                                                                           //
+// The return value is a positive integer, it indicates whether the edge is  //
+// removed or not.  A value "2" means the edge is removed, otherwise, the    //
+// edge is not removed and the value (must >= 3) is the current number of    //
+// tets in the edge star.                                                    //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+int tetgenmesh::removeedgebyflips(triface *flipedge, flipconstraints* fc)
+{
+  triface *abtets, spintet;
+  int t1ver; 
+  int n, nn, i;
+
+
+  if (checksubsegflag) {
+    // Do not flip a segment.
+    if (issubseg(*flipedge)) {
+      if (fc->collectencsegflag) {
+        face checkseg, *paryseg;
+        tsspivot1(*flipedge, checkseg);
+        if (!sinfected(checkseg)) {
+          // Queue this segment in list.
+          sinfect(checkseg);                
+          caveencseglist->newindex((void **) &paryseg);
+          *paryseg = checkseg;
+        }
+      }
+      return 0;
+    }
+  }
+
+  // Count the number of tets at edge [a,b].
+  n = 0;
+  spintet = *flipedge;
+  while (1) {
+    n++;
+    fnextself(spintet);
+    if (spintet.tet == flipedge->tet) break;
+  }
+  if (n < 3) {
+    // It is only possible when the mesh contains inverted tetrahedra.  
+    terminatetetgen(this, 2); // Report a bug
+  }
+
+  if ((b->flipstarsize > 0) && (n > b->flipstarsize)) {
+    // The star size exceeds the limit.
+    return 0; // Do not flip it.
+  }
+
+  // Allocate spaces.
+  abtets = new triface[n];
+  // Collect the tets at edge [a,b].
+  spintet = *flipedge;
+  i = 0;
+  while (1) {
+    abtets[i] = spintet;
+    setelemcounter(abtets[i], 1); 
+    i++;
+    fnextself(spintet);
+    if (spintet.tet == flipedge->tet) break;
+  }
+
+
+  // Try to flip the edge (level = 0, edgepivot = 0).
+  nn = flipnm(abtets, n, 0, 0, fc);
+
+
+  if (nn > 2) {
+    // Edge is not flipped. Unmarktest the remaining tets in Star(ab).
+    for (i = 0; i < nn; i++) {
+      setelemcounter(abtets[i], 0);
+    }
+    // Restore the input edge (needed by Lawson's flip).
+    *flipedge = abtets[0];
+  }
+
+  // Release the temporary allocated spaces.
+  // NOTE: fc->unflip must be 0.
+  int bakunflip = fc->unflip;
+  fc->unflip = 0;
+  flipnm_post(abtets, n, nn, 0, fc);
+  fc->unflip = bakunflip;
+
+  delete [] abtets;
+
+  return nn; 
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// removefacebyflips()    Remove a face by flips.                            //
+//                                                                           //
+// Return 1 if the face is removed. Otherwise, return 0.                     //
+//                                                                           //
+// ASSUMPTIONS:                                                              //
+//   - 'flipface' must not be a subface.                                     //
+//   - 'flipface' must not be a hull face.                                   //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+int tetgenmesh::removefacebyflips(triface *flipface, flipconstraints* fc)
+{
+  triface fliptets[3], flipedge;
+  point pa, pb, pc, pd, pe;
+  REAL ori;
+  int reducflag = 0;
+
+  fliptets[0] = *flipface;
+  fsym(*flipface, fliptets[1]);
+  pa = org(fliptets[0]);
+  pb = dest(fliptets[0]);
+  pc = apex(fliptets[0]);
+  pd = oppo(fliptets[0]);
+  pe = oppo(fliptets[1]);
+
+  ori = orient3d(pa, pb, pd, pe);
+  if (ori > 0) {
+    ori = orient3d(pb, pc, pd, pe);
+    if (ori > 0) {
+      ori = orient3d(pc, pa, pd, pe);
+      if (ori > 0) {
+        // Found a 2-to-3 flip.
+        reducflag = 1;
+      } else {
+        eprev(*flipface, flipedge); // [c,a]
+      }
+    } else {
+      enext(*flipface, flipedge); // [b,c]
+    }
+  } else {
+    flipedge = *flipface; // [a,b]
+  }
+
+  if (reducflag) {
+    // A 2-to-3 flip is found.
+    flip23(fliptets, 0, fc);
+    return 1;
+  } else {
+    // Try to flip the selected edge of this face.
+    if (removeedgebyflips(&flipedge, fc) == 2) {
+      return 1;
+    }
+  }
+
+  // Face is not removed.
+  return 0;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// recoveredge()    Recover an edge in current tetrahedralization.           //
+//                                                                           //
+// If the edge is recovered, 'searchtet' returns a tet containing the edge.  //
+//                                                                           //
+// This edge may intersect a set of faces and edges in the mesh.  All these  //
+// faces or edges are needed to be removed.                                  //
+//                                                                           //
+// If the parameter 'fullsearch' is set, it tries to flip any face or edge   //
+// that intersects the recovering edge.  Otherwise, only the face or edge    //
+// which is visible by 'startpt' is tried.                                   //
+//                                                                           //
+// The parameter 'sedge' is used to report self-intersection. If it is not   //
+// a NULL, it is EITHER a segment OR a subface that contains this edge.      //
+//                                                                           //
+// Note that this routine assumes that the tetrahedralization is convex.     //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+int tetgenmesh::recoveredgebyflips(point startpt, point endpt, face *sedge,
+                                   triface* searchtet, int fullsearch)
+{
+  flipconstraints fc;
+  enum interresult dir;
+
+  fc.seg[0] = startpt;
+  fc.seg[1] = endpt;
+  fc.checkflipeligibility = 1;
+
+  // The mainloop of the edge reocvery.
+  while (1) { // Loop I
+
+    // Search the edge from 'startpt'.
+    point2tetorg(startpt, *searchtet);
+    dir = finddirection(searchtet, endpt);
+    if (dir == ACROSSVERT) {
+      if (dest(*searchtet) == endpt) {
+        return 1; // Edge is recovered.
+      } else {
+        if (sedge) {
+          return report_selfint_edge(startpt, endpt, sedge, searchtet, dir);
+        } else {
+          return 0;
+        }
+      }
+    }
+
+    // The edge is missing. 
+
+    // Try to remove the first intersecting face/edge.
+    enextesymself(*searchtet); // Go to the opposite face.
+    if (dir == ACROSSFACE) {
+      if (checksubfaceflag) {
+        if (issubface(*searchtet)) {
+          if (sedge) {
+            return report_selfint_edge(startpt, endpt, sedge, searchtet, dir);
+          } else {
+            return 0; // Cannot flip a subface.
+          }
+        }
+      }
+      // Try to flip a crossing face.
+      if (removefacebyflips(searchtet, &fc)) {
+        continue;
+      }
+    } else if (dir == ACROSSEDGE) {
+      if (checksubsegflag) {
+        if (issubseg(*searchtet)) {
+          if (sedge) {
+            return report_selfint_edge(startpt, endpt, sedge, searchtet, dir);
+          } else {
+            return 0; // Cannot flip a segment.
+          }
+        }
+      }
+      // Try to flip an intersecting edge.
+      if (removeedgebyflips(searchtet, &fc) == 2) {
+        continue;
+      }
+    }
+
+    // The edge is missing.
+
+    if (fullsearch) {
+      // Try to flip one of the faces/edges which intersects the edge.
+      triface neightet, spintet;
+      point pa, pb, pc, pd;
+      badface bakface;
+      enum interresult dir1;
+      int types[2], poss[4], pos = 0;
+      int success = 0;
+      int t1ver; 
+      int i, j;
+
+      // Loop through the sequence of intersecting faces/edges from
+      //   'startpt' to 'endpt'.
+      point2tetorg(startpt, *searchtet);
+      dir = finddirection(searchtet, endpt);
+
+      // Go to the face/edge intersecting the searching edge.
+      enextesymself(*searchtet); // Go to the opposite face.
+      // This face/edge has been tried in previous step.
+
+      while (1) { // Loop I-I
+
+        // Find the next intersecting face/edge.
+        fsymself(*searchtet);
+        if (dir == ACROSSFACE) {
+          neightet = *searchtet;
+          j = (neightet.ver & 3); // j is the current face number.
+          for (i = j + 1; i < j + 4; i++) {
+            neightet.ver = (i % 4);
+            pa = org(neightet);
+            pb = dest(neightet);
+            pc = apex(neightet);
+            pd = oppo(neightet); // The above point.
+            if (tri_edge_test(pa,pb,pc,startpt,endpt, pd, 1, types, poss)) {
+              dir = (enum interresult) types[0];
+              pos = poss[0];
+              break;
+            } else {
+              dir = DISJOINT;
+              pos = 0;
+            }
+          } // i
+          // There must be an intersection face/edge.
+          if (dir == DISJOINT) {
+            terminatetetgen(this, 2);
+          }
+        } else if (dir == ACROSSEDGE) {
+          while (1) { // Loop I-I-I
+            // Check the two opposite faces (of the edge) in 'searchtet'.  
+            for (i = 0; i < 2; i++) {
+              if (i == 0) {
+                enextesym(*searchtet, neightet);
+              } else {
+                eprevesym(*searchtet, neightet);
+              }
+              pa = org(neightet);
+              pb = dest(neightet);
+              pc = apex(neightet);
+              pd = oppo(neightet); // The above point.
+              if (tri_edge_test(pa,pb,pc,startpt,endpt,pd,1, types, poss)) {
+                dir = (enum interresult) types[0];
+                pos = poss[0];
+                break; // for loop
+              } else {
+                dir = DISJOINT;
+                pos = 0;
+              }
+            } // i
+            if (dir != DISJOINT) {
+              // Find an intersection face/edge.
+              break;  // Loop I-I-I
+            }
+            // No intersection. Rotate to the next tet at the edge.
+            fnextself(*searchtet);
+          } // while (1) // Loop I-I-I
+        } else {
+          terminatetetgen(this, 2); // Report a bug
+        }
+
+        // Adjust to the intersecting edge/vertex.
+        for (i = 0; i < pos; i++) {
+          enextself(neightet);
+        }
+
+        if (dir == SHAREVERT) {
+          // Check if we have reached the 'endpt'.
+          pd = org(neightet);
+          if (pd == endpt) {
+            // Failed to recover the edge.
+            break; // Loop I-I
+          } else {
+            terminatetetgen(this, 2); // Report a bug
+          }
+        }
+
+        // The next to be flipped face/edge.
+        *searchtet = neightet;
+
+        // Bakup this face (tetrahedron).
+        bakface.forg = org(*searchtet);
+        bakface.fdest = dest(*searchtet);
+        bakface.fapex = apex(*searchtet);
+        bakface.foppo = oppo(*searchtet);
+
+        // Try to flip this intersecting face/edge.
+        if (dir == ACROSSFACE) {
+          if (checksubfaceflag) {
+            if (issubface(*searchtet)) {
+              if (sedge) {
+                return report_selfint_edge(startpt,endpt,sedge,searchtet,dir);
+              } else {
+                return 0; // Cannot flip a subface.
+              }
+            }
+          }
+          if (removefacebyflips(searchtet, &fc)) {
+            success = 1;
+            break; // Loop I-I 
+          }
+        } else if (dir == ACROSSEDGE) {
+          if (checksubsegflag) {
+            if (issubseg(*searchtet)) {
+              if (sedge) {
+                return report_selfint_edge(startpt,endpt,sedge,searchtet,dir);
+              } else {
+                return 0; // Cannot flip a segment.
+              }
+            }
+          }
+          if (removeedgebyflips(searchtet, &fc) == 2) {
+            success = 1;
+            break; // Loop I-I
+          }
+        } else if (dir == ACROSSVERT) {
+          if (sedge) {
+            //return report_selfint_edge(startpt, endpt, sedge, searchtet, dir);
+            terminatetetgen(this, 2);
+          } else {
+            return 0;
+          }
+        } else {
+          terminatetetgen(this, 2); 
+        }
+
+        // The face/edge is not flipped.
+        if ((searchtet->tet == NULL) ||
+            (org(*searchtet) != bakface.forg) ||
+            (dest(*searchtet) != bakface.fdest) ||
+            (apex(*searchtet) != bakface.fapex) ||
+            (oppo(*searchtet) != bakface.foppo)) {
+          // 'searchtet' was flipped. We must restore it.
+          point2tetorg(bakface.forg, *searchtet);
+          dir1 = finddirection(searchtet, bakface.fdest);
+          if (dir1 == ACROSSVERT) {
+            if (dest(*searchtet) == bakface.fdest) {
+              spintet = *searchtet;
+              while (1) {
+                if (apex(spintet) == bakface.fapex) {
+                  // Found the face.
+                  *searchtet = spintet;
+                  break;
+                }
+                fnextself(spintet);
+                if (spintet.tet == searchtet->tet) {
+                  searchtet->tet = NULL;
+                  break; // Not find.
+                }
+	          } // while (1)
+              if (searchtet->tet != NULL) {
+                if (oppo(*searchtet) != bakface.foppo) {
+                  fsymself(*searchtet);
+                  if (oppo(*searchtet) != bakface.foppo) {
+                    // The original (intersecting) tet has been flipped.
+                    searchtet->tet = NULL;
+                    break; // Not find.
+                  }
+                }
+              }
+            } else {
+              searchtet->tet = NULL; // Not find.
+            }
+          } else {
+            searchtet->tet = NULL; // Not find.
+          }
+          if (searchtet->tet == NULL) {
+            success = 0; // This face/edge has been destroyed.
+            break; // Loop I-I 
+          }
+        }
+      } // while (1) // Loop I-I
+
+      if (success) {
+        // One of intersecting faces/edges is flipped.
+        continue;
+      }
+
+    } // if (fullsearch)
+
+    // The edge is missing.
+    break; // Loop I
+
+  } // while (1) // Loop I
+
+  return 0;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// add_steinerpt_in_schoenhardtpoly()    Insert a Steiner point in a Schoen- //
+//                                       hardt polyhedron.                   //
+//                                                                           //
+// 'abtets' is an array of n tets which all share at the edge [a,b]. Let the //
+// tets are [a,b,p0,p1], [a,b,p1,p2], ..., [a,b,p_(n-2),p_(n-1)].  Moreover, //
+// the edge [p0,p_(n-1)] intersects all of the tets in 'abtets'.  A special  //
+// case is that the edge [p0,p_(n-1)] is coplanar with the edge [a,b].       //
+// Such set of tets arises when we want to recover an edge from 'p0' to 'p_  //
+// (n-1)', and the number of tets at [a,b] can not be reduced by any flip.   //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+int tetgenmesh::add_steinerpt_in_schoenhardtpoly(triface *abtets, int n,
+                                                 int chkencflag)
+{
+  triface worktet, *parytet;
+  triface faketet1, faketet2;
+  point pc, pd, steinerpt;
+  insertvertexflags ivf;
+  optparameters opm;
+  REAL vcd[3], sampt[3], smtpt[3];
+  REAL maxminvol = 0.0, minvol = 0.0, ori;
+  int success, maxidx = 0;
+  int it, i;
+
+
+  pc = apex(abtets[0]);   // pc = p0
+  pd = oppo(abtets[n-1]); // pd = p_(n-1)
+
+
+  // Find an optimial point in edge [c,d]. It is visible by all outer faces
+  //   of 'abtets', and it maxmizes the min volume.
+
+  // initialize the list of 2n boundary faces.
+  for (i = 0; i < n; i++) {    
+    edestoppo(abtets[i], worktet); // [p_i,p_i+1,a]
+    cavetetlist->newindex((void **) &parytet);
+    *parytet = worktet;
+    eorgoppo(abtets[i], worktet);  // [p_i+1,p_i,b]
+    cavetetlist->newindex((void **) &parytet);
+    *parytet = worktet;
+  }
+
+  int N = 100;
+  REAL stepi = 0.01;
+
+  // Search the point along the edge [c,d].
+  for (i = 0; i < 3; i++) vcd[i] = pd[i] - pc[i];
+
+  // Sample N points in edge [c,d].
+  for (it = 1; it < N; it++) {
+    for (i = 0; i < 3; i++) {
+      sampt[i] = pc[i] + (stepi * (double) it) * vcd[i];
+    }
+    for (i = 0; i < cavetetlist->objects; i++) {
+      parytet = (triface *) fastlookup(cavetetlist, i);
+      ori = orient3d(dest(*parytet), org(*parytet), apex(*parytet), sampt);
+      if (i == 0) {
+        minvol = ori;
+      } else {
+        if (minvol > ori) minvol = ori;
+      }
+    } // i
+    if (it == 1) {
+      maxminvol = minvol;
+      maxidx = it;
+    } else {
+      if (maxminvol < minvol) {
+        maxminvol = minvol;
+        maxidx = it;
+      } 
+    }
+  } // it
+
+  if (maxminvol <= 0) {
+    cavetetlist->restart();
+    return 0;
+  }
+
+  for (i = 0; i < 3; i++) {
+    smtpt[i] = pc[i] + (stepi * (double) maxidx) * vcd[i];
+  }
+
+  // Create two faked tets to hold the two non-existing boundary faces:
+  //   [d,c,a] and [c,d,b].
+  maketetrahedron(&faketet1);
+  setvertices(faketet1, pd, pc, org(abtets[0]), dummypoint);
+  cavetetlist->newindex((void **) &parytet);
+  *parytet = faketet1;
+  maketetrahedron(&faketet2);
+  setvertices(faketet2, pc, pd, dest(abtets[0]), dummypoint);
+  cavetetlist->newindex((void **) &parytet);
+  *parytet = faketet2;
+
+  // Point smooth options.
+  opm.max_min_volume = 1;
+  opm.numofsearchdirs = 20;
+  opm.searchstep = 0.001;  
+  opm.maxiter = 100; // Limit the maximum iterations.
+  opm.initval = 0.0; // Initial volume is zero.
+
+  // Try to relocate the point into the inside of the polyhedron.
+  success = smoothpoint(smtpt, cavetetlist, 1, &opm);
+
+  if (success) {
+    while (opm.smthiter == 100) {
+      // It was relocated and the prescribed maximum iteration reached. 
+      // Try to increase the search stepsize.
+      opm.searchstep *= 10.0;
+      //opm.maxiter = 100; // Limit the maximum iterations.
+      opm.initval = opm.imprval;
+      opm.smthiter = 0; // Init.
+      smoothpoint(smtpt, cavetetlist, 1, &opm);  
+    }
+  } // if (success)
+
+  // Delete the two faked tets.
+  tetrahedrondealloc(faketet1.tet);
+  tetrahedrondealloc(faketet2.tet);
+
+  cavetetlist->restart();
+
+  if (!success) {
+    return 0;
+  }
+
+
+  // Insert the Steiner point.
+  makepoint(&steinerpt, FREEVOLVERTEX);
+  for (i = 0; i < 3; i++) steinerpt[i] = smtpt[i];
+
+  // Insert the created Steiner point.
+  for (i = 0; i < n; i++) {
+    infect(abtets[i]);
+    caveoldtetlist->newindex((void **) &parytet);
+    *parytet = abtets[i];
+  }
+  worktet = abtets[0]; // No need point location.
+  ivf.iloc = (int) INSTAR;
+  ivf.chkencflag = chkencflag;
+  ivf.assignmeshsize = b->metric; 
+  if (ivf.assignmeshsize) {
+    // Search the tet containing 'steinerpt' for size interpolation.
+    locate(steinerpt, &(abtets[0]));
+    worktet = abtets[0];
+  }
+
+  // Insert the new point into the tetrahedralization T.
+  // Note that T is convex (nonconvex = 0).
+  if (insertpoint(steinerpt, &worktet, NULL, NULL, &ivf)) {
+    // The vertex has been inserted.
+    st_volref_count++; 
+    if (steinerleft > 0) steinerleft--;
+    return 1;
+  } else {
+    // Not inserted. 
+    pointdealloc(steinerpt);
+    return 0;
+  }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// add_steinerpt_in_segment()    Add a Steiner point inside a segment.       //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+int tetgenmesh::add_steinerpt_in_segment(face* misseg, int searchlevel)
+{
+  triface searchtet;
+  face *paryseg, candseg;
+  point startpt, endpt, pc, pd;
+  flipconstraints fc;
+  enum interresult dir;
+  REAL P[3], Q[3], tp, tq;
+  REAL len, smlen = 0, split = 0, split_q = 0;
+  int success=0;
+  int i;
+
+  startpt = sorg(*misseg);
+  endpt = sdest(*misseg);
+
+  fc.seg[0] = startpt;
+  fc.seg[1] = endpt;
+  fc.checkflipeligibility = 1;
+  fc.collectencsegflag = 1;
+
+  point2tetorg(startpt, searchtet);
+  dir = finddirection(&searchtet, endpt);
+  // Try to flip the first intersecting face/edge.
+  enextesymself(searchtet); // Go to the opposite face.
+
+  int bak_fliplinklevel = b->fliplinklevel;
+  b->fliplinklevel = searchlevel;
+
+  if (dir == ACROSSFACE) {
+    // A face is intersected with the segment. Try to flip it.
+    success = removefacebyflips(&searchtet, &fc);
+  } else if (dir == ACROSSEDGE) {
+    // An edge is intersected with the segment. Try to flip it.
+    success = removeedgebyflips(&searchtet, &fc);
+  }
+
+  split = 0;
+  for (i = 0; i < caveencseglist->objects; i++) {
+    paryseg = (face *) fastlookup(caveencseglist, i);
+    suninfect(*paryseg);
+    // Calculate the shortest edge between the two lines.
+    pc = sorg(*paryseg);
+    pd = sdest(*paryseg);
+    tp = tq = 0;
+    if (linelineint(startpt, endpt, pc, pd, P, Q, &tp, &tq)) {
+      // Does the shortest edge lie between the two segments? 
+      // Round tp and tq.
+      if ((tp > 0) && (tq < 1)) {
+        if (tp < 0.5) {
+          if (tp < (b->epsilon * 1e+3)) tp = 0.0;
+        } else {
+          if ((1.0 - tp) < (b->epsilon * 1e+3)) tp = 1.0;
+        }
+      }
+      if ((tp <= 0) || (tp >= 1)) continue; 
+      if ((tq > 0) && (tq < 1)) {
+        if (tq < 0.5) {
+          if (tq < (b->epsilon * 1e+3)) tq = 0.0;
+        } else {
+          if ((1.0 - tq) < (b->epsilon * 1e+3)) tq = 1.0;
+        }
+      }
+      if ((tq <= 0) || (tq >= 1)) continue;
+      // It is a valid shortest edge. Calculate its length.
+      len = distance(P, Q);
+      if (split == 0) {
+        smlen = len;
+        split = tp;
+        split_q = tq;
+        candseg = *paryseg;
+      } else {
+        if (len < smlen) {
+          smlen = len;
+          split = tp;
+          split_q = tq;
+          candseg = *paryseg;
+        }
+      }
+    }
+  }
+
+  caveencseglist->restart();
+  b->fliplinklevel = bak_fliplinklevel;
+
+  if (split == 0) {
+    // Found no crossing segment. 
+    return 0;
+  }
+
+  face splitsh;
+  face splitseg;
+  point steinerpt, *parypt;
+  insertvertexflags ivf;
+
+  if (b->addsteiner_algo == 1) {
+    // Split the segment at the closest point to a near segment.
+    makepoint(&steinerpt, FREESEGVERTEX);
+    for (i = 0; i < 3; i++) {
+      steinerpt[i] = startpt[i] + split * (endpt[i] - startpt[i]);
+    }
+  } else { // b->addsteiner_algo == 2
+    for (i = 0; i < 3; i++) {
+      P[i] = startpt[i] + split * (endpt[i] - startpt[i]);
+    }
+    pc = sorg(candseg);
+    pd = sdest(candseg);
+    for (i = 0; i < 3; i++) {
+      Q[i] = pc[i] + split_q * (pd[i] - pc[i]);
+    }
+    makepoint(&steinerpt, FREEVOLVERTEX);
+    for (i = 0; i < 3; i++) {
+      steinerpt[i] = 0.5 * (P[i] + Q[i]);
+    }
+  }
+
+  // We need to locate the point. Start searching from 'searchtet'.
+  if (split < 0.5) {
+    point2tetorg(startpt, searchtet);
+  } else {
+    point2tetorg(endpt, searchtet);
+  }
+  if (b->addsteiner_algo == 1) {
+    splitseg = *misseg;
+    spivot(*misseg, splitsh);
+  } else {
+    splitsh.sh = NULL;
+    splitseg.sh = NULL;
+  }
+  ivf.iloc = (int) OUTSIDE;
+  ivf.bowywat = 1;
+  ivf.lawson = 0;
+  ivf.rejflag = 0;
+  ivf.chkencflag = 0;
+  ivf.sloc = (int) ONEDGE;
+  ivf.sbowywat = 1;
+  ivf.splitbdflag = 0;
+  ivf.validflag = 1;
+  ivf.respectbdflag = 1;
+  ivf.assignmeshsize = b->metric; 
+
+  if (!insertpoint(steinerpt, &searchtet, &splitsh, &splitseg, &ivf)) {
+    pointdealloc(steinerpt);
+    return 0;
+  }
+
+  if (b->addsteiner_algo == 1) {
+    // Save this Steiner point (for removal).
+    //   Re-use the array 'subvertstack'.
+    subvertstack->newindex((void **) &parypt);
+    *parypt = steinerpt;
+    st_segref_count++;
+  } else { // b->addsteiner_algo == 2
+    // Queue the segment for recovery.
+    subsegstack->newindex((void **) &paryseg);
+    *paryseg = *misseg; 
+    st_volref_count++;
+  }
+  if (steinerleft > 0) steinerleft--;
+  if (!success){
+    // error
+  }
+    
+  return 1;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// addsteiner4recoversegment()    Add a Steiner point for recovering a seg.  //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+int tetgenmesh::addsteiner4recoversegment(face* misseg, int splitsegflag)
+{
+  triface *abtets, searchtet, spintet;
+  face splitsh;
+  face *paryseg;
+  point startpt, endpt;
+  point pa, pb, pd, steinerpt, *parypt;
+  enum interresult dir;
+  insertvertexflags ivf;
+  int types[2], poss[4];
+  int n, endi, success;
+  int t1ver;
+  int i;
+
+  startpt = sorg(*misseg);
+  if (pointtype(startpt) == FREESEGVERTEX) {
+    sesymself(*misseg);
+    startpt = sorg(*misseg);
+  }
+  endpt = sdest(*misseg);
+
+  // Try to recover the edge by adding Steiner points.
+  point2tetorg(startpt, searchtet);
+  dir = finddirection(&searchtet, endpt);
+  enextself(searchtet); 
+
+  if (dir == ACROSSFACE) {
+    // The segment is crossing at least 3 faces. Find the common edge of 
+    //   the first 3 crossing faces.
+    esymself(searchtet);
+    fsym(searchtet, spintet);
+    pd = oppo(spintet);
+    for (i = 0; i < 3; i++) {
+      pa = org(spintet);
+      pb = dest(spintet);
+      if (tri_edge_test(pa, pb, pd, startpt, endpt, NULL, 1, types, poss)) {
+        break; // Found the edge.
+      }
+      enextself(spintet);
+      eprevself(searchtet);
+    }
+    esymself(searchtet);        
+  } 
+
+  spintet = searchtet;
+  n = 0; endi = -1;
+  while (1) {
+    // Check if the endpt appears in the star.
+    if (apex(spintet) == endpt) {
+      endi = n; // Remember the position of endpt.
+    }
+    n++; // Count a tet in the star.
+    fnextself(spintet);
+    if (spintet.tet == searchtet.tet) break;
+  }
+
+  if (endi > 0) {
+    // endpt is also in the edge star
+    // Get all tets in the edge star.
+    abtets = new triface[n];
+    spintet = searchtet;
+    for (i = 0; i < n; i++) {
+      abtets[i] = spintet;
+      fnextself(spintet);
+    }
+
+    success = 0;
+
+    if (dir == ACROSSFACE) {
+      // Find a Steiner points inside the polyhedron.
+      if (add_steinerpt_in_schoenhardtpoly(abtets, endi, 0)) {
+        success = 1;
+      }
+    } else if (dir == ACROSSEDGE) {
+      // PLC check.
+      if (issubseg(searchtet)) {
+        terminatetetgen(this, 2);
+      }
+      if (n > 4) {
+        // In this case, 'abtets' is separated by the plane (containing the
+        //   two intersecting edges) into two parts, P1 and P2, where P1
+        //   consists of 'endi' tets: abtets[0], abtets[1], ..., 
+        //   abtets[endi-1], and P2 consists of 'n - endi' tets: 
+        //   abtets[endi], abtets[endi+1], abtets[n-1].
+        if (endi > 2) { // P1
+          // There are at least 3 tets in the first part.
+          if (add_steinerpt_in_schoenhardtpoly(abtets, endi, 0)) {
+            success++;
+          }
+        }
+        if ((n - endi) > 2) { // P2
+          // There are at least 3 tets in the first part.
+          if (add_steinerpt_in_schoenhardtpoly(&(abtets[endi]), n - endi, 0)) {
+            success++;
+          }
+        }
+      } else {
+        // In this case, a 4-to-4 flip should be re-cover the edge [c,d].
+        //   However, there will be invalid tets (either zero or negtive 
+        //   volume). Otherwise, [c,d] should already be recovered by the 
+        //   recoveredge() function.
+        terminatetetgen(this, 2);
+      }
+    } else {
+      terminatetetgen(this, 2);
+    }
+
+    delete [] abtets;
+
+    if (success) {
+      // Add the missing segment back to the recovering list.
+      subsegstack->newindex((void **) &paryseg);
+      *paryseg = *misseg;
+      return 1;
+    }
+  } // if (endi > 0)
+
+  if (!splitsegflag) {
+    return 0;
+  }
+
+  if (b->verbose > 2) {
+    printf("      Splitting segment (%d, %d)\n", pointmark(startpt), 
+           pointmark(endpt));
+  }
+  steinerpt = NULL;
+
+  if (b->addsteiner_algo > 0) { // -Y/1 or -Y/2
+    if (add_steinerpt_in_segment(misseg, 3)) {
+      return 1;
+    }
+    sesymself(*misseg);
+    if (add_steinerpt_in_segment(misseg, 3)) {
+      return 1;
+    }
+    sesymself(*misseg);
+  }
+
+
+
+
+  if (steinerpt == NULL) {
+    // Split the segment at its midpoint.
+    makepoint(&steinerpt, FREESEGVERTEX);
+    for (i = 0; i < 3; i++) {
+      steinerpt[i] = 0.5 * (startpt[i] + endpt[i]);
+    }
+
+    // We need to locate the point.
+    spivot(*misseg, splitsh);
+    ivf.iloc = (int) OUTSIDE;
+    ivf.bowywat = 1;
+    ivf.lawson = 0;
+    ivf.rejflag = 0;
+    ivf.chkencflag = 0;
+    ivf.sloc = (int) ONEDGE;
+    ivf.sbowywat = 1;
+    ivf.splitbdflag = 0;
+    ivf.validflag = 1;
+    ivf.respectbdflag = 1;
+    ivf.assignmeshsize = b->metric; 
+    if (!insertpoint(steinerpt, &searchtet, &splitsh, misseg, &ivf)) {
+      terminatetetgen(this, 2);
+    }
+  } // if (endi > 0)
+
+  // Save this Steiner point (for removal).
+  //   Re-use the array 'subvertstack'.
+  subvertstack->newindex((void **) &parypt);
+  *parypt = steinerpt;
+
+  st_segref_count++;
+  if (steinerleft > 0) steinerleft--;
+
+  return 1;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// recoversegments()    Recover all segments.                                //
+//                                                                           //
+// All segments need to be recovered are in 'subsegstack'.                   //
+//                                                                           //
+// This routine first tries to recover each segment by only using flips. If  //
+// no flip is possible, and the flag 'steinerflag' is set, it then tries to  //
+// insert Steiner points near or in the segment.                             //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+int tetgenmesh::recoversegments(arraypool *misseglist, int fullsearch,
+                                int steinerflag)
+{
+  triface searchtet, spintet;
+  face sseg, *paryseg;
+  point startpt, endpt;
+  int success;
+  int t1ver;
+
+  long bak_inpoly_count = st_volref_count; 
+  long bak_segref_count = st_segref_count;
+
+  if (b->verbose > 1) {
+    printf("    Recover segments [%s level = %2d] #:  %ld.\n",
+           (b->fliplinklevel > 0) ? "fixed" : "auto",
+           (b->fliplinklevel > 0) ? b->fliplinklevel : autofliplinklevel,
+           subsegstack->objects);
+  }
+
+  // Loop until 'subsegstack' is empty.
+  while (subsegstack->objects > 0l) {
+    // seglist is used as a stack.
+    subsegstack->objects--;
+    paryseg = (face *) fastlookup(subsegstack, subsegstack->objects);
+    sseg = *paryseg;
+
+    // Check if this segment has been recovered.
+    sstpivot1(sseg, searchtet);
+    if (searchtet.tet != NULL) {
+      continue; // Not a missing segment.
+    }
+
+    startpt = sorg(sseg);
+    endpt = sdest(sseg);
+
+    if (b->verbose > 2) {
+      printf("      Recover segment (%d, %d).\n", pointmark(startpt), 
+             pointmark(endpt));
+    }
+
+    success = 0;
+
+    if (recoveredgebyflips(startpt, endpt, &sseg, &searchtet, 0)) {
+      success = 1;
+    } else {
+      // Try to recover it from the other direction.
+      if (recoveredgebyflips(endpt, startpt, &sseg, &searchtet, 0)) {
+        success = 1;
+      }
+    }
+
+    if (!success && fullsearch) {
+      if (recoveredgebyflips(startpt, endpt, &sseg, &searchtet, fullsearch)) {
+        success = 1;
+      }
+    }
+
+    if (success) {
+      // Segment is recovered. Insert it.
+      // Let the segment remember an adjacent tet.
+      sstbond1(sseg, searchtet);
+      // Bond the segment to all tets containing it.
+      spintet = searchtet;
+      do {
+        tssbond1(spintet, sseg);
+        fnextself(spintet);
+      } while (spintet.tet != searchtet.tet);
+    } else {
+      if (steinerflag > 0) {
+        // Try to recover the segment but do not split it.
+        if (addsteiner4recoversegment(&sseg, 0)) {
+          success = 1;
+        }
+        if (!success && (steinerflag > 1)) {
+          // Split the segment.
+          addsteiner4recoversegment(&sseg, 1);
+          success = 1;
+        }
+      }
+      if (!success) {
+        if (misseglist != NULL) {
+          // Save this segment.
+          misseglist->newindex((void **) &paryseg);
+          *paryseg = sseg;
+        }
+      }
+    }
+
+  } // while (subsegstack->objects > 0l)
+
+  if (steinerflag) {
+    if (b->verbose > 1) {
+      // Report the number of added Steiner points.
+      if (st_volref_count > bak_inpoly_count) {
+        printf("    Add %ld Steiner points in volume.\n", 
+               st_volref_count - bak_inpoly_count);
+      }
+      if (st_segref_count > bak_segref_count) {
+        printf("    Add %ld Steiner points in segments.\n", 
+               st_segref_count - bak_segref_count);
+      }
+    }
+  }
+
+  return 0;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// recoverfacebyflips()    Recover a face by flips.                          //
+//                                                                           //
+// 'pa', 'pb', and 'pc' are the three vertices of this face.  This routine   //
+// tries to recover it in the tetrahedral mesh. It is assumed that the three //
+// edges, i.e., pa->pb, pb->pc, and pc->pa all exist.                        //
+//                                                                           //
+// If the face is recovered, it is returned by 'searchtet'.                  //
+//                                                                           //
+// If 'searchsh' is not NULL, it is a subface to be recovered.  Its vertices //
+// must be pa, pb, and pc.  It is mainly used to check self-intersections.   //
+// Another use of this subface is to split it when a Steiner point is found  //
+// inside this subface.                                                      //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+int tetgenmesh::recoverfacebyflips(point pa, point pb, point pc, 
+                                   face *searchsh, triface* searchtet)
+{
+  triface spintet, flipedge;
+  point pd, pe;
+  flipconstraints fc;
+  int types[2], poss[4], intflag;
+  int success;
+  int t1ver; 
+  int i, j;
+
+
+  fc.fac[0] = pa;
+  fc.fac[1] = pb;
+  fc.fac[2] = pc;
+  fc.checkflipeligibility = 1;
+  success = 0;
+
+  for (i = 0; i < 3 && !success; i++) {
+    while (1) {
+      // Get a tet containing the edge [a,b].
+      point2tetorg(fc.fac[i], *searchtet);
+      finddirection(searchtet, fc.fac[(i+1)%3]);
+      // Search the face [a,b,c]
+      spintet = *searchtet;
+      while (1) {
+        if (apex(spintet) == fc.fac[(i+2)%3]) {
+          // Found the face.
+          *searchtet = spintet;
+          // Return the face [a,b,c].
+          for (j = i; j > 0; j--) {
+            eprevself(*searchtet);
+          }
+          success = 1;
+          break;
+        }
+        fnextself(spintet);
+        if (spintet.tet == searchtet->tet) break;
+      } // while (1)
+      if (success) break;
+      // The face is missing. Try to recover it.
+      flipedge.tet = NULL;
+      // Find a crossing edge of this face.
+      spintet = *searchtet;
+      while (1) {
+        pd = apex(spintet);
+        pe = oppo(spintet);
+        if ((pd != dummypoint) && (pe != dummypoint)) {
+          // Check if [d,e] intersects [a,b,c]
+          intflag = tri_edge_test(pa, pb, pc, pd, pe, NULL, 1, types, poss);
+          if (intflag > 0) {
+            // By the assumption that all edges of the face exist, they can
+            //   only intersect at a single point.
+            if (intflag == 2) {
+              // Go to the edge [d,e].
+              edestoppo(spintet, flipedge); // [d,e,a,b]
+              if (searchsh != NULL) {
+                // Check the intersection type.
+                if ((types[0] == (int) ACROSSFACE) || 
+                    (types[0] == (int) ACROSSEDGE)) {
+                  // Check if [e,d] is a segment.
+                  if (issubseg(flipedge)) {
+                    return report_selfint_face(pa, pb, pc, searchsh, &flipedge,
+                                               intflag, types, poss);
+		          } else {
+				    // Check if [e,d] is an edge of a subface.
+					triface chkface = flipedge;
+					while (1) {
+					  if (issubface(chkface)) break;
+					  fsymself(chkface);
+					  if (chkface.tet == flipedge.tet) break;
+					}
+					if (issubface(chkface)) {
+					  // Two subfaces are intersecting.
+					  return report_selfint_face(pa, pb, pc,searchsh,&chkface,
+                                                 intflag, types, poss);
+					}
+				  }
+				} else if (types[0] == TOUCHFACE) {
+                  // This is possible when a Steiner point was added on it.
+				  point touchpt, *parypt;
+                  if (poss[1] == 0) {
+                    touchpt = pd; // pd is a coplanar vertex.
+                  } else {
+                    touchpt = pe; // pe is a coplanar vertex.
+                  }
+				  if (pointtype(touchpt) == FREEVOLVERTEX) {
+                    // A volume Steiner point was added in this subface.
+                    // Split this subface by this point.
+                    face checksh, *parysh;
+                    int siloc = (int) ONFACE;
+                    int sbowat = 0; // Only split this subface. A 1-to-3 flip.
+                    setpointtype(touchpt, FREEFACETVERTEX);
+                    sinsertvertex(touchpt, searchsh, NULL, siloc, sbowat, 0);
+                    st_volref_count--;
+                    st_facref_count++;
+                    // Queue this vertex for removal.
+                    subvertstack->newindex((void **) &parypt);
+                    *parypt = touchpt;
+                    // Queue new subfaces for recovery.
+                    // Put all new subfaces into stack for recovery.
+                    for (i = 0; i < caveshbdlist->objects; i++) {
+                      // Get an old subface at edge [a, b].
+                      parysh = (face *) fastlookup(caveshbdlist, i);
+                      spivot(*parysh, checksh); // The new subface [a, b, p].
+                      // Do not recover a deleted new face (degenerated).
+                      if (checksh.sh[3] != NULL) {
+                        subfacstack->newindex((void **) &parysh);
+                        *parysh = checksh;
+                      }
+                    }
+                    // Delete the old subfaces in sC(p).
+                    for (i = 0; i < caveshlist->objects; i++) {
+                      parysh = (face *) fastlookup(caveshlist, i);
+                      shellfacedealloc(subfaces, parysh->sh);
+                    }
+                    // Clear working lists.
+                    caveshlist->restart();
+                    caveshbdlist->restart();
+                    cavesegshlist->restart();
+                    // We can return this function.
+                    searchsh->sh = NULL; // It has been split.
+					return 1;
+                  } else {
+				    // Other cases may be due to a bug or a PLC error.
+					return report_selfint_face(pa, pb, pc, searchsh, &flipedge,
+                                               intflag, types, poss);
+				  }
+                } else {
+                  // The other intersection types: ACROSSVERT, TOUCHEDGE, 
+                  // SHAREVERTEX should not be possible or due to a PLC error.
+                  return report_selfint_face(pa, pb, pc, searchsh, &flipedge,
+                                             intflag, types, poss);
+                }
+              } // if (searchsh != NULL)
+            } else { // intflag == 4. Coplanar case.
+              terminatetetgen(this, 2); 
+            }
+            break;
+          } // if (intflag > 0)
+        }
+        fnextself(spintet);
+        if (spintet.tet == searchtet->tet) {
+          terminatetetgen(this, 2);
+        }
+      } // while (1)
+      // Try to flip the edge [d,e].
+      if (removeedgebyflips(&flipedge, &fc) == 2) {
+        // A crossing edge is removed.
+        continue; 
+      }
+      break;
+    } // while (1)
+  } // i
+
+  return success;
+}
+                                   
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// recoversubfaces()    Recover all subfaces.                                //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+int tetgenmesh::recoversubfaces(arraypool *misshlist, int steinerflag)
+{
+  triface searchtet, neightet, spintet;
+  face searchsh, neighsh, neineish, *parysh;
+  face bdsegs[3];
+  point startpt, endpt, apexpt, *parypt;
+  point steinerpt;
+  insertvertexflags ivf;
+  int success;
+  int t1ver;
+  int i, j;
+
+  if (b->verbose > 1) {
+    printf("    Recover subfaces [%s level = %2d] #:  %ld.\n",
+           (b->fliplinklevel > 0) ? "fixed" : "auto",
+           (b->fliplinklevel > 0) ? b->fliplinklevel : autofliplinklevel,
+           subfacstack->objects);
+  }
+
+  // Loop until 'subfacstack' is empty.
+  while (subfacstack->objects > 0l) {
+
+    subfacstack->objects--;
+    parysh = (face *) fastlookup(subfacstack, subfacstack->objects);
+    searchsh = *parysh;
+
+    if (searchsh.sh[3] == NULL) continue; // Skip a dead subface.
+
+    stpivot(searchsh, neightet);
+    if (neightet.tet != NULL) continue; // Skip a recovered subface.
+
+
+    if (b->verbose > 2) {
+      printf("      Recover subface (%d, %d, %d).\n",pointmark(sorg(searchsh)),
+             pointmark(sdest(searchsh)), pointmark(sapex(searchsh)));
+    }
+
+    // The three edges of the face need to be existed first.
+    for (i = 0; i < 3; i++) {
+      sspivot(searchsh, bdsegs[i]);   
+      if (bdsegs[i].sh != NULL) {
+        // The segment must exist.
+        sstpivot1(bdsegs[i], searchtet);
+        if (searchtet.tet == NULL) {
+          terminatetetgen(this, 2);
+        }
+      } else {
+        // This edge is not a segment (due to a Steiner point).
+        // Check whether it exists or not.
+        success = 0;
+        startpt = sorg(searchsh);
+        endpt = sdest(searchsh);
+        point2tetorg(startpt, searchtet);
+        finddirection(&searchtet, endpt);
+        if (dest(searchtet) == endpt) {
+          success = 1;
+        } else {
+          // The edge is missing. Try to recover it.
+          if (recoveredgebyflips(startpt, endpt, &searchsh, &searchtet, 0)) {
+            success = 1;
+          } else {
+            if (recoveredgebyflips(endpt, startpt, &searchsh, &searchtet, 0)) {
+              success = 1;
+            }
+          }
+        }
+        if (success) {
+          // Insert a temporary segment to protect this edge.
+          makeshellface(subsegs, &(bdsegs[i]));
+          setshvertices(bdsegs[i], startpt, endpt, NULL);
+          smarktest2(bdsegs[i]); // It's a temporary segment.
+          // Insert this segment into surface mesh.
+          ssbond(searchsh, bdsegs[i]);
+          spivot(searchsh, neighsh);
+          if (neighsh.sh != NULL) {
+            ssbond(neighsh, bdsegs[i]);
+          }
+          // Insert this segment into tetrahedralization.
+          sstbond1(bdsegs[i], searchtet);
+          // Bond the segment to all tets containing it.
+          spintet = searchtet;
+          do {
+            tssbond1(spintet, bdsegs[i]);
+            fnextself(spintet);
+          } while (spintet.tet != searchtet.tet);
+        } else {
+          // An edge of this subface is missing. Can't recover this subface.
+          // Delete any temporary segment that has been created.
+          for (j = (i - 1); j >= 0; j--) {
+            if (smarktest2ed(bdsegs[j])) { 
+              spivot(bdsegs[j], neineish);
+                ssdissolve(neineish);
+                spivot(neineish, neighsh);
+                if (neighsh.sh != NULL) {
+                  ssdissolve(neighsh);
+                }
+              sstpivot1(bdsegs[j], searchtet);
+                spintet = searchtet;
+                while (1) {
+                  tssdissolve1(spintet);
+                  fnextself(spintet);
+                  if (spintet.tet == searchtet.tet) break;
+                }
+              shellfacedealloc(subsegs, bdsegs[j].sh);
+            }
+          } // j
+          if (steinerflag) {
+            // Add a Steiner point at the midpoint of this edge.
+            if (b->verbose > 2) {
+              printf("      Add a Steiner point in subedge (%d, %d).\n",
+                     pointmark(startpt), pointmark(endpt));
+            }
+            makepoint(&steinerpt, FREEFACETVERTEX);
+            for (j = 0; j < 3; j++) {
+              steinerpt[j] = 0.5 * (startpt[j] + endpt[j]);
+            }
+
+            point2tetorg(startpt, searchtet); // Start from 'searchtet'.
+            ivf.iloc = (int) OUTSIDE; // Need point location.
+            ivf.bowywat = 1;
+            ivf.lawson = 0;
+            ivf.rejflag = 0;
+            ivf.chkencflag = 0;
+            ivf.sloc = (int) ONEDGE;            
+            ivf.sbowywat = 1; // Allow flips in facet.
+            ivf.splitbdflag = 0;
+            ivf.validflag = 1;
+            ivf.respectbdflag = 1;
+            ivf.assignmeshsize = b->metric;
+            if (!insertpoint(steinerpt, &searchtet, &searchsh, NULL, &ivf)) {
+              terminatetetgen(this, 2);
+            }
+            // Save this Steiner point (for removal).
+            //   Re-use the array 'subvertstack'.
+            subvertstack->newindex((void **) &parypt);
+            *parypt = steinerpt;
+
+            st_facref_count++;
+            if (steinerleft > 0) steinerleft--;
+          } // if (steinerflag)
+          break;
+        }
+      }
+      senextself(searchsh);
+    } // i
+
+    if (i == 3) {
+      // Recover the subface.
+      startpt = sorg(searchsh);
+      endpt   = sdest(searchsh);
+      apexpt  = sapex(searchsh);
+
+      success = recoverfacebyflips(startpt,endpt,apexpt,&searchsh,&searchtet);
+
+      // Delete any temporary segment that has been created.
+      for (j = 0; j < 3; j++) {
+        if (smarktest2ed(bdsegs[j])) { 
+          spivot(bdsegs[j], neineish);
+            ssdissolve(neineish);
+            spivot(neineish, neighsh);
+            if (neighsh.sh != NULL) {
+              ssdissolve(neighsh);
+            }
+          sstpivot1(bdsegs[j], neightet);
+            spintet = neightet;
+            while (1) {
+              tssdissolve1(spintet);
+              fnextself(spintet);
+              if (spintet.tet == neightet.tet) break;
+            }
+          shellfacedealloc(subsegs, bdsegs[j].sh);
+        }
+      } // j
+
+      if (success) {
+        if (searchsh.sh != NULL) {
+          // Face is recovered. Insert it.
+          tsbond(searchtet, searchsh);
+          fsymself(searchtet);
+          sesymself(searchsh);
+          tsbond(searchtet, searchsh);
+        }
+      } else {
+        if (steinerflag) {
+          // Add a Steiner point at the barycenter of this subface.
+          if (b->verbose > 2) {
+            printf("      Add a Steiner point in subface (%d, %d, %d).\n",
+                   pointmark(startpt), pointmark(endpt), pointmark(apexpt));
+          }
+          makepoint(&steinerpt, FREEFACETVERTEX);
+          for (j = 0; j < 3; j++) {
+            steinerpt[j] = (startpt[j] + endpt[j] + apexpt[j]) / 3.0;
+          }
+
+          point2tetorg(startpt, searchtet); // Start from 'searchtet'.
+          ivf.iloc = (int) OUTSIDE; // Need point location.
+          ivf.bowywat = 1;
+          ivf.lawson = 0;
+          ivf.rejflag = 0;
+          ivf.chkencflag = 0;
+          ivf.sloc = (int) ONFACE;          
+          ivf.sbowywat = 1; // Allow flips in facet.
+          ivf.splitbdflag = 0;
+          ivf.validflag = 1;
+          ivf.respectbdflag = 1;
+          ivf.assignmeshsize = b->metric; 
+          if (!insertpoint(steinerpt, &searchtet, &searchsh, NULL, &ivf)) {
+            terminatetetgen(this, 2);
+          }
+          // Save this Steiner point (for removal).
+          //   Re-use the array 'subvertstack'.
+          subvertstack->newindex((void **) &parypt);
+          *parypt = steinerpt;
+
+          st_facref_count++;
+          if (steinerleft > 0) steinerleft--;
+        } // if (steinerflag)
+      }
+    } else {
+      success = 0;      
+    }
+
+    if (!success) {
+      if (misshlist != NULL) {
+        // Save this subface.
+        misshlist->newindex((void **) &parysh);
+        *parysh = searchsh;
+      }
+    }
+
+  } // while (subfacstack->objects > 0l)
+
+  return 0;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// getvertexstar()    Return the star of a vertex.                           //
+//                                                                           //
+// If the flag 'fullstar' is set, return the complete star of this vertex.   //
+// Otherwise, only a part of the star which is bounded by facets is returned.// 
+//                                                                           //
+// 'tetlist' returns the list of tets in the star of the vertex 'searchpt'.  //
+// Every tet in 'tetlist' is at the face opposing to 'searchpt'.             //
+//                                                                           //
+// 'vertlist' returns the list of vertices in the star (exclude 'searchpt'). //
+//                                                                           //
+// 'shlist' returns the list of subfaces in the star. Each subface must face //
+// to the interior of this star.                                             //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+int tetgenmesh::getvertexstar(int fullstar, point searchpt, arraypool* tetlist, 
+                              arraypool* vertlist, arraypool* shlist)
+{
+  triface searchtet, neightet, *parytet;
+  face checksh, *parysh;
+  point pt, *parypt;
+  int collectflag;
+  int t1ver;
+  int i, j;
+
+  point2tetorg(searchpt, searchtet);
+
+  // Go to the opposite face (the link face) of the vertex.
+  enextesymself(searchtet);
+  //assert(oppo(searchtet) == searchpt);
+  infect(searchtet); // Collect this tet (link face).
+  tetlist->newindex((void **) &parytet);
+  *parytet = searchtet;
+  if (vertlist != NULL) {
+    // Collect three (link) vertices.
+    j = (searchtet.ver & 3); // The current vertex index.
+    for (i = 1; i < 4; i++) {
+      pt = (point) searchtet.tet[4 + ((j + i) % 4)];
+      pinfect(pt);
+      vertlist->newindex((void **) &parypt);
+      *parypt = pt;
+    }
+  }
+
+  collectflag = 1;
+  esym(searchtet, neightet);
+  if (issubface(neightet)) {
+    if (shlist != NULL) {
+      tspivot(neightet, checksh);
+      if (!sinfected(checksh)) {
+        // Collect this subface (link edge).
+        sinfected(checksh);
+        shlist->newindex((void **) &parysh);
+        *parysh = checksh;
+      }
+    } 
+    if (!fullstar) {
+      collectflag = 0;
+    }
+  }
+  if (collectflag) {
+    fsymself(neightet); // Goto the adj tet of this face.
+    esymself(neightet); // Goto the oppo face of this vertex.
+    // assert(oppo(neightet) == searchpt);
+    infect(neightet); // Collect this tet (link face).
+    tetlist->newindex((void **) &parytet);
+    *parytet = neightet;
+    if (vertlist != NULL) {
+      // Collect its apex.
+      pt = apex(neightet);
+      pinfect(pt);
+      vertlist->newindex((void **) &parypt);
+      *parypt = pt;
+    }
+  } // if (collectflag)
+
+  // Continue to collect all tets in the star.
+  for (i = 0; i < tetlist->objects; i++) {
+    searchtet = * (triface *) fastlookup(tetlist, i);
+    // Note that 'searchtet' is a face opposite to 'searchpt', and the neighbor
+    //   tet at the current edge is already collected.
+    // Check the neighbors at the other two edges of this face.
+    for (j = 0; j < 2; j++) {
+      collectflag = 1;
+      enextself(searchtet);
+      esym(searchtet, neightet);
+      if (issubface(neightet)) {
+        if (shlist != NULL) {
+          tspivot(neightet, checksh);
+          if (!sinfected(checksh)) {
+            // Collect this subface (link edge).
+            sinfected(checksh);
+            shlist->newindex((void **) &parysh);
+            *parysh = checksh;
+          }
+        }
+        if (!fullstar) {
+          collectflag = 0;
+        }
+      }
+      if (collectflag) {
+        fsymself(neightet);
+        if (!infected(neightet)) {
+          esymself(neightet); // Go to the face opposite to 'searchpt'.
+          infect(neightet);
+          tetlist->newindex((void **) &parytet);
+          *parytet = neightet;
+          if (vertlist != NULL) {
+            // Check if a vertex is collected.
+            pt = apex(neightet);
+            if (!pinfected(pt)) {
+              pinfect(pt);
+              vertlist->newindex((void **) &parypt);
+              *parypt = pt;
+            }
+          }
+        } // if (!infected(neightet))
+      } // if (collectflag)
+    } // j
+  } // i
+
+
+  // Uninfect the list of tets and vertices.
+  for (i = 0; i < tetlist->objects; i++) {
+    parytet = (triface *) fastlookup(tetlist, i);
+    uninfect(*parytet);
+  }
+
+  if (vertlist != NULL) {
+    for (i = 0; i < vertlist->objects; i++) {
+      parypt = (point *) fastlookup(vertlist, i);
+      puninfect(*parypt);
+    }
+  }
+
+  if (shlist != NULL) {
+    for (i = 0; i < shlist->objects; i++) {
+      parysh = (face *) fastlookup(shlist, i);
+      suninfect(*parysh);
+    }
+  }
+
+  return (int) tetlist->objects;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// getedge()    Get a tetrahedron having the two endpoints.                  //
+//                                                                           //
+// The method here is to search the second vertex in the link faces of the   //
+// first vertex. The global array 'cavetetlist' is re-used for searching.    //
+//                                                                           //
+// This function is used for the case when the mesh is non-convex. Otherwise,//
+// the function finddirection() should be faster than this.                  //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+int tetgenmesh::getedge(point e1, point e2, triface *tedge)
+{
+  triface searchtet, neightet, *parytet;
+  point pt;
+  int done;
+  int i, j;
+
+  if (b->verbose > 2) {
+    printf("      Get edge from %d to %d.\n", pointmark(e1), pointmark(e2));
+  }
+
+  // Quickly check if 'tedge' is just this edge.
+  if (!isdeadtet(*tedge)) {
+    if (org(*tedge) == e1) {
+      if (dest(*tedge) == e2) {
+        return 1;
+      }
+    } else if (org(*tedge) == e2) {
+      if (dest(*tedge) == e1) {
+        esymself(*tedge);
+        return 1;
+      }
+    }
+  }
+
+  // Search for the edge [e1, e2].
+  point2tetorg(e1, *tedge);
+  finddirection(tedge, e2);
+  if (dest(*tedge) == e2) {
+    return 1;
+  } else {
+    // Search for the edge [e2, e1].
+    point2tetorg(e2, *tedge);
+    finddirection(tedge, e1);
+    if (dest(*tedge) == e1) {
+      esymself(*tedge);
+      return 1;
+    }
+  }
+
+
+  // Go to the link face of e1.
+  point2tetorg(e1, searchtet);
+  enextesymself(searchtet);
+  arraypool *tetlist = cavebdrylist;
+
+  // Search e2.
+  for (i = 0; i < 3; i++) {
+    pt = apex(searchtet);
+    if (pt == e2) {
+      // Found. 'searchtet' is [#,#,e2,e1].
+      eorgoppo(searchtet, *tedge); // [e1,e2,#,#].
+      return 1;
+    }
+    enextself(searchtet);
+  }
+
+  // Get the adjacent link face at 'searchtet'.
+  fnext(searchtet, neightet);
+  esymself(neightet);
+  // assert(oppo(neightet) == e1);
+  pt = apex(neightet);
+  if (pt == e2) {
+    // Found. 'neightet' is [#,#,e2,e1].
+    eorgoppo(neightet, *tedge); // [e1,e2,#,#].
+    return 1;
+  }
+
+  // Continue searching in the link face of e1.
+  infect(searchtet);
+  tetlist->newindex((void **) &parytet);
+  *parytet = searchtet;
+  infect(neightet);
+  tetlist->newindex((void **) &parytet);
+  *parytet = neightet;
+
+  done = 0;
+
+  for (i = 0; (i < tetlist->objects) && !done; i++) {
+    parytet = (triface *) fastlookup(tetlist, i);
+    searchtet = *parytet;
+    for (j = 0; (j < 2) && !done; j++) {
+      enextself(searchtet);
+      fnext(searchtet, neightet);
+      if (!infected(neightet)) {        
+        esymself(neightet);
+        pt = apex(neightet);
+        if (pt == e2) {
+          // Found. 'neightet' is [#,#,e2,e1].
+          eorgoppo(neightet, *tedge);
+          done = 1;
+        } else {
+          infect(neightet);
+          tetlist->newindex((void **) &parytet);
+          *parytet = neightet;
+        }
+      }
+    } // j
+  } // i 
+
+  // Uninfect the list of visited tets.
+  for (i = 0; i < tetlist->objects; i++) {
+    parytet = (triface *) fastlookup(tetlist, i);
+    uninfect(*parytet);
+  }
+  tetlist->restart();
+
+  return done;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// reduceedgesatvertex()    Reduce the number of edges at a given vertex.    //
+//                                                                           //
+// 'endptlist' contains the endpoints of edges connecting at the vertex.     //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+int tetgenmesh::reduceedgesatvertex(point startpt, arraypool* endptlist)
+{
+  triface searchtet;
+  point *pendpt, *parypt;
+  enum interresult dir;
+  flipconstraints fc;
+  int reduceflag;
+  int count;
+  int n, i, j;
+
+
+  fc.remvert = startpt;
+  fc.checkflipeligibility = 1;
+
+  while (1) {
+
+    count = 0;
+
+    for (i = 0; i < endptlist->objects; i++) {
+      pendpt = (point *) fastlookup(endptlist, i);
+      if (*pendpt == dummypoint) {
+        continue; // Do not reduce a virtual edge.
+      }
+      reduceflag = 0;
+      // Find the edge.
+      if (nonconvex) {
+        if (getedge(startpt, *pendpt, &searchtet)) {
+          dir = ACROSSVERT;
+        } else {
+          // The edge does not exist (was flipped).
+          dir = INTERSECT;
+        }
+      } else {
+        point2tetorg(startpt, searchtet);
+        dir = finddirection(&searchtet, *pendpt);
+      }
+      if (dir == ACROSSVERT) {
+        if (dest(searchtet) == *pendpt) {
+          // Do not flip a segment.
+          if (!issubseg(searchtet)) {
+            n = removeedgebyflips(&searchtet, &fc);
+            if (n == 2) {
+              reduceflag = 1;
+            }
+          }
+        }
+      } else {
+        // The edge has been flipped.
+        reduceflag = 1;
+      }
+      if (reduceflag) {
+        count++;
+        // Move the last vertex into this slot.
+        j = endptlist->objects - 1;
+        parypt = (point *) fastlookup(endptlist, j);
+        *pendpt = *parypt;
+        endptlist->objects--;
+        i--;
+      }
+    } // i
+
+    if (count == 0) {
+      // No edge is reduced.
+      break;
+    }
+
+  } // while (1)
+
+  return (int) endptlist->objects;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// removevertexbyflips()    Remove a vertex by flips.                        //
+//                                                                           //
+// This routine attempts to remove the given vertex 'rempt' (p) from the     //
+// tetrahedralization (T) by a sequence of flips.                            //
+//                                                                           //
+// The algorithm used here is a simple edge reduce method. Suppose there are //
+// n edges connected at p. We try to reduce the number of edges by flipping  //
+// any edge (not a segment) that is connecting at p.                         //
+//                                                                           //
+// Unless T is a Delaunay tetrahedralization, there is no guarantee that 'p' //
+// can be successfully removed.                                              //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+int tetgenmesh::removevertexbyflips(point steinerpt)
+{
+  triface *fliptets = NULL, wrktets[4];
+  triface searchtet, spintet, neightet;
+  face parentsh, spinsh, checksh;
+  face leftseg, rightseg, checkseg;
+  point lpt = NULL, rpt = NULL, apexpt; //, *parypt;
+  flipconstraints fc;
+  enum verttype vt;
+  enum locateresult loc;
+  int valence, removeflag;
+  int slawson;
+  int t1ver;
+  int n, i;
+
+  vt = pointtype(steinerpt);
+
+  if (vt == FREESEGVERTEX) {
+    sdecode(point2sh(steinerpt), leftseg);
+    leftseg.shver = 0;
+    if (sdest(leftseg) == steinerpt) {
+      senext(leftseg, rightseg);
+      spivotself(rightseg);
+      rightseg.shver = 0;
+    } else {
+      rightseg = leftseg;
+      senext2(rightseg, leftseg);
+      spivotself(leftseg);
+      leftseg.shver = 0;
+    }
+    lpt = sorg(leftseg);
+    rpt = sdest(rightseg);
+    if (b->verbose > 2) {
+      printf("      Removing Steiner point %d in segment (%d, %d).\n",
+             pointmark(steinerpt), pointmark(lpt), pointmark(rpt));
+
+    }
+  } else if (vt == FREEFACETVERTEX) {
+    if (b->verbose > 2) {
+      printf("      Removing Steiner point %d in facet.\n",
+             pointmark(steinerpt));
+    }
+  } else if (vt == FREEVOLVERTEX) {
+    if (b->verbose > 2) {
+      printf("      Removing Steiner point %d in volume.\n",
+             pointmark(steinerpt));
+    }
+  } else if (vt == VOLVERTEX) {
+    if (b->verbose > 2) {
+      printf("      Removing a point %d in volume.\n",
+             pointmark(steinerpt));
+    }
+  } else {
+    // It is not a Steiner point.
+    return 0;
+  }
+
+  // Try to reduce the number of edges at 'p' by flips.
+  getvertexstar(1, steinerpt, cavetetlist, cavetetvertlist, NULL);
+  cavetetlist->restart(); // This list may be re-used.
+  if (cavetetvertlist->objects > 3l) {
+    valence = reduceedgesatvertex(steinerpt, cavetetvertlist);
+  } else {
+    valence = cavetetvertlist->objects;
+  }
+  cavetetvertlist->restart();
+
+  removeflag = 0;
+
+  if (valence == 4) {
+    // Only 4 vertices (4 tets) left! 'p' is inside the convex hull of the 4
+    //   vertices. This case is due to that 'p' is not exactly on the segment.
+    point2tetorg(steinerpt, searchtet);
+    loc = INTETRAHEDRON;
+    removeflag = 1;
+  } else if (valence == 5) {
+    // There are 5 edges.
+    if (vt == FREESEGVERTEX) {
+      sstpivot1(leftseg, searchtet);
+      if (org(searchtet) != steinerpt) {
+        esymself(searchtet);
+      }
+      i = 0; // Count the numbe of tet at the edge [p,lpt].
+      neightet.tet = NULL; // Init the face.
+      spintet = searchtet;
+      while (1) {
+        i++;
+        if (apex(spintet) == rpt) {
+          // Remember the face containing the edge [lpt, rpt].
+          neightet = spintet;
+        }
+        fnextself(spintet);
+        if (spintet.tet == searchtet.tet) break;
+      }
+      if (i == 3) {
+        // This case has been checked below.
+      } else if (i == 4) {
+        // There are 4 tets sharing at [p,lpt]. There must be 4 tets sharing
+        //   at [p,rpt].  There must be a face [p, lpt, rpt].  
+        if (apex(neightet) == rpt) {
+          // The edge (segment) has been already recovered!  
+          // Check if a 6-to-2 flip is possible (to remove 'p').
+          // Let 'searchtet' be [p,d,a,b]
+          esym(neightet, searchtet);
+          enextself(searchtet);
+          // Check if there are exactly three tets at edge [p,d].
+          wrktets[0] = searchtet; // [p,d,a,b]
+          for (i = 0; i < 2; i++) {
+            fnext(wrktets[i], wrktets[i+1]); // [p,d,b,c], [p,d,c,a]
+          }
+          if (apex(wrktets[0]) == oppo(wrktets[2])) {
+            loc = ONFACE;
+            removeflag = 1;
+          }
+        }
+      }
+    } else if (vt == FREEFACETVERTEX) {
+      // It is possible to do a 6-to-2 flip to remove the vertex.
+      point2tetorg(steinerpt, searchtet);
+      // Get the three faces of 'searchtet' which share at p.
+      //    All faces has p as origin.
+      wrktets[0] = searchtet;
+      wrktets[1] = searchtet;
+      esymself(wrktets[1]);
+      enextself(wrktets[1]);
+      wrktets[2] = searchtet;
+      eprevself(wrktets[2]);
+      esymself(wrktets[2]);
+      // All internal edges of the six tets have valance either 3 or 4.
+      // Get one edge which has valance 3.
+      searchtet.tet = NULL;
+      for (i = 0; i < 3; i++) {
+        spintet = wrktets[i];
+        valence = 0;
+        while (1) {
+          valence++;
+          fnextself(spintet);
+          if (spintet.tet == wrktets[i].tet) break;
+        }
+        if (valence == 3) {
+          // Found the edge.
+          searchtet = wrktets[i];
+          break;
+        }
+      }
+      // Note, we do not detach the three subfaces at p.
+      // They will be removed within a 4-to-1 flip.
+      loc = ONFACE;
+      removeflag = 1;
+    }
+    //removeflag = 1;
+  } 
+
+  if (!removeflag) {
+    if (vt == FREESEGVERTEX) { 
+      // Check is it possible to recover the edge [lpt,rpt].
+      // The condition to check is:  Whether each tet containing 'leftseg' is
+      //   adjacent to a tet containing 'rightseg'.
+      sstpivot1(leftseg, searchtet);
+      if (org(searchtet) != steinerpt) {
+        esymself(searchtet);
+      }
+      spintet = searchtet;
+      while (1) {
+        // Go to the bottom face of this tet.
+        eprev(spintet, neightet);
+        esymself(neightet);  // [steinerpt, p1, p2, lpt]
+        // Get the adjacent tet.
+        fsymself(neightet);  // [p1, steinerpt, p2, rpt]
+        if (oppo(neightet) != rpt) {
+          // Found a non-matching adjacent tet.
+          break;
+        }
+        fnextself(spintet);
+        if (spintet.tet == searchtet.tet) {
+          // 'searchtet' is [p,d,p1,p2].
+          loc = ONEDGE;
+          removeflag = 1;
+          break;
+        }
+      }
+    } // if (vt == FREESEGVERTEX)
+  }
+
+  if (!removeflag) {
+    if (vt == FREESEGVERTEX) {
+      // Check if the edge [lpt, rpt] exists.
+      if (getedge(lpt, rpt, &searchtet)) {
+        // We have recovered this edge. Shift the vertex into the volume.
+        // We can recover this edge if the subfaces are not recovered yet.
+        if (!checksubfaceflag) {
+          // Remove the vertex from the surface mesh.
+          //   This will re-create the segment [lpt, rpt] and re-triangulate
+          //   all the facets at the segment.
+          // Detach the subsegments from their surrounding tets.
+          for (i = 0; i < 2; i++) {
+            checkseg = (i == 0) ? leftseg : rightseg;
+            sstpivot1(checkseg, neightet);
+            spintet = neightet;
+            while (1) {
+              tssdissolve1(spintet);
+              fnextself(spintet);
+              if (spintet.tet == neightet.tet) break;
+            }
+            sstdissolve1(checkseg);
+          } // i
+          slawson = 1; // Do lawson flip after removal.
+          spivot(rightseg, parentsh); // 'rightseg' has p as its origin.
+          sremovevertex(steinerpt, &parentsh, &rightseg, slawson);
+          // Clear the list for new subfaces.
+          caveshbdlist->restart();
+          // Insert the new segment.
+          sstbond1(rightseg, searchtet);
+          spintet = searchtet;
+          while (1) {
+            tssbond1(spintet, rightseg);
+            fnextself(spintet);
+            if (spintet.tet == searchtet.tet) break;
+          }
+          // The Steiner point has been shifted into the volume.
+          setpointtype(steinerpt, FREEVOLVERTEX);          
+          st_segref_count--;
+          st_volref_count++;
+          return 1;
+        } // if (!checksubfaceflag)
+      } // if (getedge(...))
+    } // if (vt == FREESEGVERTEX)
+  } // if (!removeflag)
+
+  if (!removeflag) {
+    return 0;
+  }
+
+  if (vt == FREESEGVERTEX) {
+    // Detach the subsegments from their surronding tets.
+    for (i = 0; i < 2; i++) {
+      checkseg = (i == 0) ? leftseg : rightseg;
+      sstpivot1(checkseg, neightet);
+      spintet = neightet;
+      while (1) {
+        tssdissolve1(spintet);
+        fnextself(spintet);
+        if (spintet.tet == neightet.tet) break;
+      }
+      sstdissolve1(checkseg);
+    } // i
+    if (checksubfaceflag) {
+      // Detach the subfaces at the subsegments from their attached tets.
+      for (i = 0; i < 2; i++) {
+        checkseg = (i == 0) ? leftseg : rightseg;
+        spivot(checkseg, parentsh);
+        if (parentsh.sh != NULL) {
+          spinsh = parentsh;
+          while (1) {
+            stpivot(spinsh, neightet);
+            if (neightet.tet != NULL) {
+              tsdissolve(neightet);
+            }
+            sesymself(spinsh);
+            stpivot(spinsh, neightet);
+            if (neightet.tet != NULL) {
+              tsdissolve(neightet);
+            }
+            stdissolve(spinsh);
+            spivotself(spinsh); // Go to the next subface.
+            if (spinsh.sh == parentsh.sh) break;
+          }
+        }
+      } // i
+    } // if (checksubfaceflag)
+  }
+
+  if (loc == INTETRAHEDRON) {
+    // Collect the four tets containing 'p'.
+    fliptets = new triface[4];
+    fliptets[0] = searchtet; // [p,d,a,b]
+    for (i = 0; i < 2; i++) {
+      fnext(fliptets[i], fliptets[i+1]); // [p,d,b,c], [p,d,c,a]
+    }
+    eprev(fliptets[0], fliptets[3]);
+    fnextself(fliptets[3]); // it is [a,p,b,c]
+    eprevself(fliptets[3]);
+    esymself(fliptets[3]); // [a,b,c,p].
+    // Remove p by a 4-to-1 flip.
+    //flip41(fliptets, 1, 0, 0);
+    flip41(fliptets, 1, &fc);
+    //recenttet = fliptets[0];
+  } else if (loc == ONFACE) {
+    // Let the original two tets be [a,b,c,d] and [b,a,c,e]. And p is in
+    //   face [a,b,c].  Let 'searchtet' be the tet [p,d,a,b].
+    // Collect the six tets containing 'p'.
+    fliptets = new triface[6];
+    fliptets[0] = searchtet; // [p,d,a,b]
+    for (i = 0; i < 2; i++) {
+      fnext(fliptets[i], fliptets[i+1]); // [p,d,b,c], [p,d,c,a]
+    }
+    eprev(fliptets[0], fliptets[3]);
+    fnextself(fliptets[3]); // [a,p,b,e]
+    esymself(fliptets[3]);  // [p,a,e,b]
+    eprevself(fliptets[3]); // [e,p,a,b]
+    for (i = 3; i < 5; i++) {
+      fnext(fliptets[i], fliptets[i+1]); // [e,p,b,c], [e,p,c,a]
+    }
+    if (vt == FREEFACETVERTEX) {
+      // We need to determine the location of three subfaces at p.
+      valence = 0; // Re-use it.
+      // Check if subfaces are all located in the lower three tets.
+      //   i.e., [e,p,a,b], [e,p,b,c], and [e,p,c,a].
+      for (i = 3; i < 6; i++) {
+        if (issubface(fliptets[i])) valence++;
+      }
+      if (valence > 0) {
+        // We must do 3-to-2 flip in the upper part. We simply re-arrange
+        //   the six tets.
+        for (i = 0; i < 3; i++) {
+          esym(fliptets[i+3], wrktets[i]);
+          esym(fliptets[i], fliptets[i+3]);
+          fliptets[i] = wrktets[i];
+        }
+        // Swap the last two pairs, i.e., [1]<->[[2], and [4]<->[5]
+        wrktets[1] = fliptets[1];
+        fliptets[1] = fliptets[2];
+        fliptets[2] = wrktets[1];
+        wrktets[1] = fliptets[4];
+        fliptets[4] = fliptets[5];
+        fliptets[5] = wrktets[1];
+      }
+    }
+    // Remove p by a 6-to-2 flip, which is a combination of two flips:
+    //   a 3-to-2 (deletes the edge [e,p]), and
+    //   a 4-to-1 (deletes the vertex p).
+    // First do a 3-to-2 flip on [e,p,a,b],[e,p,b,c],[e,p,c,a]. It creates
+    //   two new tets: [a,b,c,p] and [b,a,c,e].  The new tet [a,b,c,p] is
+    //   degenerate (has zero volume). It will be deleted in the followed
+    //   4-to-1 flip.
+    //flip32(&(fliptets[3]), 1, 0, 0);
+    flip32(&(fliptets[3]), 1, &fc);
+    // Second do a 4-to-1 flip on [p,d,a,b],[p,d,b,c],[p,d,c,a],[a,b,c,p].
+    //   This creates a new tet [a,b,c,d].
+    //flip41(fliptets, 1, 0, 0);
+    flip41(fliptets, 1, &fc);
+    //recenttet = fliptets[0];
+  } else if (loc == ONEDGE) {
+    // Let the original edge be [e,d] and p is in [e,d]. Assume there are n
+    //   tets sharing at edge [e,d] originally.  We number the link vertices
+    //   of [e,d]: p_0, p_1, ..., p_n-1. 'searchtet' is [p,d,p_0,p_1].
+    // Count the number of tets at edge [e,p] and [p,d] (this is n).
+    n = 0;
+    spintet = searchtet;
+    while (1) {
+      n++;
+      fnextself(spintet);
+      if (spintet.tet == searchtet.tet) break;
+    }
+    // Collect the 2n tets containing 'p'.
+    fliptets = new triface[2 * n];
+    fliptets[0] = searchtet; // [p,b,p_0,p_1] 
+    for (i = 0; i < (n - 1); i++) {
+      fnext(fliptets[i], fliptets[i+1]); // [p,d,p_i,p_i+1].
+    }
+    eprev(fliptets[0], fliptets[n]);
+    fnextself(fliptets[n]); // [p_0,p,p_1,e]
+    esymself(fliptets[n]);  // [p,p_0,e,p_1]
+    eprevself(fliptets[n]); // [e,p,p_0,p_1]
+    for (i = n; i <  (2 * n - 1); i++) {
+      fnext(fliptets[i], fliptets[i+1]); // [e,p,p_i,p_i+1].
+    }
+    // Remove p by a 2n-to-n flip, it is a sequence of n flips:
+    // - Do a 2-to-3 flip on 
+    //     [p_0,p_1,p,d] and 
+    //     [p,p_1,p_0,e].
+    //   This produces: 
+    //     [e,d,p_0,p_1], 
+    //     [e,d,p_1,p] (degenerated), and 
+    //     [e,d,p,p_0] (degenerated).
+    wrktets[0] = fliptets[0]; // [p,d,p_0,p_1]
+    eprevself(wrktets[0]);    // [p_0,p,d,p_1]
+    esymself(wrktets[0]);     // [p,p_0,p_1,d]
+    enextself(wrktets[0]);    // [p_0,p_1,p,d] [0]
+    wrktets[1] = fliptets[n]; // [e,p,p_0,p_1]
+    enextself(wrktets[1]);    // [p,p_0,e,p_1]
+    esymself(wrktets[1]);     // [p_0,p,p_1,e]
+    eprevself(wrktets[1]);    // [p_1,p_0,p,e] [1]
+    //flip23(wrktets, 1, 0, 0);
+    flip23(wrktets, 1, &fc);
+    // Save the new tet [e,d,p,p_0] (degenerated).
+    fliptets[n] = wrktets[2];
+    // Save the new tet [e,d,p_0,p_1].
+    fliptets[0] = wrktets[0];
+    // - Repeat from i = 1 to n-2: (n - 2) flips
+    //   - Do a 3-to-2 flip on 
+    //       [p,p_i,d,e], 
+    //       [p,p_i,e,p_i+1], and 
+    //       [p,p_i,p_i+1,d]. 
+    //     This produces: 
+    //       [d,e,p_i+1,p_i], and
+    //       [e,d,p_i+1,p] (degenerated).
+    for (i = 1; i < (n - 1); i++) {
+      wrktets[0] = wrktets[1]; // [e,d,p_i,p] (degenerated).
+      enextself(wrktets[0]);   // [d,p_i,e,p] (...)
+      esymself(wrktets[0]);    // [p_i,d,p,e] (...) 
+      eprevself(wrktets[0]);   // [p,p_i,d,e] (degenerated) [0].
+      wrktets[1] = fliptets[n+i];  // [e,p,p_i,p_i+1]
+      enextself(wrktets[1]);       // [p,p_i,e,p_i+1] [1]
+      wrktets[2] = fliptets[i]; // [p,d,p_i,p_i+1]
+      eprevself(wrktets[2]);    // [p_i,p,d,p_i+1]
+      esymself(wrktets[2]);     // [p,p_i,p_i+1,d] [2]
+      //flip32(wrktets, 1, 0, 0);
+      flip32(wrktets, 1, &fc);
+      // Save the new tet [e,d,p_i,p_i+1].         // FOR DEBUG ONLY
+      fliptets[i] = wrktets[0]; // [d,e,p_i+1,p_i] // FOR DEBUG ONLY
+      esymself(fliptets[i]);    // [e,d,p_i,p_i+1] // FOR DEBUG ONLY
+    }
+    // - Do a 4-to-1 flip on 
+    //     [p,p_0,e,d],     [d,e,p_0,p],
+    //     [p,p_0,d,p_n-1], [e,p_n-1,p_0,p], 
+    //     [p,p_0,p_n-1,e], [p_0,p_n-1,d,p], and
+    //     [e,d,p_n-1,p]. 
+    //   This produces 
+    //     [e,d,p_n-1,p_0] and 
+    //     deletes p.
+    wrktets[3] = wrktets[1];  // [e,d,p_n-1,p] (degenerated) [3]
+    wrktets[0] = fliptets[n]; // [e,d,p,p_0] (degenerated)
+    eprevself(wrktets[0]);    // [p,e,d,p_0] (...)
+    esymself(wrktets[0]);     // [e,p,p_0,d] (...)
+    enextself(wrktets[0]);    // [p,p_0,e,d] (degenerated) [0]
+    wrktets[1] = fliptets[n-1];   // [p,d,p_n-1,p_0]
+    esymself(wrktets[1]);         // [d,p,p_0,p_n-1]
+    enextself(wrktets[1]);        // [p,p_0,d,p_n-1] [1]
+    wrktets[2] = fliptets[2*n-1]; // [e,p,p_n-1,p_0]
+    enextself(wrktets[2]);        // [p_p_n-1,e,p_0]
+    esymself(wrktets[2]);         // [p_n-1,p,p_0,e]
+    enextself(wrktets[2]);        // [p,p_0,p_n-1,e] [2]
+    //flip41(wrktets, 1, 0, 0);
+    flip41(wrktets, 1, &fc);
+    // Save the new tet [e,d,p_n-1,p_0]             // FOR DEBUG ONLY
+    fliptets[n-1] = wrktets[0];  // [e,d,p_n-1,p_0] // FOR DEBUG ONLY
+    //recenttet = fliptets[0];
+  }
+
+  delete [] fliptets;
+
+  if (vt == FREESEGVERTEX) {
+    // Remove the vertex from the surface mesh.
+    //   This will re-create the segment [lpt, rpt] and re-triangulate
+    //   all the facets at the segment.
+    // Only do lawson flip when subfaces are not recovery yet.
+    slawson = (checksubfaceflag ? 0 : 1);
+    spivot(rightseg, parentsh); // 'rightseg' has p as its origin.
+    sremovevertex(steinerpt, &parentsh, &rightseg, slawson);
+
+    // The original segment is returned in 'rightseg'. 
+    rightseg.shver = 0;
+    // Insert the new segment.
+    point2tetorg(lpt, searchtet);
+    finddirection(&searchtet, rpt);
+    sstbond1(rightseg, searchtet);
+    spintet = searchtet;
+    while (1) {
+      tssbond1(spintet, rightseg);
+      fnextself(spintet);
+      if (spintet.tet == searchtet.tet) break;
+    }
+
+    if (checksubfaceflag) {
+      // Insert subfaces at segment [lpt,rpt] into the tetrahedralization.
+      spivot(rightseg, parentsh);
+      if (parentsh.sh != NULL) {
+        spinsh = parentsh;
+        while (1) {
+          if (sorg(spinsh) != lpt) {
+            sesymself(spinsh);
+          }
+          apexpt = sapex(spinsh);
+          // Find the adjacent tet of [lpt,rpt,apexpt];
+          spintet = searchtet;
+          while (1) {
+            if (apex(spintet) == apexpt) {
+              tsbond(spintet, spinsh);
+              sesymself(spinsh); // Get to another side of this face.
+              fsym(spintet, neightet);
+              tsbond(neightet, spinsh);
+              sesymself(spinsh); // Get back to the original side.
+              break;
+            }
+            fnextself(spintet);
+          }
+          spivotself(spinsh);
+          if (spinsh.sh == parentsh.sh) break;
+        }
+      }
+    } // if (checksubfaceflag)
+
+    // Clear the set of new subfaces.
+    caveshbdlist->restart();
+  } // if (vt == FREESEGVERTEX)
+
+  // The point has been removed.
+  if (pointtype(steinerpt) != UNUSEDVERTEX) {
+    setpointtype(steinerpt, UNUSEDVERTEX);
+    unuverts++;
+  }
+  if (vt != VOLVERTEX) {
+    // Update the correspinding counters.
+    if (vt == FREESEGVERTEX) {
+      st_segref_count--;
+    } else if (vt == FREEFACETVERTEX) {
+      st_facref_count--;
+    } else if (vt == FREEVOLVERTEX) {
+      st_volref_count--;
+    }
+    if (steinerleft > 0) steinerleft++;
+  }
+
+  return 1;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// suppressbdrysteinerpoint()    Suppress a boundary Steiner point           //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+int tetgenmesh::suppressbdrysteinerpoint(point steinerpt)
+{
+  face parentsh, spinsh, *parysh;
+  face leftseg, rightseg;
+  point lpt = NULL, rpt = NULL;
+  int i;
+
+  verttype vt = pointtype(steinerpt);
+
+  if (vt == FREESEGVERTEX) {
+    sdecode(point2sh(steinerpt), leftseg);
+    leftseg.shver = 0;
+    if (sdest(leftseg) == steinerpt) {
+      senext(leftseg, rightseg);
+      spivotself(rightseg);
+      rightseg.shver = 0;
+    } else {
+      rightseg = leftseg;
+      senext2(rightseg, leftseg);
+      spivotself(leftseg);
+      leftseg.shver = 0;
+    }
+    lpt = sorg(leftseg);
+    rpt = sdest(rightseg);
+    if (b->verbose > 2) {
+      printf("      Suppressing Steiner point %d in segment (%d, %d).\n",
+             pointmark(steinerpt), pointmark(lpt), pointmark(rpt));
+    }
+    // Get all subfaces at the left segment [lpt, steinerpt].
+    spivot(leftseg, parentsh);
+    if (parentsh.sh != NULL) {
+      // It is not a dangling segment.
+      spinsh = parentsh;
+      while (1) {
+        cavesegshlist->newindex((void **) &parysh);
+        *parysh = spinsh;
+        // Orient the face consistently. 
+        if (sorg(*parysh)!= sorg(parentsh)) sesymself(*parysh);
+        spivotself(spinsh);
+        if (spinsh.sh == NULL) break;
+        if (spinsh.sh == parentsh.sh) break;
+      }
+    }
+    if (cavesegshlist->objects < 2) {
+      // It is a single segment. Not handle it yet.
+      cavesegshlist->restart();
+      return 0;
+    }
+  } else if (vt == FREEFACETVERTEX) {
+    if (b->verbose > 2) {
+      printf("      Suppressing Steiner point %d from facet.\n",
+             pointmark(steinerpt));
+    }
+    sdecode(point2sh(steinerpt), parentsh);
+    // A facet Steiner point. There are exactly two sectors.
+    for (i = 0; i < 2; i++) {
+      cavesegshlist->newindex((void **) &parysh);
+      *parysh = parentsh;
+      sesymself(parentsh);
+    }
+  } else {
+    return 0;
+  }
+
+  triface searchtet, neightet, *parytet;
+  point pa, pb, pc, pd;
+  REAL v1[3], v2[3], len, u;
+
+  REAL startpt[3] = {0,}, samplept[3] = {0,}, candpt[3] = {0,};
+  REAL ori, minvol, smallvol;
+  int samplesize;
+  int it, j, k;
+
+  int n = (int) cavesegshlist->objects;
+  point *newsteiners = new point[n];
+  for (i = 0; i < n; i++) newsteiners[i] = NULL;
+
+  // Search for each sector an interior vertex. 
+  for (i = 0; i < cavesegshlist->objects; i++) {
+    parysh = (face *) fastlookup(cavesegshlist, i);
+    stpivot(*parysh, searchtet);
+    // Skip it if it is outside.
+    if (ishulltet(searchtet)) continue;
+    // Get the "half-ball". Tets in 'cavetetlist' all contain 'steinerpt' as
+    //   opposite.  Subfaces in 'caveshlist' all contain 'steinerpt' as apex.
+    //   Moreover, subfaces are oriented towards the interior of the ball.
+    setpoint2tet(steinerpt, encode(searchtet));
+    getvertexstar(0, steinerpt, cavetetlist, NULL, caveshlist);
+    // Calculate the searching vector.
+    pa = sorg(*parysh);
+    pb = sdest(*parysh);
+    pc = sapex(*parysh);
+    facenormal(pa, pb, pc, v1, 1, NULL);
+    len = sqrt(dot(v1, v1));
+    v1[0] /= len;
+    v1[1] /= len;
+    v1[2] /= len;
+    if (vt == FREESEGVERTEX) {
+      parysh = (face *) fastlookup(cavesegshlist, (i + 1) % n);
+      pd = sapex(*parysh);
+      facenormal(pb, pa, pd, v2, 1, NULL);
+      len = sqrt(dot(v2, v2));
+      v2[0] /= len;
+      v2[1] /= len;
+      v2[2] /= len;
+      // Average the two vectors.
+      v1[0] = 0.5 * (v1[0] + v2[0]);
+      v1[1] = 0.5 * (v1[1] + v2[1]);
+      v1[2] = 0.5 * (v1[2] + v2[2]);
+    }
+    // Search the intersection of the ray starting from 'steinerpt' to
+    //   the search direction 'v1' and the shell of the half-ball.
+    // - Construct an endpoint.
+    len = distance(pa, pb);
+    v2[0] = steinerpt[0] + len * v1[0];
+    v2[1] = steinerpt[1] + len * v1[1];
+    v2[2] = steinerpt[2] + len * v1[2];
+    for (j = 0; j < cavetetlist->objects; j++) {
+      parytet = (triface *) fastlookup(cavetetlist, j);
+      pa = org(*parytet);
+      pb = dest(*parytet);
+      pc = apex(*parytet);
+      // Test if the ray startpt->v2 lies in the cone: where 'steinerpt'
+      //   is the apex, and three sides are defined by the triangle 
+      //   [pa, pb, pc].
+      ori = orient3d(steinerpt, pa, pb, v2);
+      if (ori >= 0) {
+        ori = orient3d(steinerpt, pb, pc, v2);
+        if (ori >= 0) {
+          ori = orient3d(steinerpt, pc, pa, v2);
+          if (ori >= 0) {
+            // Found! Calculate the intersection.
+            planelineint(pa, pb, pc, steinerpt, v2, startpt, &u);
+            break;
+          }
+        }
+      }
+    } // j
+    // Close the ball by adding the subfaces.
+    for (j = 0; j < caveshlist->objects; j++) {
+      parysh = (face *) fastlookup(caveshlist, j);
+      stpivot(*parysh, neightet);
+      cavetetlist->newindex((void **) &parytet);
+      *parytet = neightet;
+    }
+    // Search a best point inside the segment [startpt, steinerpt].
+    it = 0;
+    samplesize = 100;
+    v1[0] = steinerpt[0] - startpt[0];
+    v1[1] = steinerpt[1] - startpt[1];
+    v1[2] = steinerpt[2] - startpt[2];
+    minvol = -1.0;
+    while (it < 3) {
+      for (j = 1; j < samplesize - 1; j++) {
+        samplept[0] = startpt[0] + ((REAL) j / (REAL) samplesize) * v1[0];
+        samplept[1] = startpt[1] + ((REAL) j / (REAL) samplesize) * v1[1];
+        samplept[2] = startpt[2] + ((REAL) j / (REAL) samplesize) * v1[2];
+        // Find the minimum volume for 'samplept'.
+        smallvol = -1;
+        for (k = 0; k < cavetetlist->objects; k++) {
+          parytet = (triface *) fastlookup(cavetetlist, k);
+          pa = org(*parytet);
+          pb = dest(*parytet);
+          pc = apex(*parytet);
+          ori = orient3d(pb, pa, pc, samplept);
+          if (ori <= 0) {
+            break; // An invalid tet.
+          }
+          if (smallvol == -1) {
+            smallvol = ori;
+          } else {
+            if (ori < smallvol) smallvol = ori;
+          }
+        } // k
+        if (k == cavetetlist->objects) {
+          // Found a valid point. Remember it.
+          if (minvol == -1.0) {
+            candpt[0] = samplept[0];
+            candpt[1] = samplept[1];
+            candpt[2] = samplept[2];
+            minvol = smallvol;
+          } else {
+            if (minvol < smallvol) {
+              // It is a better location. Remember it.
+              candpt[0] = samplept[0];
+              candpt[1] = samplept[1];
+              candpt[2] = samplept[2];
+              minvol = smallvol;
+            } else {
+              // No improvement of smallest volume. 
+              // Since we are searching along the line [startpt, steinerpy],
+              // The smallest volume can only be decreased later.
+              break;
+            }
+          }
+        }
+      } // j
+      if (minvol > 0) break; 
+      samplesize *= 10;
+      it++;
+    } // while (it < 3)
+    if (minvol == -1.0) {
+      // Failed to find a valid point.
+      cavetetlist->restart();
+      caveshlist->restart();
+      break;
+    }
+    // Create a new Steiner point inside this section.
+    makepoint(&(newsteiners[i]), FREEVOLVERTEX);
+    newsteiners[i][0] = candpt[0];
+    newsteiners[i][1] = candpt[1];
+    newsteiners[i][2] = candpt[2];
+    cavetetlist->restart();
+    caveshlist->restart();
+  } // i
+
+  if (i < cavesegshlist->objects) {
+    // Failed to suppress the vertex.
+    for (; i > 0; i--) {
+      if (newsteiners[i - 1] != NULL) {
+        pointdealloc(newsteiners[i - 1]);
+      }
+    }
+    delete [] newsteiners;
+    cavesegshlist->restart();
+    return 0;
+  }
+
+  // Remove p from the segment or the facet.
+  triface newtet, newface, spintet;
+  face newsh, neighsh;
+  face *splitseg, checkseg;
+  int slawson = 0; // Do not do flip afterword.
+  int t1ver;
+
+  if (vt == FREESEGVERTEX) {
+    // Detach 'leftseg' and 'rightseg' from their adjacent tets.
+    //   These two subsegments will be deleted. 
+    sstpivot1(leftseg, neightet);
+    spintet = neightet;
+    while (1) {
+      tssdissolve1(spintet);
+      fnextself(spintet);
+      if (spintet.tet == neightet.tet) break;
+    }
+    sstpivot1(rightseg, neightet);
+    spintet = neightet;
+    while (1) {
+      tssdissolve1(spintet);
+      fnextself(spintet);
+      if (spintet.tet == neightet.tet) break;
+    }
+  }
+
+  // Loop through all sectors bounded by facets at this segment.
+  //   Within each sector, create a new Steiner point 'np', and replace 'p'
+  //   by 'np' for all tets in this sector.
+  for (i = 0; i < cavesegshlist->objects; i++) {
+    parysh = (face *) fastlookup(cavesegshlist, i);
+    // 'parysh' is the face [lpt, steinerpt, #].
+    stpivot(*parysh, neightet);
+    // Get all tets in this sector.
+    setpoint2tet(steinerpt, encode(neightet));
+    getvertexstar(0, steinerpt, cavetetlist, NULL, caveshlist);
+    if (!ishulltet(neightet)) {
+      // Within each tet in the ball, replace 'p' by 'np'.
+      for (j = 0; j < cavetetlist->objects; j++) {
+        parytet = (triface *) fastlookup(cavetetlist, j);
+        setoppo(*parytet, newsteiners[i]);
+      } // j
+      // Point to a parent tet.
+      parytet = (triface *) fastlookup(cavetetlist, 0);
+      setpoint2tet(newsteiners[i], (tetrahedron) (parytet->tet)); 
+      st_volref_count++;
+      if (steinerleft > 0) steinerleft--;
+    }
+    // Disconnect the set of boundary faces. They're temporarily open faces.
+    //   They will be connected to the new tets after 'p' is removed.
+    for (j = 0; j < caveshlist->objects; j++) {
+      // Get a boundary face.
+      parysh = (face *) fastlookup(caveshlist, j);
+      stpivot(*parysh, neightet);
+      //assert(apex(neightet) == newpt);
+      // Clear the connection at this face.
+      dissolve(neightet);
+      tsdissolve(neightet);
+    }
+    // Clear the working lists.
+    cavetetlist->restart();
+    caveshlist->restart();
+  } // i
+  cavesegshlist->restart();
+
+  if (vt == FREESEGVERTEX) { 
+    spivot(rightseg, parentsh); // 'rightseg' has p as its origin.
+    splitseg = &rightseg;
+  } else {
+    if (sdest(parentsh) == steinerpt) {
+      senextself(parentsh);
+    } else if (sapex(parentsh) == steinerpt) {
+      senext2self(parentsh);
+    }
+    splitseg = NULL;
+  }
+  sremovevertex(steinerpt, &parentsh, splitseg, slawson);
+
+  if (vt == FREESEGVERTEX) {
+    // The original segment is returned in 'rightseg'. 
+    rightseg.shver = 0;
+  }
+
+  // For each new subface, create two new tets at each side of it.
+  //   Both of the two new tets have its opposite be dummypoint. 
+  for (i = 0; i < caveshbdlist->objects; i++) {
+    parysh = (face *) fastlookup(caveshbdlist, i);
+    sinfect(*parysh); // Mark it for connecting new tets.
+    newsh = *parysh;
+    pa = sorg(newsh);
+    pb = sdest(newsh);
+    pc = sapex(newsh);
+    maketetrahedron(&newtet);
+    maketetrahedron(&neightet);
+    setvertices(newtet, pa, pb, pc, dummypoint);
+    setvertices(neightet, pb, pa, pc, dummypoint);
+    bond(newtet, neightet);
+    tsbond(newtet, newsh);
+    sesymself(newsh);
+    tsbond(neightet, newsh);
+  }
+  // Temporarily increase the hullsize.
+  hullsize += (caveshbdlist->objects * 2l);
+
+  if (vt == FREESEGVERTEX) {
+    // Connecting new tets at the recovered segment.
+    spivot(rightseg, parentsh);
+    spinsh = parentsh;
+    while (1) {
+      if (sorg(spinsh) != lpt) sesymself(spinsh);
+      // Get the new tet at this subface.
+      stpivot(spinsh, newtet);
+      tssbond1(newtet, rightseg);
+      // Go to the other face at this segment.
+      spivot(spinsh, neighsh);
+      if (sorg(neighsh) != lpt) sesymself(neighsh);
+      sesymself(neighsh);
+      stpivot(neighsh, neightet);
+      tssbond1(neightet, rightseg);
+      sstbond1(rightseg, neightet); 
+      // Connecting two adjacent tets at this segment.
+      esymself(newtet);
+      esymself(neightet);
+      // Connect the two tets (at rightseg) together.
+      bond(newtet, neightet);
+      // Go to the next subface.
+      spivotself(spinsh);
+      if (spinsh.sh == parentsh.sh) break;
+    }
+  }
+
+  // Connecting new tets at new subfaces together.
+  for (i = 0; i < caveshbdlist->objects; i++) {
+    parysh = (face *) fastlookup(caveshbdlist, i);
+    newsh = *parysh;
+    //assert(sinfected(newsh));
+    // Each new subface contains two new tets.
+    for (k = 0; k < 2; k++) {
+      stpivot(newsh, newtet);
+      for (j = 0; j < 3; j++) {
+        // Check if this side is open.
+        esym(newtet, newface);
+        if (newface.tet[newface.ver & 3] == NULL) {
+          // An open face. Connect it to its adjacent tet.
+          sspivot(newsh, checkseg);
+          if (checkseg.sh != NULL) {
+            // A segment. It must not be the recovered segment.
+            tssbond1(newtet, checkseg);
+            sstbond1(checkseg, newtet);
+          }
+          spivot(newsh, neighsh);
+          if (neighsh.sh != NULL) {
+            // The adjacent subface exists. It's not a dangling segment.
+            if (sorg(neighsh) != sdest(newsh)) sesymself(neighsh);
+            stpivot(neighsh, neightet);
+            if (sinfected(neighsh)) {
+              esymself(neightet);
+            } else {
+              // Search for an open face at this edge.
+              spintet = neightet;
+              while (1) {
+                esym(spintet, searchtet);
+                fsym(searchtet, spintet);
+                if (spintet.tet == NULL) break;
+              }
+              // Found an open face at 'searchtet'.
+              neightet = searchtet;
+            }
+          } else {
+            // The edge (at 'newsh') is a dangling segment.
+            // Get an adjacent tet at this segment.
+            sstpivot1(checkseg, neightet);
+            if (org(neightet) != sdest(newsh)) esymself(neightet);
+            // Search for an open face at this edge.
+            spintet = neightet;
+            while (1) {
+              esym(spintet, searchtet);
+              fsym(searchtet, spintet);
+              if (spintet.tet == NULL) break;
+            }
+            // Found an open face at 'searchtet'.
+            neightet = searchtet;
+          }
+          pc = apex(newface);
+          if (apex(neightet) == steinerpt) {
+            // Exterior case. The 'neightet' is a hull tet which contain
+            //   'steinerpt'. It will be deleted after 'steinerpt' is removed. 
+            caveoldtetlist->newindex((void **) &parytet);
+            *parytet = neightet;
+            // Connect newface to the adjacent hull tet of 'neightet', which
+            //   has the same edge as 'newface', and does not has 'steinerpt'.
+            fnextself(neightet);
+          } else {
+            if (pc == dummypoint) {
+              if (apex(neightet) != dummypoint) {
+                setapex(newface, apex(neightet));
+                // A hull tet has turned into an interior tet.
+                hullsize--; // Must update the hullsize.
+              } 
+            }
+          }
+          bond(newface, neightet);
+        } // if (newface.tet[newface.ver & 3] == NULL)
+        enextself(newtet);
+        senextself(newsh);
+      } // j
+      sesymself(newsh);
+    } // k
+  } // i
+
+  // Unmark all new subfaces.
+  for (i = 0; i < caveshbdlist->objects; i++) {
+    parysh = (face *) fastlookup(caveshbdlist, i);
+    suninfect(*parysh);
+  }
+  caveshbdlist->restart();
+
+  if (caveoldtetlist->objects > 0l) {
+    // Delete hull tets which contain 'steinerpt'.
+    for (i = 0; i < caveoldtetlist->objects; i++) {
+      parytet = (triface *) fastlookup(caveoldtetlist, i);
+      tetrahedrondealloc(parytet->tet);
+    }
+    // Must update the hullsize.
+    hullsize -= caveoldtetlist->objects;
+    caveoldtetlist->restart();
+  }
+
+  setpointtype(steinerpt, UNUSEDVERTEX);
+  unuverts++;
+  if (vt == FREESEGVERTEX) {
+    st_segref_count--;
+  } else { // vt == FREEFACETVERTEX
+    st_facref_count--;
+  }
+  if (steinerleft > 0) steinerleft++;  // We've removed a Steiner points.
+
+
+  point *parypt;
+  int steinercount = 0;
+
+  int bak_fliplinklevel = b->fliplinklevel;
+  b->fliplinklevel = 100000; // Unlimited flip level.
+
+  // Try to remove newly added Steiner points.
+  for (i = 0; i < n; i++) {
+    if (newsteiners[i] != NULL) {
+      if (!removevertexbyflips(newsteiners[i])) {
+        if (b->supsteiner_level > 0) { // Not -Y/0
+          // Save it in subvertstack for removal.
+          subvertstack->newindex((void **) &parypt);
+          *parypt = newsteiners[i];
+        }
+        steinercount++;
+      }
+    }
+  }
+
+  b->fliplinklevel = bak_fliplinklevel;
+
+  if (steinercount > 0) {
+    if (b->verbose > 2) {
+      printf("      Added %d interior Steiner points.\n", steinercount);
+    }
+  }
+
+  delete [] newsteiners;
+
+  return 1;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// suppresssteinerpoints()    Suppress Steiner points.                       //
+//                                                                           //
+// All Steiner points have been saved in 'subvertstack' in the routines      //
+// carveholes() and suppresssteinerpoint().                                  //
+// Each Steiner point is either removed or shifted into the interior.        //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+int tetgenmesh::suppresssteinerpoints()
+{
+
+  if (!b->quiet) {
+    printf("Suppressing Steiner points ...\n");
+  }
+
+  point rempt, *parypt;
+
+  int bak_fliplinklevel = b->fliplinklevel;
+  b->fliplinklevel = 100000; // Unlimited flip level.
+  int suppcount = 0, remcount = 0;
+  int i;
+
+  // Try to suppress boundary Steiner points.
+  for (i = 0; i < subvertstack->objects; i++) {
+    parypt = (point *) fastlookup(subvertstack, i);
+    rempt = *parypt;
+    if (pointtype(rempt) != UNUSEDVERTEX) {
+      if ((pointtype(rempt) == FREESEGVERTEX) || 
+          (pointtype(rempt) == FREEFACETVERTEX)) {
+        if (suppressbdrysteinerpoint(rempt)) {
+          suppcount++;
+        }
+      }
+    }
+  } // i
+
+  if (suppcount > 0) {
+    if (b->verbose) {
+      printf("  Suppressed %d boundary Steiner points.\n", suppcount);
+    }
+  }
+
+  if (b->supsteiner_level > 0) { // -Y/1
+    for (i = 0; i < subvertstack->objects; i++) {
+      parypt = (point *) fastlookup(subvertstack, i);
+      rempt = *parypt;
+      if (pointtype(rempt) != UNUSEDVERTEX) {
+        if (pointtype(rempt) == FREEVOLVERTEX) {
+          if (removevertexbyflips(rempt)) {
+            remcount++;
+          }
+        }
+      }
+    }
+  }
+
+  if (remcount > 0) {
+    if (b->verbose) {
+      printf("  Removed %d interior Steiner points.\n", remcount);
+    }
+  }
+
+  b->fliplinklevel = bak_fliplinklevel;
+
+  if (b->supsteiner_level > 1) { // -Y/2
+    // Smooth interior Steiner points.
+    optparameters opm;
+    triface *parytet;
+    point *ppt;
+    REAL ori;
+    int smtcount, count, ivcount;
+    int nt, j;
+
+    // Point smooth options.
+    opm.max_min_volume = 1;
+    opm.numofsearchdirs = 20;
+    opm.searchstep = 0.001;
+    opm.maxiter = 30; // Limit the maximum iterations.
+
+    smtcount = 0;
+
+    do {
+
+      nt = 0;
+
+      while (1) {
+        count = 0;
+        ivcount = 0; // Clear the inverted count.
+
+        for (i = 0; i < subvertstack->objects; i++) {
+          parypt = (point *) fastlookup(subvertstack, i);
+          rempt = *parypt;
+          if (pointtype(rempt) == FREEVOLVERTEX) {
+            getvertexstar(1, rempt, cavetetlist, NULL, NULL);
+            // Calculate the initial smallest volume (maybe zero or negative).
+            for (j = 0; j < cavetetlist->objects; j++) {
+              parytet = (triface *) fastlookup(cavetetlist, j);
+              ppt = (point *) &(parytet->tet[4]);
+              ori = orient3dfast(ppt[1], ppt[0], ppt[2], ppt[3]);
+              if (j == 0) {
+                opm.initval = ori;
+              } else {
+                if (opm.initval > ori) opm.initval = ori; 
+              }
+            }
+            if (smoothpoint(rempt, cavetetlist, 1, &opm)) {
+              count++;
+            }
+            if (opm.imprval <= 0.0) {
+              ivcount++; // The mesh contains inverted elements.
+            }
+            cavetetlist->restart();
+          }
+        } // i
+
+        smtcount += count;
+
+        if (count == 0) {
+          // No point has been smoothed.
+          break;
+        }
+
+        nt++;
+        if (nt > 2) {
+          break; // Already three iterations.
+        }
+      } // while
+
+      if (ivcount > 0) {
+        // There are inverted elements!
+        if (opm.maxiter > 0) {
+          // Set unlimited smoothing steps. Try again.
+          opm.numofsearchdirs = 30;
+          opm.searchstep = 0.0001;
+          opm.maxiter = -1;
+          continue;
+        }
+      }
+
+      break;
+    } while (1); // Additional loop for (ivcount > 0)
+
+    if (ivcount > 0) {
+      printf("BUG Report!  The mesh contain inverted elements.\n");
+    }
+
+    if (b->verbose) {
+      if (smtcount > 0) {
+        printf("  Smoothed %d Steiner points.\n", smtcount); 
+      }
+    }
+  } // -Y2
+
+  subvertstack->restart();
+
+  return 1;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// recoverboundary()    Recover segments and facets.                         //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::recoverboundary(clock_t& tv)
+{
+  arraypool *misseglist, *misshlist;
+  arraypool *bdrysteinerptlist;
+  face searchsh, *parysh;
+  face searchseg, *paryseg;
+  point rempt, *parypt;
+  long ms; // The number of missing segments/subfaces.
+  int nit; // The number of iterations.
+  int s, i;
+
+  // Counters.
+  long bak_segref_count, bak_facref_count, bak_volref_count;
+
+  if (!b->quiet) {
+    printf("Recovering boundaries...\n");
+  }
+
+
+  if (b->verbose) {
+    printf("  Recovering segments.\n");
+  }
+
+  // Segments will be introduced.
+  checksubsegflag = 1;
+
+  misseglist = new arraypool(sizeof(face), 8);
+  bdrysteinerptlist = new arraypool(sizeof(point), 8);
+
+  // In random order.
+  subsegs->traversalinit();
+  for (i = 0; i < subsegs->items; i++) {
+    s = randomnation(i + 1);
+    // Move the s-th seg to the i-th.
+    subsegstack->newindex((void **) &paryseg);
+    *paryseg = * (face *) fastlookup(subsegstack, s);
+    // Put i-th seg to be the s-th.
+    searchseg.sh = shellfacetraverse(subsegs);
+    paryseg = (face *) fastlookup(subsegstack, s);
+    *paryseg = searchseg;
+  }
+
+  // The init number of missing segments.
+  ms = subsegs->items;
+  nit = 0; 
+  if (b->fliplinklevel < 0) {
+    autofliplinklevel = 1; // Init value.
+  }
+
+  // First, trying to recover segments by only doing flips.
+  while (1) {
+    recoversegments(misseglist, 0, 0);
+
+    if (misseglist->objects > 0) {
+      if (b->fliplinklevel >= 0) {
+        break;
+      } else {
+        if (misseglist->objects >= ms) {
+          nit++;
+          if (nit >= 3) {
+            //break;
+            // Do the last round with unbounded flip link level.
+            b->fliplinklevel = 100000;
+          }
+        } else {
+          ms = misseglist->objects;
+          if (nit > 0) {
+            nit--;
+          }
+        }
+        for (i = 0; i < misseglist->objects; i++) {
+          subsegstack->newindex((void **) &paryseg);
+          *paryseg = * (face *) fastlookup(misseglist, i);
+        }
+        misseglist->restart();
+        autofliplinklevel+=b->fliplinklevelinc;
+      }
+    } else {
+      // All segments are recovered.
+      break;
+    }
+  } // while (1)
+
+  if (b->verbose) {
+    printf("  %ld (%ld) segments are recovered (missing).\n", 
+           subsegs->items - misseglist->objects, misseglist->objects);
+  }
+
+  if (misseglist->objects > 0) {
+    // Second, trying to recover segments by doing more flips (fullsearch).
+    while (misseglist->objects > 0) {
+      ms = misseglist->objects;
+      for (i = 0; i < misseglist->objects; i++) {
+        subsegstack->newindex((void **) &paryseg);
+        *paryseg = * (face *) fastlookup(misseglist, i);
+      }
+      misseglist->restart();
+
+      recoversegments(misseglist, 1, 0);
+
+      if (misseglist->objects < ms) {
+        // The number of missing segments is reduced.
+        continue;
+      } else {
+        break;
+      }
+    }
+    if (b->verbose) {
+      printf("  %ld (%ld) segments are recovered (missing).\n", 
+             subsegs->items - misseglist->objects, misseglist->objects);
+    }
+  }
+
+  if (misseglist->objects > 0) {
+    // Third, trying to recover segments by doing more flips (fullsearch)
+    //   and adding Steiner points in the volume.
+    while (misseglist->objects > 0) {
+      ms = misseglist->objects;
+      for (i = 0; i < misseglist->objects; i++) {
+        subsegstack->newindex((void **) &paryseg);
+        *paryseg = * (face *) fastlookup(misseglist, i);
+      }
+      misseglist->restart();
+
+      recoversegments(misseglist, 1, 1);
+
+      if (misseglist->objects < ms) {
+        // The number of missing segments is reduced.
+        continue;
+      } else {
+        break;
+      }
+    }
+    if (b->verbose) {
+      printf("  Added %ld Steiner points in volume.\n", st_volref_count);
+    }
+  }
+
+  if (misseglist->objects > 0) {
+    // Last, trying to recover segments by doing more flips (fullsearch),
+    //   and adding Steiner points in the volume, and splitting segments.
+    long bak_inpoly_count = st_volref_count; //st_inpoly_count;
+    for (i = 0; i < misseglist->objects; i++) {
+      subsegstack->newindex((void **) &paryseg);
+      *paryseg = * (face *) fastlookup(misseglist, i);
+    }
+    misseglist->restart();
+
+    recoversegments(misseglist, 1, 2);
+
+    if (b->verbose) {
+      printf("  Added %ld Steiner points in segments.\n", st_segref_count);
+      if (st_volref_count > bak_inpoly_count) {
+        printf("  Added another %ld Steiner points in volume.\n", 
+               st_volref_count - bak_inpoly_count);
+      }
+    }
+  }
+
+
+  if (st_segref_count > 0) {
+    // Try to remove the Steiner points added in segments.
+    bak_segref_count = st_segref_count;
+    bak_volref_count = st_volref_count;
+    for (i = 0; i < subvertstack->objects; i++) {
+      // Get the Steiner point.
+      parypt = (point *) fastlookup(subvertstack, i);
+      rempt = *parypt;
+      if (!removevertexbyflips(rempt)) {
+        // Save it in list.
+        bdrysteinerptlist->newindex((void **) &parypt);
+        *parypt = rempt;
+      }
+    }
+    if (b->verbose) {
+      if (st_segref_count < bak_segref_count) {
+        if (bak_volref_count < st_volref_count) {
+          printf("  Suppressed %ld Steiner points in segments.\n", 
+                 st_volref_count - bak_volref_count);
+        }
+        if ((st_segref_count + (st_volref_count - bak_volref_count)) <
+            bak_segref_count) {
+          printf("  Removed %ld Steiner points in segments.\n", 
+                 bak_segref_count - 
+                   (st_segref_count + (st_volref_count - bak_volref_count)));
+        }
+      }
+    }
+    subvertstack->restart();
+  }
+
+
+  tv = clock();
+
+  if (b->verbose) {
+    printf("  Recovering facets.\n");
+  }
+
+  // Subfaces will be introduced.
+  checksubfaceflag = 1;
+
+  misshlist = new arraypool(sizeof(face), 8);
+
+  // Randomly order the subfaces.
+  subfaces->traversalinit();
+  for (i = 0; i < subfaces->items; i++) {
+    s = randomnation(i + 1);
+    // Move the s-th subface to the i-th.
+    subfacstack->newindex((void **) &parysh);
+    *parysh = * (face *) fastlookup(subfacstack, s);
+    // Put i-th subface to be the s-th.
+    searchsh.sh = shellfacetraverse(subfaces);
+    parysh = (face *) fastlookup(subfacstack, s);
+    *parysh = searchsh;
+  }
+
+  ms = subfaces->items;
+  nit = 0; 
+  b->fliplinklevel = -1; // Init.
+  if (b->fliplinklevel < 0) {
+    autofliplinklevel = 1; // Init value.
+  }
+
+  while (1) {
+    recoversubfaces(misshlist, 0);
+
+    if (misshlist->objects > 0) {
+      if (b->fliplinklevel >= 0) {
+        break;
+      } else {
+        if (misshlist->objects >= ms) {
+          nit++;
+          if (nit >= 3) {
+            //break;
+            // Do the last round with unbounded flip link level.
+            b->fliplinklevel = 100000;
+          }
+        } else {
+          ms = misshlist->objects;
+          if (nit > 0) {
+            nit--;
+          }
+        }
+        for (i = 0; i < misshlist->objects; i++) {
+          subfacstack->newindex((void **) &parysh);
+          *parysh = * (face *) fastlookup(misshlist, i);
+        }
+        misshlist->restart();
+        autofliplinklevel+=b->fliplinklevelinc;
+      }
+    } else {
+      // All subfaces are recovered.
+      break;
+    }
+  } // while (1)
+
+  if (b->verbose) {
+    printf("  %ld (%ld) subfaces are recovered (missing).\n", 
+           subfaces->items - misshlist->objects, misshlist->objects);
+  }
+
+  if (misshlist->objects > 0) {
+    // There are missing subfaces. Add Steiner points.
+    for (i = 0; i < misshlist->objects; i++) {
+      subfacstack->newindex((void **) &parysh);
+      *parysh = * (face *) fastlookup(misshlist, i);
+    }
+    misshlist->restart();
+
+    recoversubfaces(NULL, 1);
+
+    if (b->verbose) {
+      printf("  Added %ld Steiner points in facets.\n", st_facref_count);
+    }
+  }
+
+
+  if (st_facref_count > 0) {
+    // Try to remove the Steiner points added in facets.
+    bak_facref_count = st_facref_count;
+    for (i = 0; i < subvertstack->objects; i++) {
+      // Get the Steiner point.
+      parypt = (point *) fastlookup(subvertstack, i);
+      rempt = *parypt;
+      if (!removevertexbyflips(*parypt)) {
+        // Save it in list.
+        bdrysteinerptlist->newindex((void **) &parypt);
+        *parypt = rempt;
+      }
+    }
+    if (b->verbose) {
+      if (st_facref_count < bak_facref_count) {
+        printf("  Removed %ld Steiner points in facets.\n", 
+               bak_facref_count - st_facref_count);
+      }
+    }
+    subvertstack->restart();
+  }
+
+
+  if (bdrysteinerptlist->objects > 0) {
+    if (b->verbose) {
+      printf("  %ld Steiner points remained in boundary.\n",
+             bdrysteinerptlist->objects);
+    }
+  } // if
+
+
+  // Accumulate the dynamic memory.
+  totalworkmemory += (misseglist->totalmemory + misshlist->totalmemory +
+                      bdrysteinerptlist->totalmemory);
+
+  delete bdrysteinerptlist;
+  delete misseglist;
+  delete misshlist;
+}
+
+////                                                                       ////
+////                                                                       ////
+//// steiner_cxx //////////////////////////////////////////////////////////////
+
+
+//// reconstruct_cxx //////////////////////////////////////////////////////////
+////                                                                       ////
+////                                                                       ////
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// carveholes()    Remove tetrahedra not in the mesh domain.                 //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+
+void tetgenmesh::carveholes()
+{
+  arraypool *tetarray, *hullarray;
+  triface tetloop, neightet, *parytet, *parytet1;
+  triface *regiontets = NULL;
+  face checksh, *parysh;
+  face checkseg;
+  point ptloop, *parypt;
+  int t1ver;
+  int i, j, k;
+
+  if (!b->quiet) {
+    if (b->convex) {
+      printf("Marking exterior tetrahedra ...\n");
+    } else {
+      printf("Removing exterior tetrahedra ...\n");
+    }
+  }
+
+  // Initialize the pool of exterior tets.
+  tetarray = new arraypool(sizeof(triface), 10);
+  hullarray = new arraypool(sizeof(triface), 10);
+
+  // Collect unprotected tets and hull tets.
+  tetrahedrons->traversalinit();
+  tetloop.ver = 11; // The face opposite to dummypoint.
+  tetloop.tet = alltetrahedrontraverse();
+  while (tetloop.tet != (tetrahedron *) NULL) {
+    if (ishulltet(tetloop)) {
+      // Is this side protected by a subface?
+      if (!issubface(tetloop)) {
+        // Collect an unprotected hull tet and tet.
+        infect(tetloop);
+        hullarray->newindex((void **) &parytet);
+        *parytet = tetloop;
+        // tetloop's face number is 11 & 3 = 3.
+        decode(tetloop.tet[3], neightet);
+        if (!infected(neightet)) {
+          infect(neightet);
+          tetarray->newindex((void **) &parytet);
+          *parytet = neightet;
+        }
+      }
+    }
+    tetloop.tet = alltetrahedrontraverse();
+  }
+
+  if (in->numberofholes > 0) {
+    // Mark as infected any tets inside volume holes.
+    for (i = 0; i < 3 * in->numberofholes; i += 3) {
+      // Search a tet containing the i-th hole point.
+      neightet.tet = NULL;
+      randomsample(&(in->holelist[i]), &neightet);
+      if (locate(&(in->holelist[i]), &neightet) != OUTSIDE) {
+        // The tet 'neightet' contain this point.
+        if (!infected(neightet)) {
+          infect(neightet);
+          tetarray->newindex((void **) &parytet);
+          *parytet = neightet;
+          // Add its adjacent tet if it is not protected.
+          if (!issubface(neightet)) {
+            decode(neightet.tet[neightet.ver & 3], tetloop);
+            if (!infected(tetloop)) {
+              infect(tetloop);
+              if (ishulltet(tetloop)) {
+                hullarray->newindex((void **) &parytet);
+              } else {
+                tetarray->newindex((void **) &parytet);
+              }
+              *parytet = tetloop;
+            }
+          }
+          else {
+            // It is protected. Check if its adjacent tet is a hull tet.
+            decode(neightet.tet[neightet.ver & 3], tetloop);
+            if (ishulltet(tetloop)) {
+              // It is hull tet, add it into the list. Moreover, the subface
+              //   is dead, i.e., both sides are in exterior.
+              if (!infected(tetloop)) {
+                infect(tetloop);
+                hullarray->newindex((void **) &parytet);
+                *parytet = tetloop;
+              }
+            }
+            if (infected(tetloop)) {
+              // Both sides of this subface are in exterior.
+              tspivot(neightet, checksh);
+              sinfect(checksh); // Only queue it once.
+              subfacstack->newindex((void **) &parysh);
+              *parysh = checksh;
+            }
+          }
+        } // if (!infected(neightet))
+      } else {
+        // A hole point locates outside of the convex hull.
+        if (!b->quiet) {
+          printf("Warning:  The %d-th hole point ", i/3 + 1);
+          printf("lies outside the convex hull.\n");
+        }
+      }
+    } // i
+  } // if (in->numberofholes > 0)
+
+  if (b->regionattrib && (in->numberofregions > 0)) { // -A option.
+    // Record the tetrahedra that contains the region points for assigning
+    //   region attributes after the holes have been carved.
+    regiontets = new triface[in->numberofregions];
+    // Mark as marktested any tetrahedra inside volume regions.
+    for (i = 0; i < 5 * in->numberofregions; i += 5) {
+      // Search a tet containing the i-th region point.
+      neightet.tet = NULL;
+      randomsample(&(in->regionlist[i]), &neightet);
+      if (locate(&(in->regionlist[i]), &neightet) != OUTSIDE) {
+        regiontets[i/5] = neightet;
+      } else {
+        if (!b->quiet) {
+          printf("Warning:  The %d-th region point ", i/5+1);
+          printf("lies outside the convex hull.\n");
+        }
+        regiontets[i/5].tet = NULL;
+      }
+    }
+  }
+
+  // Collect all exterior tets (in concave place and in holes).
+  for (i = 0; i < tetarray->objects; i++) {
+    parytet = (triface *) fastlookup(tetarray, i);
+    j = (parytet->ver & 3); // j is the current face number.
+    // Check the other three adjacent tets.
+    for (k = 1; k < 4; k++) {
+      decode(parytet->tet[(j + k) % 4], neightet); 
+      // neightet may be a hull tet.
+      if (!infected(neightet)) {
+        // Is neightet protected by a subface.
+        if (!issubface(neightet)) {
+          // Not proected. Collect it. (It must not be a hull tet).
+          infect(neightet);
+          tetarray->newindex((void **) &parytet1);
+          *parytet1 = neightet;
+        } else {
+          // Protected. Check if it is a hull tet.
+          if (ishulltet(neightet)) {
+            // A hull tet. Collect it.
+            infect(neightet);
+            hullarray->newindex((void **) &parytet1);
+            *parytet1 = neightet;
+            // Both sides of this subface are exterior.
+            tspivot(neightet, checksh);
+            // Queue this subface (to be deleted later).
+            sinfect(checksh); // Only queue it once.
+            subfacstack->newindex((void **) &parysh);
+            *parysh = checksh;
+          }
+        }
+      } else {
+        // Both sides of this face are in exterior.
+        // If there is a subface. It should be collected.
+        if (issubface(neightet)) {
+          tspivot(neightet, checksh);
+          if (!sinfected(checksh)) {
+            sinfect(checksh);
+            subfacstack->newindex((void **) &parysh);
+            *parysh = checksh;
+          }
+        }
+      }
+    } // j, k
+  } // i
+
+  if (b->regionattrib && (in->numberofregions > 0)) {
+    // Re-check saved region tets to see if they lie outside.
+    for (i = 0; i < in->numberofregions; i++) {
+      if (infected(regiontets[i])) {
+        if (b->verbose) {
+          printf("Warning:  The %d-th region point ", i+1);
+          printf("lies in the exterior of the domain.\n");
+        }
+        regiontets[i].tet = NULL;
+      }
+    }
+  }
+
+  // Collect vertices which point to infected tets. These vertices
+  //   may get deleted after the removal of exterior tets.
+  //   If -Y1 option is used, collect all Steiner points for removal.
+  //   The lists 'cavetetvertlist' and 'subvertstack' are re-used.
+  points->traversalinit();
+  ptloop = pointtraverse();
+  while (ptloop != NULL) {
+    if ((pointtype(ptloop) != UNUSEDVERTEX) &&
+        (pointtype(ptloop) != DUPLICATEDVERTEX)) {
+      decode(point2tet(ptloop), neightet);
+      if (infected(neightet)) {
+        cavetetvertlist->newindex((void **) &parypt);
+        *parypt = ptloop;
+      }
+      if (b->nobisect && (b->supsteiner_level > 0)) { // -Y/1
+        // Queue it if it is a Steiner point.
+        if (pointmark(ptloop) > 
+              (in->numberofpoints - (in->firstnumber ? 0 : 1))) {
+          subvertstack->newindex((void **) &parypt);
+          *parypt = ptloop;
+        }
+      }
+    }
+    ptloop = pointtraverse();
+  }
+
+  if (!b->convex && (tetarray->objects > 0l)) { // No -c option.
+    // Remove exterior tets. Hull tets are updated.
+    arraypool *newhullfacearray;
+    triface hulltet, casface;
+    face segloop, *paryseg;
+    point pa, pb, pc;
+    long delsegcount = 0l;
+ 
+    // Collect segments which point to infected tets. Some segments
+    //   may get deleted after the removal of exterior tets.
+    subsegs->traversalinit();
+    segloop.sh = shellfacetraverse(subsegs);
+    while (segloop.sh != NULL) {
+      sstpivot1(segloop, neightet);
+      if (infected(neightet)) {
+        subsegstack->newindex((void **) &paryseg);
+        *paryseg = segloop;
+      }
+      segloop.sh = shellfacetraverse(subsegs);
+    }
+
+    newhullfacearray = new arraypool(sizeof(triface), 10);
+
+    // Create and save new hull tets.
+    for (i = 0; i < tetarray->objects; i++) {
+      parytet = (triface *) fastlookup(tetarray, i);
+      for (j = 0; j < 4; j++) {
+        decode(parytet->tet[j], tetloop);
+        if (!infected(tetloop)) {
+          // Found a new hull face (must be a subface).
+          tspivot(tetloop, checksh);
+          maketetrahedron(&hulltet);
+          pa = org(tetloop);
+          pb = dest(tetloop);
+          pc = apex(tetloop);
+          setvertices(hulltet, pb, pa, pc, dummypoint);
+          bond(tetloop, hulltet);
+          // Update the subface-to-tet map.
+          sesymself(checksh);
+          tsbond(hulltet, checksh);
+          // Update the segment-to-tet map.
+          for (k = 0; k < 3; k++) {
+            if (issubseg(tetloop)) {
+              tsspivot1(tetloop, checkseg);
+              tssbond1(hulltet, checkseg);
+              sstbond1(checkseg, hulltet);
+            }
+            enextself(tetloop);
+            eprevself(hulltet);
+          }
+          // Update the point-to-tet map.
+          setpoint2tet(pa, (tetrahedron) tetloop.tet);
+          setpoint2tet(pb, (tetrahedron) tetloop.tet);
+          setpoint2tet(pc, (tetrahedron) tetloop.tet);
+          // Save the exterior tet at this hull face. It still holds pointer
+          //   to the adjacent interior tet. Use it to connect new hull tets. 
+          newhullfacearray->newindex((void **) &parytet1);
+          parytet1->tet = parytet->tet;
+          parytet1->ver = j;
+        } // if (!infected(tetloop))
+      } // j
+    } // i
+
+    // Connect new hull tets.
+    for (i = 0; i < newhullfacearray->objects; i++) {
+      parytet = (triface *) fastlookup(newhullfacearray, i);
+      fsym(*parytet, neightet);
+      // Get the new hull tet.
+      fsym(neightet, hulltet);
+      for (j = 0; j < 3; j++) {
+        esym(hulltet, casface);
+        if (casface.tet[casface.ver & 3] == NULL) {
+          // Since the boundary of the domain may not be a manifold, we
+          //   find the adjacent hull face by traversing the tets in the
+          //   exterior (which are all infected tets).
+          neightet = *parytet;
+          while (1) {
+            fnextself(neightet);
+            if (!infected(neightet)) break;
+          }
+          if (!ishulltet(neightet)) {
+            // An interior tet. Get the new hull tet.
+            fsymself(neightet);
+            esymself(neightet);
+          } 
+          // Bond them together.
+          bond(casface, neightet);
+        }
+        enextself(hulltet);
+        enextself(*parytet);
+      } // j
+    } // i
+
+    if (subfacstack->objects > 0l) {
+      // Remove all subfaces which do not attach to any tetrahedron.
+      //   Segments which are not attached to any subfaces and tets
+      //   are deleted too.
+      face casingout, casingin;
+
+      for (i = 0; i < subfacstack->objects; i++) {
+        parysh = (face *) fastlookup(subfacstack, i);
+        if (i == 0) {
+          if (b->verbose) {
+            printf("Warning:  Removed an exterior face (%d, %d, %d) #%d\n",
+                   pointmark(sorg(*parysh)), pointmark(sdest(*parysh)),
+                   pointmark(sapex(*parysh)), shellmark(*parysh));
+          }
+        }
+        // Dissolve this subface from face links.
+        for (j = 0; j < 3; j++) {         
+          spivot(*parysh, casingout);
+          sspivot(*parysh, checkseg);
+          if (casingout.sh != NULL) {
+            casingin = casingout;
+            while (1) {
+              spivot(casingin, checksh);
+              if (checksh.sh == parysh->sh) break;
+              casingin = checksh;
+            }
+            if (casingin.sh != casingout.sh) {
+              // Update the link: ... -> casingin -> casingout ->...
+              sbond1(casingin, casingout);
+            } else {
+              // Only one subface at this edge is left.
+              sdissolve(casingout);
+            }
+            if (checkseg.sh != NULL) {
+              // Make sure the segment does not connect to a dead one.
+              ssbond(casingout, checkseg);
+            }
+          } else {
+            if (checkseg.sh != NULL) {
+              //if (checkseg.sh[3] != NULL) {
+              if (delsegcount == 0) {
+                if (b->verbose) {
+                  printf("Warning:  Removed an exterior segment (%d, %d) #%d\n",
+                         pointmark(sorg(checkseg)), pointmark(sdest(checkseg)),
+                         shellmark(checkseg));
+                }
+              }
+              shellfacedealloc(subsegs, checkseg.sh);
+              delsegcount++;
+            }
+          }
+          senextself(*parysh);
+        } // j
+        // Delete this subface.
+        shellfacedealloc(subfaces, parysh->sh);
+      } // i
+      if (b->verbose) {
+        printf("  Deleted %ld subfaces.\n", subfacstack->objects);
+      }
+      subfacstack->restart();
+    } // if (subfacstack->objects > 0l)
+
+    if (subsegstack->objects > 0l) {
+      for (i = 0; i < subsegstack->objects; i++) {
+        paryseg = (face *) fastlookup(subsegstack, i);
+        if (paryseg->sh && (paryseg->sh[3] != NULL)) {
+          sstpivot1(*paryseg, neightet);
+          if (infected(neightet)) {
+            if (b->verbose) {
+              printf("Warning:  Removed an exterior segment (%d, %d) #%d\n",
+                     pointmark(sorg(*paryseg)), pointmark(sdest(*paryseg)),
+                     shellmark(*paryseg));
+            }
+            shellfacedealloc(subsegs, paryseg->sh);
+            delsegcount++;
+          }
+        }
+      }
+      subsegstack->restart();
+    } // if (subsegstack->objects > 0l)
+
+    if (delsegcount > 0) {
+      if (b->verbose) {
+        printf("  Deleted %ld segments.\n", delsegcount);
+      }
+    }
+
+    if (cavetetvertlist->objects > 0l) {
+      // Some vertices may lie in exterior. Marke them as UNUSEDVERTEX.
+      long delvertcount = unuverts;
+      long delsteinercount = 0l;
+
+      for (i = 0; i < cavetetvertlist->objects; i++) {
+        parypt = (point *) fastlookup(cavetetvertlist, i);
+        decode(point2tet(*parypt), neightet);
+        if (infected(neightet)) {
+          // Found an exterior vertex.
+          if (pointmark(*parypt) > 
+                (in->numberofpoints - (in->firstnumber ? 0 : 1))) {
+            // A Steiner point.
+            if (pointtype(*parypt) == FREESEGVERTEX) {
+              st_segref_count--;
+            } else if (pointtype(*parypt) == FREEFACETVERTEX) {
+              st_facref_count--;
+            } else {
+              st_volref_count--;
+            }
+            delsteinercount++;
+            if (steinerleft > 0) steinerleft++;
+          }
+          setpointtype(*parypt, UNUSEDVERTEX);
+          unuverts++;
+        }
+      }
+
+      if (b->verbose) {
+        if (unuverts > delvertcount) {
+          if (delsteinercount > 0l) {
+            if (unuverts > (delvertcount + delsteinercount)) {
+              printf("  Removed %ld exterior input vertices.\n", 
+                     unuverts - delvertcount - delsteinercount);
+            }
+            printf("  Removed %ld exterior Steiner vertices.\n", 
+                   delsteinercount);
+          } else {
+            printf("  Removed %ld exterior input vertices.\n", 
+                   unuverts - delvertcount);
+          }
+        }
+      }
+      cavetetvertlist->restart();
+      // Comment: 'subvertstack' will be cleaned in routine
+      //   suppresssteinerpoints().
+    } // if (cavetetvertlist->objects > 0l)
+
+    // Update the hull size.
+    hullsize += (newhullfacearray->objects - hullarray->objects);
+
+    // Delete all exterior tets and old hull tets.
+    for (i = 0; i < tetarray->objects; i++) {
+      parytet = (triface *) fastlookup(tetarray, i);
+      tetrahedrondealloc(parytet->tet);
+    }
+    tetarray->restart();
+
+    for (i = 0; i < hullarray->objects; i++) {
+      parytet = (triface *) fastlookup(hullarray, i);
+      tetrahedrondealloc(parytet->tet);
+    }
+    hullarray->restart();
+
+    delete newhullfacearray;
+  } // if (!b->convex && (tetarray->objects > 0l))
+
+  if (b->convex && (tetarray->objects > 0l)) { // With -c option
+    // In this case, all exterior tets get a region marker '-1'.
+    int attrnum = numelemattrib - 1;
+
+    for (i = 0; i < tetarray->objects; i++) {
+      parytet = (triface *) fastlookup(tetarray, i);
+      setelemattribute(parytet->tet, attrnum, -1);
+    }
+    tetarray->restart();
+
+    for (i = 0; i < hullarray->objects; i++) {
+      parytet = (triface *) fastlookup(hullarray, i);
+      uninfect(*parytet);
+    }
+    hullarray->restart();
+
+    if (subfacstack->objects > 0l) {
+      for (i = 0; i < subfacstack->objects; i++) {
+        parysh = (face *) fastlookup(subfacstack, i);
+        suninfect(*parysh);
+      }
+      subfacstack->restart();
+    }
+
+    if (cavetetvertlist->objects > 0l) {
+      cavetetvertlist->restart();
+    }
+  } // if (b->convex && (tetarray->objects > 0l))
+
+  if (b->regionattrib) { // With -A option.
+    if (!b->quiet) {
+      printf("Spreading region attributes.\n");
+    }
+    REAL volume;
+    int attr, maxattr = 0; // Choose a small number here.
+    int attrnum = numelemattrib - 1; 
+    // Comment: The element region marker is at the end of the list of
+    //   the element attributes.
+    int regioncount = 0;
+
+    // If has user-defined region attributes.
+    if (in->numberofregions > 0) {
+      // Spread region attributes.
+      for (i = 0; i < 5 * in->numberofregions; i += 5) {
+        if (regiontets[i/5].tet != NULL) {
+          attr = (int) in->regionlist[i + 3];
+          if (attr > maxattr) {
+            maxattr = attr;
+          }
+          volume = in->regionlist[i + 4];
+          tetarray->restart(); // Re-use this array.
+          infect(regiontets[i/5]);
+          tetarray->newindex((void **) &parytet);
+          *parytet = regiontets[i/5];
+          // Collect and set attrs for all tets of this region.
+          for (j = 0; j < tetarray->objects; j++) {
+            parytet = (triface *) fastlookup(tetarray, j);
+            tetloop = *parytet;
+            setelemattribute(tetloop.tet, attrnum, attr);
+            if (b->varvolume) { // If has -a option.
+              setvolumebound(tetloop.tet, volume);
+            }
+            for (k = 0; k < 4; k++) {
+              decode(tetloop.tet[k], neightet);
+              // Is the adjacent already checked?
+              if (!infected(neightet)) {
+                // Is this side protected by a subface?
+                if (!issubface(neightet)) {
+                  infect(neightet);
+                  tetarray->newindex((void **) &parytet);
+                  *parytet = neightet;
+                }
+              }
+            } // k
+          } // j
+          regioncount++;
+        } // if (regiontets[i/5].tet != NULL)
+      } // i
+    }
+
+    // Set attributes for all tetrahedra.
+    attr = maxattr + 1;
+    tetrahedrons->traversalinit();
+    tetloop.tet = tetrahedrontraverse();
+    while (tetloop.tet != (tetrahedron *) NULL) {
+      if (!infected(tetloop)) {
+        // An unmarked region.
+        tetarray->restart(); // Re-use this array.
+        infect(tetloop);
+        tetarray->newindex((void **) &parytet);
+        *parytet = tetloop;
+        // Find and mark all tets.
+        for (j = 0; j < tetarray->objects; j++) {
+          parytet = (triface *) fastlookup(tetarray, j);
+          tetloop = *parytet;
+          setelemattribute(tetloop.tet, attrnum, attr);
+          for (k = 0; k < 4; k++) {
+            decode(tetloop.tet[k], neightet);
+            // Is the adjacent tet already checked?
+            if (!infected(neightet)) {
+              // Is this side protected by a subface?
+              if (!issubface(neightet)) {
+                infect(neightet);
+                tetarray->newindex((void **) &parytet);
+                *parytet = neightet;
+              }
+            }
+          } // k
+        } // j
+        attr++; // Increase the attribute.
+        regioncount++;
+      }
+      tetloop.tet = tetrahedrontraverse();
+    }
+    // Until here, every tet has a region attribute.
+
+    // Uninfect processed tets.
+    tetrahedrons->traversalinit();
+    tetloop.tet = tetrahedrontraverse();
+    while (tetloop.tet != (tetrahedron *) NULL) {
+      uninfect(tetloop);
+      tetloop.tet = tetrahedrontraverse();
+    }
+
+    if (b->verbose) {
+      //assert(regioncount > 0);
+      if (regioncount > 1) {
+        printf("  Found %d subdomains.\n", regioncount);
+      } else {
+        printf("  Found %d domain.\n", regioncount);
+      }
+    }
+  } // if (b->regionattrib)
+
+  if (regiontets != NULL) {
+    delete [] regiontets;
+  }
+  delete tetarray;
+  delete hullarray;
+
+  if (!b->convex) { // No -c option
+    // The mesh is non-convex now.
+    nonconvex = 1;
+
+    // Push all hull tets into 'flipstack'.
+    tetrahedrons->traversalinit();
+    tetloop.ver = 11; // The face opposite to dummypoint.
+    tetloop.tet = alltetrahedrontraverse();
+    while (tetloop.tet != (tetrahedron *) NULL) {
+      if ((point) tetloop.tet[7] == dummypoint) {
+        fsym(tetloop, neightet);
+        flippush(flipstack, &neightet);
+      }
+      tetloop.tet = alltetrahedrontraverse();
+    }
+
+    flipconstraints fc;
+    fc.enqflag = 2;
+    long sliver_peel_count = lawsonflip3d(&fc);
+
+    if (sliver_peel_count > 0l) {
+      if (b->verbose) {
+        printf("  Removed %ld hull slivers.\n", sliver_peel_count);
+      }
+    }
+    unflipqueue->restart();
+  } // if (!b->convex)
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// enqueuesubface()    Queue a subface or a subsegment for encroachment chk. //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::enqueuesubface(memorypool *pool, face *chkface)
+{
+  if (!smarktest2ed(*chkface)) {
+    smarktest2(*chkface); // Only queue it once.
+    face *queface = (face *) pool->alloc();
+    *queface = *chkface;
+  }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// enqueuetetrahedron()    Queue a tetrahedron for quality check.            //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::enqueuetetrahedron(triface *chktet)
+{
+  if (!marktest2ed(*chktet)) {
+    marktest2(*chktet); // Only queue it once.
+    triface *quetet = (triface *) badtetrahedrons->alloc();
+    *quetet = *chktet;
+  }
+}
+
+//// optimize_cxx /////////////////////////////////////////////////////////////
+////                                                                       ////
+////                                                                       ////
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// lawsonflip3d()    A three-dimensional Lawson's algorithm.                 //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+long tetgenmesh::lawsonflip3d(flipconstraints *fc)
+{
+  triface fliptets[5], neightet, hulltet;
+  face checksh, casingout;
+  badface *popface, *bface;
+  point pd, pe, *pts;
+  REAL sign, ori;
+  REAL vol, len3;
+  long flipcount, totalcount = 0l;
+  long sliver_peels = 0l;
+  int t1ver;
+  int i;
+
+
+  while (1) {
+
+    if (b->verbose > 2) {
+      printf("      Lawson flip %ld faces.\n", flippool->items);
+    }
+    flipcount = 0l;
+
+    while (flipstack != (badface *) NULL) {
+      // Pop a face from the stack.
+      popface = flipstack;
+      fliptets[0] = popface->tt;
+      flipstack = flipstack->nextitem; // The next top item in stack.
+      flippool->dealloc((void *) popface);
+
+      // Skip it if it is a dead tet (destroyed by previous flips).
+      if (isdeadtet(fliptets[0])) continue;
+      // Skip it if it is not the same tet as we saved.
+      if (!facemarked(fliptets[0])) continue;
+
+      unmarkface(fliptets[0]);
+
+      if (ishulltet(fliptets[0])) continue;
+
+      fsym(fliptets[0], fliptets[1]);
+      if (ishulltet(fliptets[1])) {
+        if (nonconvex) {
+          // Check if 'fliptets[0]' it is a hull sliver.
+          tspivot(fliptets[0], checksh);
+          for (i = 0; i < 3; i++) {
+            if (!isshsubseg(checksh)) {
+              spivot(checksh, casingout);
+              //assert(casingout.sh != NULL);
+              if (sorg(checksh) != sdest(casingout)) sesymself(casingout);
+              stpivot(casingout, neightet);
+              if (neightet.tet == fliptets[0].tet) {
+                // Found a hull sliver 'neightet'. Let it be [e,d,a,b], where 
+                //   [e,d,a] and [d,e,b] are hull faces.
+                edestoppo(neightet, hulltet); // [a,b,e,d]
+                fsymself(hulltet); // [b,a,e,#]
+                if (oppo(hulltet) == dummypoint) {
+                  pe = org(neightet);
+                  if ((pointtype(pe) == FREEFACETVERTEX) ||
+                      (pointtype(pe) == FREESEGVERTEX)) {
+                    removevertexbyflips(pe);
+                  }
+                } else {
+                  eorgoppo(neightet, hulltet); // [b,a,d,e]
+                  fsymself(hulltet); // [a,b,d,#]
+                  if (oppo(hulltet) == dummypoint) {
+                    pd = dest(neightet);
+                    if ((pointtype(pd) == FREEFACETVERTEX) ||
+                        (pointtype(pd) == FREESEGVERTEX)) {
+                      removevertexbyflips(pd);
+                    }
+                  } else {
+                    // Perform a 3-to-2 flip to remove the sliver.
+                    fliptets[0] = neightet;          // [e,d,a,b]
+                    fnext(fliptets[0], fliptets[1]); // [e,d,b,c]
+                    fnext(fliptets[1], fliptets[2]); // [e,d,c,a]
+                    flip32(fliptets, 1, fc);
+                    // Update counters.
+                    flip32count--;
+                    flip22count--;
+                    sliver_peels++;
+                    if (fc->remove_ndelaunay_edge) {
+                      // Update the volume (must be decreased).
+                      //assert(fc->tetprism_vol_sum <= 0);
+                      tetprism_vol_sum += fc->tetprism_vol_sum;
+                      fc->tetprism_vol_sum = 0.0; // Clear it.
+                    }
+                  }
+                }
+                break;
+              } // if (neightet.tet == fliptets[0].tet)
+            } // if (!isshsubseg(checksh))
+            senextself(checksh);
+          } // i
+        } // if (nonconvex)
+        continue;
+      }
+
+      if (checksubfaceflag) {
+        // Do not flip if it is a subface.
+        if (issubface(fliptets[0])) continue;
+      }
+
+      // Test whether the face is locally Delaunay or not.
+      pts = (point *) fliptets[1].tet; 
+      sign = insphere_s(pts[4], pts[5], pts[6], pts[7], oppo(fliptets[0]));
+
+      if (sign < 0) {
+        // A non-Delaunay face. Try to flip it.
+        pd = oppo(fliptets[0]);
+        pe = oppo(fliptets[1]);
+
+        // Use the length of the edge [d,e] as a reference to determine
+        //   a nearly degenerated new tet.
+        len3 = distance(pd, pe);
+        len3 = (len3 * len3 * len3);
+
+        // Check the convexity of its three edges. Stop checking either a
+        //   locally non-convex edge (ori < 0) or a flat edge (ori = 0) is
+        //   encountered, and 'fliptet' represents that edge.
+        for (i = 0; i < 3; i++) {
+          ori = orient3d(org(fliptets[0]), dest(fliptets[0]), pd, pe);
+          if (ori > 0) {
+            // Avoid creating a nearly degenerated new tet at boundary.
+            //   Re-use fliptets[2], fliptets[3];
+            esym(fliptets[0], fliptets[2]);
+            esym(fliptets[1], fliptets[3]);
+            if (issubface(fliptets[2]) || issubface(fliptets[3])) {
+              vol = orient3dfast(org(fliptets[0]), dest(fliptets[0]), pd, pe);
+              if ((fabs(vol) / len3) < b->epsilon) {
+                ori = 0.0; // Do rounding.
+              }
+            }
+          } // Rounding check
+          if (ori <= 0) break;
+          enextself(fliptets[0]);
+          eprevself(fliptets[1]);
+        }
+
+        if (ori > 0) {
+          // A 2-to-3 flip is found.
+          //   [0] [a,b,c,d], 
+          //   [1] [b,a,c,e]. no dummypoint.
+          flip23(fliptets, 0, fc);
+          flipcount++;
+          if (fc->remove_ndelaunay_edge) {
+            // Update the volume (must be decreased).
+            //assert(fc->tetprism_vol_sum <= 0);
+            tetprism_vol_sum += fc->tetprism_vol_sum;
+            fc->tetprism_vol_sum = 0.0; // Clear it.
+          }
+          continue;
+        } else { // ori <= 0
+          // The edge ('fliptets[0]' = [a',b',c',d]) is non-convex or flat,
+          //   where the edge [a',b'] is one of [a,b], [b,c], and [c,a].
+          if (checksubsegflag) {
+            // Do not flip if it is a segment.
+            if (issubseg(fliptets[0])) continue;
+          }
+          // Check if there are three or four tets sharing at this edge.        
+          esymself(fliptets[0]); // [b,a,d,c]
+          for (i = 0; i < 3; i++) {
+            fnext(fliptets[i], fliptets[i+1]);
+          }
+          if (fliptets[3].tet == fliptets[0].tet) {
+            // A 3-to-2 flip is found. (No hull tet.)
+            flip32(fliptets, 0, fc); 
+            flipcount++;
+            if (fc->remove_ndelaunay_edge) {
+              // Update the volume (must be decreased).
+              //assert(fc->tetprism_vol_sum <= 0);
+              tetprism_vol_sum += fc->tetprism_vol_sum;
+              fc->tetprism_vol_sum = 0.0; // Clear it.
+            }
+            continue;
+          } else {
+            // There are more than 3 tets at this edge.
+            fnext(fliptets[3], fliptets[4]);
+            if (fliptets[4].tet == fliptets[0].tet) {
+              // There are exactly 4 tets at this edge.
+              if (nonconvex) {
+                if (apex(fliptets[3]) == dummypoint) {
+                  // This edge is locally non-convex on the hull.
+                  // It can be removed by a 4-to-4 flip.                  
+                  ori = 0;
+                }
+              } // if (nonconvex)
+              if (ori == 0) {
+                // A 4-to-4 flip is found. (Two hull tets may be involved.)
+                // Current tets in 'fliptets':
+                //   [0] [b,a,d,c] (d may be newpt)
+                //   [1] [b,a,c,e]
+                //   [2] [b,a,e,f] (f may be dummypoint)
+                //   [3] [b,a,f,d]
+                esymself(fliptets[0]); // [a,b,c,d] 
+                // A 2-to-3 flip replaces face [a,b,c] by edge [e,d].
+                //   This creates a degenerate tet [e,d,a,b] (tmpfliptets[0]).
+                //   It will be removed by the followed 3-to-2 flip.
+                flip23(fliptets, 0, fc); // No hull tet.
+                fnext(fliptets[3], fliptets[1]);
+                fnext(fliptets[1], fliptets[2]);
+                // Current tets in 'fliptets':
+                //   [0] [...]
+                //   [1] [b,a,d,e] (degenerated, d may be new point).
+                //   [2] [b,a,e,f] (f may be dummypoint)
+                //   [3] [b,a,f,d]
+                // A 3-to-2 flip replaces edge [b,a] by face [d,e,f].
+                //   Hull tets may be involved (f may be dummypoint).
+                flip32(&(fliptets[1]), (apex(fliptets[3]) == dummypoint), fc);
+                flipcount++;
+                flip23count--;
+                flip32count--;
+                flip44count++;
+                if (fc->remove_ndelaunay_edge) {
+                  // Update the volume (must be decreased).
+                  //assert(fc->tetprism_vol_sum <= 0);
+                  tetprism_vol_sum += fc->tetprism_vol_sum;
+                  fc->tetprism_vol_sum = 0.0; // Clear it.
+                }
+                continue;
+              } // if (ori == 0)
+            }
+          }
+        } // if (ori <= 0)
+
+        // This non-Delaunay face is unflippable. Save it.
+        unflipqueue->newindex((void **) &bface);
+        bface->tt = fliptets[0];
+        bface->forg  = org(fliptets[0]);
+        bface->fdest = dest(fliptets[0]);
+        bface->fapex = apex(fliptets[0]);
+      } // if (sign < 0)
+    } // while (flipstack)
+
+    if (b->verbose > 2) {
+      if (flipcount > 0) {
+        printf("      Performed %ld flips.\n", flipcount);
+      }
+    }
+    // Accumulate the counter of flips.
+    totalcount += flipcount;
+
+    // Return if no unflippable faces left.
+    if (unflipqueue->objects == 0l) break; 
+    // Return if no flip has been performed.
+    if (flipcount == 0l) break;
+
+    // Try to flip the unflippable faces.
+    for (i = 0; i < unflipqueue->objects; i++) {
+      bface = (badface *) fastlookup(unflipqueue, i);
+      if (!isdeadtet(bface->tt) && 
+          (org(bface->tt) == bface->forg) &&
+          (dest(bface->tt) == bface->fdest) &&
+          (apex(bface->tt) == bface->fapex)) {
+        flippush(flipstack, &(bface->tt));
+      }
+    }
+    unflipqueue->restart();
+
+  } // while (1)
+
+  if (b->verbose > 2) {
+    if (totalcount > 0) {
+      printf("      Performed %ld flips.\n", totalcount);
+    }
+    if (sliver_peels > 0) {
+      printf("      Removed %ld hull slivers.\n", sliver_peels);
+    }
+    if (unflipqueue->objects > 0l) {
+      printf("      %ld unflippable edges remained.\n", unflipqueue->objects);
+    }
+  }
+
+  return totalcount + sliver_peels;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// recoverdelaunay()    Recovery the locally Delaunay property.              //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::recoverdelaunay()
+{
+  arraypool *flipqueue, *nextflipqueue, *swapqueue;
+  triface tetloop, neightet, *parytet;
+  badface *bface, *parybface;
+  point *ppt;
+  flipconstraints fc;
+  int i, j;
+
+  if (!b->quiet) {
+    printf("Recovering Delaunayness...\n");
+  }
+
+  tetprism_vol_sum = 0.0; // Initialize it.
+
+  // Put all interior faces of the mesh into 'flipstack'.
+  tetrahedrons->traversalinit();
+  tetloop.tet = tetrahedrontraverse();
+  while (tetloop.tet != NULL) {
+    for (tetloop.ver = 0; tetloop.ver < 4; tetloop.ver++) {
+      decode(tetloop.tet[tetloop.ver], neightet);
+      if (!facemarked(neightet)) {
+        flippush(flipstack, &tetloop);
+      }
+    }
+    ppt = (point *) &(tetloop.tet[4]);
+    tetprism_vol_sum += tetprismvol(ppt[0], ppt[1], ppt[2], ppt[3]);
+    tetloop.tet = tetrahedrontraverse();
+  }
+
+  // Calulate a relatively lower bound for small improvement. 
+  //   Used to avoid rounding error in volume calculation.
+  fc.bak_tetprism_vol = tetprism_vol_sum * b->epsilon * 1e-3;
+
+  if (b->verbose) {
+    printf("  Initial obj = %.17g\n", tetprism_vol_sum);
+  }
+
+  if (b->verbose > 1) {
+    printf("    Recover Delaunay [Lawson] : %ld\n", flippool->items);
+  }
+
+  // First only use the basic Lawson's flip.
+  fc.remove_ndelaunay_edge = 1;
+  fc.enqflag = 2;
+
+  lawsonflip3d(&fc);
+
+  if (b->verbose > 1) {
+    printf("    obj (after Lawson) = %.17g\n", tetprism_vol_sum);
+  }
+
+  if (unflipqueue->objects == 0l) {
+    return; // The mesh is Delaunay.
+  }
+
+  fc.unflip = 1; // Unflip if the edge is not flipped.
+  fc.collectnewtets = 1; // new tets are returned in 'cavetetlist'.
+  fc.enqflag = 0;
+
+  autofliplinklevel = 1; // Init level.
+  b->fliplinklevel = -1; // No fixed level.
+
+  // For efficiency reason, we limit the maximium size of the edge star.
+  int bakmaxflipstarsize = b->flipstarsize;
+  b->flipstarsize = 10; // default
+
+  flipqueue = new arraypool(sizeof(badface), 10);
+  nextflipqueue = new arraypool(sizeof(badface), 10);
+  
+  // Swap the two flip queues.
+  swapqueue = flipqueue;
+  flipqueue = unflipqueue;
+  unflipqueue = swapqueue;
+
+  while (flipqueue->objects > 0l) {
+
+    if (b->verbose > 1) {
+      printf("    Recover Delaunay [level = %2d] #:  %ld.\n",
+             autofliplinklevel, flipqueue->objects);
+    }
+
+    for (i = 0; i < flipqueue->objects; i++) {
+      bface  = (badface *) fastlookup(flipqueue, i);
+      if (getedge(bface->forg, bface->fdest, &bface->tt)) {
+        if (removeedgebyflips(&(bface->tt), &fc) == 2) {
+          tetprism_vol_sum += fc.tetprism_vol_sum;
+          fc.tetprism_vol_sum = 0.0; // Clear it.
+          // Queue new faces for flips.
+          for (j = 0; j < cavetetlist->objects; j++) {
+            parytet = (triface *) fastlookup(cavetetlist, j);
+            // A queued new tet may be dead.
+            if (!isdeadtet(*parytet)) {
+              for (parytet->ver = 0; parytet->ver < 4; parytet->ver++) {
+                // Avoid queue a face twice.
+                decode(parytet->tet[parytet->ver], neightet);
+                if (!facemarked(neightet)) {
+                  flippush(flipstack, parytet);
+                }
+              } // parytet->ver
+            }
+          } // j
+          cavetetlist->restart();
+          // Remove locally non-Delaunay faces. New non-Delaunay edges
+          //   may be found. They are saved in 'unflipqueue'.
+          fc.enqflag = 2;
+          lawsonflip3d(&fc);
+          fc.enqflag = 0;
+          // There may be unflipable faces. Add them in flipqueue.
+          for (j = 0; j < unflipqueue->objects; j++) {
+            bface  = (badface *) fastlookup(unflipqueue, j);
+            flipqueue->newindex((void **) &parybface);
+            *parybface = *bface;
+          }
+          unflipqueue->restart();
+        } else {
+          // Unable to remove this edge. Save it.
+          nextflipqueue->newindex((void **) &parybface);
+          *parybface = *bface;
+          // Normally, it should be zero. 
+          //assert(fc.tetprism_vol_sum == 0.0);
+          // However, due to rounding errors, a tiny value may appear.
+          fc.tetprism_vol_sum = 0.0;
+        }
+      }
+    } // i
+
+    if (b->verbose > 1) {
+      printf("    obj (after level %d) = %.17g.\n", autofliplinklevel,
+             tetprism_vol_sum);
+    }
+    flipqueue->restart();
+
+    // Swap the two flip queues.
+    swapqueue = flipqueue;
+    flipqueue = nextflipqueue;
+    nextflipqueue = swapqueue;
+
+    if (flipqueue->objects > 0l) {
+      // default 'b->delmaxfliplevel' is 1.
+      if (autofliplinklevel >= b->delmaxfliplevel) {
+        // For efficiency reason, we do not search too far.
+        break;
+      }
+      autofliplinklevel+=b->fliplinklevelinc;
+    }
+  } // while (flipqueue->objects > 0l)
+
+  if (flipqueue->objects > 0l) {
+    if (b->verbose > 1) {
+      printf("    %ld non-Delaunay edges remained.\n", flipqueue->objects);
+    }
+  }
+
+  if (b->verbose) {
+    printf("  Final obj  = %.17g\n", tetprism_vol_sum);
+  }
+
+  b->flipstarsize = bakmaxflipstarsize;
+  delete flipqueue;
+  delete nextflipqueue;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// gettetrahedron()    Get a tetrahedron which have the given vertices.      //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+int tetgenmesh::gettetrahedron(point pa, point pb, point pc, point pd, 
+                               triface *searchtet)
+{
+  triface spintet;
+  int t1ver; 
+
+  if (getedge(pa, pb, searchtet)) {
+    spintet = *searchtet;
+    while (1) {
+      if (apex(spintet) == pc) {
+        *searchtet = spintet;
+        break;
+      }
+      fnextself(spintet);
+      if (spintet.tet == searchtet->tet) break;
+    }
+    if (apex(*searchtet) == pc) {
+      if (oppo(*searchtet) == pd) {
+        return 1;
+      } else {
+        fsymself(*searchtet);
+        if (oppo(*searchtet) == pd) {
+          return 1;
+        }
+      }
+    }
+  }
+
+  return 0;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// improvequalitybyflips()    Improve the mesh quality by flips.             //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+long tetgenmesh::improvequalitybyflips()
+{
+  arraypool *flipqueue, *nextflipqueue, *swapqueue;
+  badface *bface, *parybface;
+  triface *parytet;
+  point *ppt;
+  flipconstraints fc;
+  REAL *cosdd, ncosdd[6], maxdd;
+  long totalremcount, remcount;
+  int remflag;
+  int n, i, j, k;
+
+  //assert(unflipqueue->objects > 0l);
+  flipqueue = new arraypool(sizeof(badface), 10);
+  nextflipqueue = new arraypool(sizeof(badface), 10);
+
+  // Backup flip edge options.
+  int bakautofliplinklevel = autofliplinklevel;
+  int bakfliplinklevel = b->fliplinklevel;
+  int bakmaxflipstarsize = b->flipstarsize;
+
+  // Set flip edge options.
+  autofliplinklevel = 1; 
+  b->fliplinklevel = -1;
+  b->flipstarsize = 10; // b->optmaxflipstarsize;
+
+  fc.remove_large_angle = 1;
+  fc.unflip = 1;
+  fc.collectnewtets = 1;
+  fc.checkflipeligibility = 1;
+
+  totalremcount = 0l;
+
+  // Swap the two flip queues.
+  swapqueue = flipqueue;
+  flipqueue = unflipqueue;
+  unflipqueue = swapqueue;
+
+  while (flipqueue->objects > 0l) {
+
+    remcount = 0l;
+
+    while (flipqueue->objects > 0l) {
+      if (b->verbose > 1) {
+        printf("    Improving mesh qualiy by flips [%d]#:  %ld.\n",
+               autofliplinklevel, flipqueue->objects);
+      }
+
+      for (k = 0; k < flipqueue->objects; k++) {
+        bface  = (badface *) fastlookup(flipqueue, k);
+        if (gettetrahedron(bface->forg, bface->fdest, bface->fapex,
+                           bface->foppo, &bface->tt)) {
+          //assert(!ishulltet(bface->tt));
+          // There are bad dihedral angles in this tet.
+          if (bface->tt.ver != 11) {
+            // The dihedral angles are permuted.
+            // Here we simply re-compute them. Slow!!.
+            ppt = (point *) & (bface->tt.tet[4]);
+            tetalldihedral(ppt[0], ppt[1], ppt[2], ppt[3], bface->cent, 
+                           &bface->key, NULL);
+            bface->forg = ppt[0];
+            bface->fdest = ppt[1];
+            bface->fapex = ppt[2];
+            bface->foppo = ppt[3];
+            bface->tt.ver = 11;
+          }
+          if (bface->key == 0) {
+            // Re-comput the quality values. Due to smoothing operations.
+            ppt = (point *) & (bface->tt.tet[4]);
+            tetalldihedral(ppt[0], ppt[1], ppt[2], ppt[3], bface->cent, 
+                           &bface->key, NULL);
+          }
+          cosdd = bface->cent;
+          remflag = 0;
+          for (i = 0; (i < 6) && !remflag; i++) {
+            if (cosdd[i] < cosmaxdihed) {
+              // Found a large dihedral angle.
+              bface->tt.ver = edge2ver[i]; // Go to the edge.
+              fc.cosdihed_in = cosdd[i];
+              fc.cosdihed_out = 0.0; // 90 degree.
+              n = removeedgebyflips(&(bface->tt), &fc);
+              if (n == 2) {
+                // Edge is flipped.
+                remflag = 1;
+                if (fc.cosdihed_out < cosmaxdihed) {
+                  // Queue new bad tets for further improvements.
+                  for (j = 0; j < cavetetlist->objects; j++) {
+                    parytet = (triface *) fastlookup(cavetetlist, j);
+                    if (!isdeadtet(*parytet)) {
+                      ppt = (point *) & (parytet->tet[4]);
+                      // Do not test a hull tet.
+                      if (ppt[3] != dummypoint) {
+                        tetalldihedral(ppt[0], ppt[1], ppt[2], ppt[3], ncosdd, 
+                                       &maxdd, NULL);
+                        if (maxdd < cosmaxdihed) {
+                          // There are bad dihedral angles in this tet.
+                          nextflipqueue->newindex((void **) &parybface); 
+                          parybface->tt.tet = parytet->tet;
+                          parybface->tt.ver = 11;
+                          parybface->forg = ppt[0];
+                          parybface->fdest = ppt[1];
+                          parybface->fapex = ppt[2];
+                          parybface->foppo = ppt[3];
+                          parybface->key = maxdd;
+                          for (n = 0; n < 6; n++) {
+                            parybface->cent[n] = ncosdd[n];
+                          }
+                        }
+                      } // if (ppt[3] != dummypoint) 
+                    }
+                  } // j
+                } // if (fc.cosdihed_out < cosmaxdihed)
+                cavetetlist->restart();
+                remcount++;
+              }
+            }
+          } // i          
+          if (!remflag) {
+            // An unremoved bad tet. Queue it again. 
+            unflipqueue->newindex((void **) &parybface);
+            *parybface = *bface;
+          }
+        } // if (gettetrahedron(...))
+      } // k
+
+      flipqueue->restart();
+
+      // Swap the two flip queues.
+      swapqueue = flipqueue;
+      flipqueue = nextflipqueue;
+      nextflipqueue = swapqueue;
+    } // while (flipqueues->objects > 0)
+
+    if (b->verbose > 1) {
+      printf("    Removed %ld bad tets.\n", remcount);
+    }
+    totalremcount += remcount;
+
+    if (unflipqueue->objects > 0l) {
+      //if (autofliplinklevel >= b->optmaxfliplevel) {
+      if (autofliplinklevel >= b->optlevel) {
+        break;
+      }
+      autofliplinklevel+=b->fliplinklevelinc;
+      //b->flipstarsize = 10 + (1 << (b->optlevel - 1));
+    }
+
+    // Swap the two flip queues.
+    swapqueue = flipqueue;
+    flipqueue = unflipqueue;
+    unflipqueue = swapqueue;
+  } // while (flipqueues->objects > 0)
+
+  // Restore original flip edge options.
+  autofliplinklevel = bakautofliplinklevel;
+  b->fliplinklevel = bakfliplinklevel;
+  b->flipstarsize = bakmaxflipstarsize;
+
+  delete flipqueue;
+  delete nextflipqueue;
+
+  return totalremcount;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// smoothpoint()    Moving a vertex to improve the mesh quality.             //
+//                                                                           //
+// 'smtpt' (p) is a point to be smoothed. Generally, it is a Steiner point.  //
+// It may be not a vertex of the mesh.                                       //
+//                                                                           //
+// This routine tries to move 'p' inside its star until a selected objective //
+// function over all tetrahedra in the star is improved. The function may be //
+// the some quality measures, i.e., aspect ratio, maximum dihedral angel, or //
+// simply the volume of the tetrahedra.                                      //
+//                                                                           //
+// 'linkfacelist' contains the list of link faces of 'p'.  Since a link face //
+// has two orientations, ccw or cw, with respect to 'p'.  'ccw' indicates    //
+// the orientation is ccw (1) or not (0).                                    //
+//                                                                           //
+// 'opm' is a structure contains the parameters of the objective function.   //
+// It is needed by the evaluation of the function value.                     //
+//                                                                           //
+// The return value indicates weather the point is smoothed or not.          //
+//                                                                           //
+// ASSUMPTION: This routine assumes that all link faces are true faces, i.e, //
+// no face has 'dummypoint' as its vertex.                                   //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+int tetgenmesh::smoothpoint(point smtpt, arraypool *linkfacelist, int ccw,
+                            optparameters *opm)
+{
+  triface *parytet, *parytet1, swaptet;
+  point pa, pb, pc;
+  REAL fcent[3], startpt[3], nextpt[3], bestpt[3];
+  REAL oldval, minval = 0.0, val;
+  REAL maxcosd; // oldang, newang;
+  REAL ori, diff;
+  int numdirs, iter;
+  int i, j, k;
+
+  // Decide the number of moving directions.
+  numdirs = (int) linkfacelist->objects;
+  if (numdirs > opm->numofsearchdirs) {
+    numdirs = opm->numofsearchdirs; // Maximum search directions.
+  }
+
+  // Set the initial value.
+  opm->imprval = opm->initval;
+  iter = 0;
+
+  for (i = 0; i < 3; i++) {
+    bestpt[i] = startpt[i] = smtpt[i];
+  }
+
+  // Iterate until the obj function is not improved.
+  while (1) {
+
+    // Find the best next location.
+    oldval = opm->imprval;
+
+    for (i = 0; i < numdirs; i++) {
+      // Randomly pick a link face (0 <= k <= objects - i - 1).
+      k = (int) randomnation(linkfacelist->objects - i);
+      parytet = (triface *) fastlookup(linkfacelist, k);
+      // Calculate a new position from 'p' to the center of this face.
+      pa = org(*parytet);
+      pb = dest(*parytet);
+      pc = apex(*parytet);
+      for (j = 0; j < 3; j++) {
+        fcent[j] = (pa[j] + pb[j] + pc[j]) / 3.0;
+      }
+      for (j = 0; j < 3; j++) {
+        nextpt[j] = startpt[j] + opm->searchstep * (fcent[j] - startpt[j]); 
+      }
+      // Calculate the largest minimum function value for the new location.
+      for (j = 0; j < linkfacelist->objects; j++) {
+        parytet = (triface *) fastlookup(linkfacelist, j);
+        if (ccw) {
+          pa = org(*parytet);
+          pb = dest(*parytet);
+        } else {
+          pb = org(*parytet);
+          pa = dest(*parytet);
+        }
+        pc = apex(*parytet);
+        ori = orient3d(pa, pb, pc, nextpt);
+        if (ori < 0.0) {
+          // Calcuate the objective function value. 
+          if (opm->max_min_volume) {
+            //val = -ori;
+            val = - orient3dfast(pa, pb, pc, nextpt);
+          } else if (opm->min_max_aspectratio) {
+            val = 1.0 / tetaspectratio(pa, pb, pc, nextpt);
+          } else if (opm->min_max_dihedangle) {
+            tetalldihedral(pa, pb, pc, nextpt, NULL, &maxcosd, NULL);
+            if (maxcosd < -1) maxcosd = -1.0; // Rounding.
+            val = maxcosd + 1.0; // Make it be positive. 
+          } else {
+            // Unknown objective function.
+            val = 0.0;
+          }  
+        } else { // ori >= 0.0;
+          // An invalid new tet. 
+          // This may happen if the mesh contains inverted elements.
+          if (opm->max_min_volume) {
+            //val = -ori;
+            val = - orient3dfast(pa, pb, pc, nextpt);    
+          } else {
+            // Discard this point.
+            break; // j
+          }
+        } // if (ori >= 0.0)
+        // Stop looping when the object value is not improved.
+        if (val <= opm->imprval) {
+          break; // j
+        } else {
+          // Remember the smallest improved value.
+          if (j == 0) {
+            minval = val;
+          } else {
+            minval = (val < minval) ? val : minval;
+          }
+        }
+      } // j
+      if (j == linkfacelist->objects) {
+        // The function value has been improved.
+        opm->imprval = minval;
+        // Save the new location of the point.
+        for (j = 0; j < 3; j++) bestpt[j] = nextpt[j];
+      }
+      // Swap k-th and (object-i-1)-th entries.
+      j = linkfacelist->objects - i - 1;
+      parytet  = (triface *) fastlookup(linkfacelist, k);
+      parytet1 = (triface *) fastlookup(linkfacelist, j);
+      swaptet = *parytet1;
+      *parytet1 = *parytet;
+      *parytet = swaptet;
+    } // i
+
+    diff = opm->imprval - oldval;
+    if (diff > 0.0) {
+      // Is the function value improved effectively?
+      if (opm->max_min_volume) {
+        //if ((diff / oldval) < b->epsilon) diff = 0.0;  
+      } else if (opm->min_max_aspectratio) {
+        if ((diff / oldval) < 1e-3) diff = 0.0;
+      } else if (opm->min_max_dihedangle) {
+        //oldang = acos(oldval - 1.0);
+        //newang = acos(opm->imprval - 1.0);
+        //if ((oldang - newang) < 0.00174) diff = 0.0; // about 0.1 degree.
+      } else {
+        // Unknown objective function.
+        terminatetetgen(this, 2); 
+      }
+    }
+
+    if (diff > 0.0) {
+      // Yes, move p to the new location and continue.
+      for (j = 0; j < 3; j++) startpt[j] = bestpt[j];
+      iter++;
+      if ((opm->maxiter > 0) && (iter >= opm->maxiter)) {
+        // Maximum smoothing iterations reached.
+        break;
+      }
+    } else {
+      break;
+    }
+
+  } // while (1)
+
+  if (iter > 0) {
+    // The point has been smoothed.
+    opm->smthiter = iter; // Remember the number of iterations. 
+    // The point has been smoothed. Update it to its new position.
+    for (i = 0; i < 3; i++) smtpt[i] = startpt[i];
+  }
+
+  return iter;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// improvequalitysmoothing()    Improve mesh quality by smoothing.           //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+long tetgenmesh::improvequalitybysmoothing(optparameters *opm)
+{
+  arraypool *flipqueue, *swapqueue;
+  triface *parytet;
+  badface *bface, *parybface;
+  point *ppt;
+  long totalsmtcount, smtcount;
+  int smtflag;
+  int iter, i, j, k;
+
+  //assert(unflipqueue->objects > 0l);
+  flipqueue = new arraypool(sizeof(badface), 10);
+
+  // Swap the two flip queues.
+  swapqueue = flipqueue;
+  flipqueue = unflipqueue;
+  unflipqueue = swapqueue;
+
+  totalsmtcount = 0l;
+  iter = 0;
+
+  while (flipqueue->objects > 0l) {
+
+    smtcount = 0l;
+
+    if (b->verbose > 1) {
+      printf("    Improving mesh quality by smoothing [%d]#:  %ld.\n",
+             iter, flipqueue->objects);
+    }
+
+    for (k = 0; k < flipqueue->objects; k++) {      
+      bface  = (badface *) fastlookup(flipqueue, k);
+      if (gettetrahedron(bface->forg, bface->fdest, bface->fapex,
+                         bface->foppo, &bface->tt)) {
+        // Operate on it if it is not in 'unflipqueue'.
+        if (!marktested(bface->tt)) {
+          // Here we simply re-compute the quality. Since other smoothing
+          //   operation may have moved the vertices of this tet.
+          ppt = (point *) & (bface->tt.tet[4]);
+          tetalldihedral(ppt[0], ppt[1], ppt[2], ppt[3], bface->cent, 
+                         &bface->key, NULL);
+          if (bface->key < cossmtdihed) { // if (maxdd < cosslidihed) {
+            // It is a sliver. Try to smooth its vertices.
+            smtflag = 0;
+            opm->initval = bface->key + 1.0; 
+            for (i = 0; (i < 4) && !smtflag; i++) {
+              if (pointtype(ppt[i]) == FREEVOLVERTEX) {
+                getvertexstar(1, ppt[i], cavetetlist, NULL, NULL);
+                opm->searchstep = 0.001; // Search step size
+                smtflag = smoothpoint(ppt[i], cavetetlist, 1, opm);
+                if (smtflag) {
+                  while (opm->smthiter == opm->maxiter) {
+                    opm->searchstep *= 10.0; // Increase the step size.
+                    opm->initval = opm->imprval;
+                    opm->smthiter = 0; // reset
+                    smoothpoint(ppt[i], cavetetlist, 1, opm);
+                  }
+                  // This tet is modifed.
+                  smtcount++;
+                  if ((opm->imprval - 1.0) < cossmtdihed) {
+                    // There are slivers in new tets. Queue them.
+                    for (j = 0; j < cavetetlist->objects; j++) {
+                      parytet = (triface *) fastlookup(cavetetlist, j);
+                      // Operate it if it is not in 'unflipqueue'.
+                      if (!marktested(*parytet)) {
+                        // Evaluate its quality.
+                        // Re-use ppt, bface->key, bface->cent.
+                        ppt = (point *) & (parytet->tet[4]);
+                        tetalldihedral(ppt[0], ppt[1], ppt[2], ppt[3], 
+                                       bface->cent, &bface->key, NULL);
+                        if (bface->key < cossmtdihed) {
+                          // A new sliver. Queue it.
+                          marktest(*parytet); // It is in unflipqueue.
+                          unflipqueue->newindex((void **) &parybface);
+                          parybface->tt = *parytet;
+                          parybface->forg = ppt[0];
+                          parybface->fdest = ppt[1];
+                          parybface->fapex = ppt[2];
+                          parybface->foppo = ppt[3];
+                          parybface->tt.ver = 11; 
+                          parybface->key = 0.0;
+                        }
+                      }
+                    } // j
+                  } // if ((opm->imprval - 1.0) < cossmtdihed)
+                } // if (smtflag)
+                cavetetlist->restart();
+              } // if (pointtype(ppt[i]) == FREEVOLVERTEX)
+            } // i
+            if (!smtflag) {
+              // Didn't smooth. Queue it again.
+              marktest(bface->tt); // It is in unflipqueue.
+              unflipqueue->newindex((void **) &parybface);
+              parybface->tt = bface->tt;
+              parybface->forg = ppt[0];
+              parybface->fdest = ppt[1];
+              parybface->fapex = ppt[2];
+              parybface->foppo = ppt[3];
+              parybface->tt.ver = 11;
+              parybface->key = 0.0;
+            }
+	      } // if (maxdd < cosslidihed)
+        } // if (!marktested(...))
+      } // if (gettetrahedron(...))
+    } // k
+
+    flipqueue->restart();
+
+    // Unmark the tets in unflipqueue.
+    for (i = 0; i < unflipqueue->objects; i++) {
+      bface  = (badface *) fastlookup(unflipqueue, i);
+      unmarktest(bface->tt);
+    }
+
+    if (b->verbose > 1) {
+      printf("    Smooth %ld points.\n", smtcount);
+    }
+    totalsmtcount += smtcount;
+
+    if (smtcount == 0l) {
+      // No point has been smoothed. 
+      break;
+    } else {
+      iter++;
+      if (iter == 2) { //if (iter >= b->optpasses) {
+        break;
+      }
+    }
+
+    // Swap the two flip queues.
+    swapqueue = flipqueue;
+    flipqueue = unflipqueue;
+    unflipqueue = swapqueue;
+  } // while
+
+  delete flipqueue;
+
+  return totalsmtcount;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// splitsliver()    Split a sliver.                                          //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+int tetgenmesh::splitsliver(triface *slitet, REAL cosd, int chkencflag)
+{
+  triface *abtets;
+  triface searchtet, spintet, *parytet;
+  point pa, pb, steinerpt;
+  optparameters opm;
+  insertvertexflags ivf;
+  REAL smtpt[3], midpt[3];
+  int success;
+  int t1ver;
+  int n, i;
+
+  // 'slitet' is [c,d,a,b], where [c,d] has a big dihedral angle. 
+  // Go to the opposite edge [a,b].
+  edestoppo(*slitet, searchtet); // [a,b,c,d].
+
+  // Do not split a segment.
+  if (issubseg(searchtet)) {
+    return 0; 
+  }
+
+  // Count the number of tets shared at [a,b].
+  // Do not split it if it is a hull edge.
+  spintet = searchtet;
+  n = 0; 
+  while (1) {
+    if (ishulltet(spintet)) break;
+    n++;
+    fnextself(spintet);
+    if (spintet.tet == searchtet.tet) break;
+  }
+  if (ishulltet(spintet)) {
+    return 0; // It is a hull edge.
+  }
+
+  // Get all tets at edge [a,b].
+  abtets = new triface[n];
+  spintet = searchtet;
+  for (i = 0; i < n; i++) {
+    abtets[i] = spintet;
+    fnextself(spintet);
+  }
+
+  // Initialize the list of 2n boundary faces.
+  for (i = 0; i < n; i++) {    
+    eprev(abtets[i], searchtet);
+    esymself(searchtet); // [a,p_i,p_i+1].
+    cavetetlist->newindex((void **) &parytet);
+    *parytet = searchtet;
+    enext(abtets[i], searchtet);
+    esymself(searchtet); // [p_i,b,p_i+1].
+    cavetetlist->newindex((void **) &parytet);
+    *parytet = searchtet;
+  }
+
+  // Init the Steiner point at the midpoint of edge [a,b].
+  pa = org(abtets[0]);
+  pb = dest(abtets[0]);
+  for (i = 0; i < 3; i++) {
+    smtpt[i] = midpt[i] = 0.5 * (pa[i] + pb[i]);
+  }
+
+  // Point smooth options.
+  opm.min_max_dihedangle = 1;
+  opm.initval = cosd + 1.0; // Initial volume is zero.
+  opm.numofsearchdirs = 20;
+  opm.searchstep = 0.001;  
+  opm.maxiter = 100; // Limit the maximum iterations.
+
+  success = smoothpoint(smtpt, cavetetlist, 1, &opm);
+
+  if (success) {
+    while (opm.smthiter == opm.maxiter) {
+      // It was relocated and the prescribed maximum iteration reached. 
+      // Try to increase the search stepsize.
+      opm.searchstep *= 10.0;
+      //opm.maxiter = 100; // Limit the maximum iterations.
+      opm.initval = opm.imprval;
+      opm.smthiter = 0; // Init.
+      smoothpoint(smtpt, cavetetlist, 1, &opm);  
+    }
+  } // if (success)
+
+  cavetetlist->restart();
+
+  if (!success) {
+    delete [] abtets;
+    return 0;
+  }
+
+
+  // Insert the Steiner point.
+  makepoint(&steinerpt, FREEVOLVERTEX);
+  for (i = 0; i < 3; i++) steinerpt[i] = smtpt[i];
+
+  // Insert the created Steiner point.
+  for (i = 0; i < n; i++) {
+    infect(abtets[i]);
+    caveoldtetlist->newindex((void **) &parytet);
+    *parytet = abtets[i];
+  }
+
+  searchtet = abtets[0]; // No need point location.
+  if (b->metric) {
+    locate(steinerpt, &searchtet); // For size interpolation.
+  }
+
+  delete [] abtets;
+
+  ivf.iloc = (int) INSTAR;
+  ivf.chkencflag = chkencflag;
+  ivf.assignmeshsize = b->metric; 
+
+
+  if (insertpoint(steinerpt, &searchtet, NULL, NULL, &ivf)) {
+    // The vertex has been inserted.
+    st_volref_count++; 
+    if (steinerleft > 0) steinerleft--;
+    return 1;
+  } else {
+    // The Steiner point is too close to an existing vertex. Reject it.
+    pointdealloc(steinerpt);
+    return 0;
+  }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// removeslivers()    Remove slivers by adding Steiner points.               //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+long tetgenmesh::removeslivers(int chkencflag)
+{
+  arraypool *flipqueue, *swapqueue;
+  badface *bface, *parybface;
+  triface slitet, *parytet;
+  point *ppt;
+  REAL cosdd[6], maxcosd;
+  long totalsptcount, sptcount;
+  int iter, i, j, k;
+
+  //assert(unflipqueue->objects > 0l);
+  flipqueue = new arraypool(sizeof(badface), 10);
+
+  // Swap the two flip queues.
+  swapqueue = flipqueue;
+  flipqueue = unflipqueue;
+  unflipqueue = swapqueue;
+
+  totalsptcount = 0l;
+  iter = 0;
+
+  while ((flipqueue->objects > 0l) && (steinerleft != 0)) {
+
+    sptcount = 0l;
+
+    if (b->verbose > 1) {
+      printf("    Splitting bad quality tets [%d]#:  %ld.\n",
+             iter, flipqueue->objects);
+    }
+
+    for (k = 0; (k < flipqueue->objects) && (steinerleft != 0); k++) {      
+      bface  = (badface *) fastlookup(flipqueue, k);
+      if (gettetrahedron(bface->forg, bface->fdest, bface->fapex,
+                         bface->foppo, &bface->tt)) {
+        if ((bface->key == 0) || (bface->tt.ver != 11)) {
+          // Here we need to re-compute the quality. Since other smoothing
+          //   operation may have moved the vertices of this tet.
+          ppt = (point *) & (bface->tt.tet[4]);
+          tetalldihedral(ppt[0], ppt[1], ppt[2], ppt[3], bface->cent, 
+                         &bface->key, NULL);
+        }
+        if (bface->key < cosslidihed) { 
+          // It is a sliver. Try to split it.
+          slitet.tet = bface->tt.tet;
+          //cosdd = bface->cent;
+          for (j = 0; j < 6; j++) {
+            if (bface->cent[j] < cosslidihed) { 
+              // Found a large dihedral angle.
+              slitet.ver = edge2ver[j]; // Go to the edge.
+              if (splitsliver(&slitet, bface->cent[j], chkencflag)) {
+                sptcount++;
+                break;
+              }
+            }
+          } // j
+          if (j < 6) {
+            // A sliver is split. Queue new slivers.
+            badtetrahedrons->traversalinit();
+            parytet = (triface *) badtetrahedrons->traverse();
+            while (parytet != NULL) {
+              unmarktest2(*parytet);
+              ppt = (point *) & (parytet->tet[4]);
+              tetalldihedral(ppt[0], ppt[1], ppt[2], ppt[3], cosdd, 
+                             &maxcosd, NULL);
+              if (maxcosd < cosslidihed) {
+                // A new sliver. Queue it.
+                unflipqueue->newindex((void **) &parybface);
+                parybface->forg = ppt[0];
+                parybface->fdest = ppt[1];
+                parybface->fapex = ppt[2];
+                parybface->foppo = ppt[3];
+                parybface->tt.tet = parytet->tet;
+                parybface->tt.ver = 11;
+                parybface->key = maxcosd;
+                for (i = 0; i < 6; i++) {
+                  parybface->cent[i] = cosdd[i];
+                }
+              }
+              parytet = (triface *) badtetrahedrons->traverse();
+            }
+            badtetrahedrons->restart();
+          } else {
+            // Didn't split. Queue it again.
+            unflipqueue->newindex((void **) &parybface);
+            *parybface = *bface;
+          } // if (j == 6)
+        } // if (bface->key < cosslidihed)
+      } // if (gettetrahedron(...))
+    } // k
+
+    flipqueue->restart();
+
+    if (b->verbose > 1) {
+      printf("    Split %ld tets.\n", sptcount);
+    }
+    totalsptcount += sptcount;
+
+    if (sptcount == 0l) {
+      // No point has been smoothed. 
+      break;
+    } else {
+      iter++;
+      if (iter == 2) { //if (iter >= b->optpasses) {
+        break;
+      }
+    }
+
+    // Swap the two flip queues.
+    swapqueue = flipqueue;
+    flipqueue = unflipqueue;
+    unflipqueue = swapqueue;
+  } // while
+
+  delete flipqueue;
+
+  return totalsptcount;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// optimizemesh()    Optimize mesh for specified objective functions.        //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::optimizemesh()
+{
+  badface *parybface;
+  triface checktet;
+  point *ppt;
+  int optpasses;
+  optparameters opm;
+  REAL ncosdd[6], maxdd;
+  long totalremcount, remcount;
+  long totalsmtcount, smtcount;
+  long totalsptcount, sptcount;
+  int chkencflag;
+  int iter;
+  int n;
+
+  if (!b->quiet) {
+    printf("Optimizing mesh...\n");
+  }
+
+  optpasses = ((1 << b->optlevel) - 1);
+
+  if (b->verbose) {
+    printf("  Optimization level  = %d.\n", b->optlevel);
+    printf("  Optimization scheme = %d.\n", b->optscheme);
+    printf("  Number of iteration = %d.\n", optpasses);
+    printf("  Min_Max dihed angle = %g.\n", b->optmaxdihedral);
+  }
+
+  totalsmtcount = totalsptcount = totalremcount = 0l;
+
+  cosmaxdihed = cos(b->optmaxdihedral / 180.0 * PI);
+  cossmtdihed = cos(b->optminsmtdihed / 180.0 * PI);
+  cosslidihed = cos(b->optminslidihed / 180.0 * PI);
+
+  int attrnum = numelemattrib - 1; 
+
+  // Put all bad tetrahedra into array.
+  tetrahedrons->traversalinit();
+  checktet.tet = tetrahedrontraverse();
+  while (checktet.tet != NULL) {
+    if (b->convex) { // -c
+      // Skip this tet if it lies in the exterior.
+      if (elemattribute(checktet.tet, attrnum) == -1.0) {
+        checktet.tet = tetrahedrontraverse();
+        continue;
+      }
+    }
+    ppt = (point *) & (checktet.tet[4]);
+    tetalldihedral(ppt[0], ppt[1], ppt[2], ppt[3], ncosdd, &maxdd, NULL);
+    if (maxdd < cosmaxdihed) {
+      // There are bad dihedral angles in this tet.
+      unflipqueue->newindex((void **) &parybface); 
+      parybface->tt.tet = checktet.tet;
+      parybface->tt.ver = 11;
+      parybface->forg = ppt[0];
+      parybface->fdest = ppt[1];
+      parybface->fapex = ppt[2];
+      parybface->foppo = ppt[3];
+      parybface->key = maxdd;
+      for (n = 0; n < 6; n++) {
+        parybface->cent[n] = ncosdd[n];
+      }
+    }
+    checktet.tet = tetrahedrontraverse();
+  }
+
+  totalremcount = improvequalitybyflips();
+
+  if ((unflipqueue->objects > 0l) && 
+      ((b->optscheme & 2) || (b->optscheme & 4))) {
+    // The pool is only used by removeslivers().
+    badtetrahedrons = new memorypool(sizeof(triface), b->tetrahedraperblock,
+                                     sizeof(void *), 0);
+
+    // Smoothing options.
+    opm.min_max_dihedangle = 1;
+    opm.numofsearchdirs = 10;
+    // opm.searchstep = 0.001;  
+    opm.maxiter = 30; // Limit the maximum iterations.
+    //opm.checkencflag = 4; // Queue affected tets after smoothing.
+    chkencflag = 4; // Queue affected tets after splitting a sliver.
+    iter = 0;
+
+    while (iter < optpasses) {
+      smtcount = sptcount = remcount = 0l;
+      if (b->optscheme & 2) {
+        smtcount += improvequalitybysmoothing(&opm);
+        totalsmtcount += smtcount;
+        if (smtcount > 0l) {
+          remcount = improvequalitybyflips();
+          totalremcount += remcount;
+        }
+      }
+      if (unflipqueue->objects > 0l) {
+        if (b->optscheme & 4) {
+          sptcount += removeslivers(chkencflag);
+          totalsptcount += sptcount;
+          if (sptcount > 0l) {
+            remcount = improvequalitybyflips();
+            totalremcount += remcount;
+          }
+        }
+      }
+      if (unflipqueue->objects > 0l) {
+        if (remcount > 0l) {
+          iter++;
+        } else {
+          break;
+        }
+      } else {
+        break;
+      }
+    } // while (iter)
+
+    delete badtetrahedrons;
+    badtetrahedrons = NULL;
+  }
+
+  if (unflipqueue->objects > 0l) {
+    if (b->verbose > 1) {
+      printf("    %ld bad tets remained.\n", unflipqueue->objects);
+    }
+    unflipqueue->restart();
+  }
+
+  if (b->verbose) {
+    if (totalremcount > 0l) {
+      printf("  Removed %ld edges.\n", totalremcount);
+    }
+    if (totalsmtcount > 0l) {
+      printf("  Smoothed %ld points.\n", totalsmtcount);
+    }
+    if (totalsptcount > 0l) {
+      printf("  Split %ld slivers.\n", totalsptcount);
+    }
+  }
+}
+
+////                                                                       ////
+////                                                                       ////
+//// optimize_cxx /////////////////////////////////////////////////////////////
+
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// jettisonnodes()    Jettison unused or duplicated vertices.                //
+//                                                                           //
+// Unused points are those input points which are outside the mesh domain or //
+// have no connection (isolated) to the mesh.  Duplicated points exist for   //
+// example if the input PLC is read from a .stl mesh file (marked during the //
+// Delaunay tetrahedralization step. This routine remove these points from   //
+// points list. All existing points are reindexed.                           //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::jettisonnodes()
+{
+  point pointloop;
+  bool jetflag;
+  int oldidx, newidx;
+  int remcount;
+
+  if (!b->quiet) {
+    printf("Jettisoning redundant points.\n");
+  }
+
+  points->traversalinit();
+  pointloop = pointtraverse();
+  oldidx = newidx = 0; // in->firstnumber;
+  remcount = 0;
+  while (pointloop != (point) NULL) {
+    jetflag = (pointtype(pointloop) == DUPLICATEDVERTEX) || 
+      (pointtype(pointloop) == UNUSEDVERTEX);
+    if (jetflag) {
+      // It is a duplicated or unused point, delete it.
+      pointdealloc(pointloop);
+      remcount++;
+    } else {
+      // Re-index it.
+      setpointmark(pointloop, newidx + in->firstnumber);
+      if (in->pointmarkerlist != (int *) NULL) {
+        if (oldidx < in->numberofpoints) {
+          // Re-index the point marker as well.
+          in->pointmarkerlist[newidx] = in->pointmarkerlist[oldidx];
+        }
+      }
+      newidx++;
+    }
+    oldidx++;
+    pointloop = pointtraverse();
+  }
+  if (b->verbose) {
+    printf("  %ld duplicated vertices are removed.\n", dupverts);
+    printf("  %ld unused vertices are removed.\n", unuverts);
+  }
+  dupverts = 0l;
+  unuverts = 0l;
+
+  // The following line ensures that dead items in the pool of nodes cannot
+  //   be allocated for the new created nodes. This ensures that the input
+  //   nodes will occur earlier in the output files, and have lower indices.
+  points->deaditemstack = (void *) NULL;
+}
+
+
diff --git a/contrib/hxt/tetgenBR.h b/contrib/hxt/tetgenBR.h
new file mode 100644
index 0000000000000000000000000000000000000000..b88347e4de2a7511134076e3cfb697685446e1a3
--- /dev/null
+++ b/contrib/hxt/tetgenBR.h
@@ -0,0 +1,2589 @@
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// TetGen                                                                    //
+//                                                                           //
+// A Quality Tetrahedral Mesh Generator and A 3D Delaunay Triangulator       //
+//                                                                           //
+// Version 1.5                                                               //
+// May 31, 2014                                                              //
+//                                                                           //
+// Copyright (C) 2002--2016                                                  //
+//                                                                           //
+// TetGen is freely available through the website: http://www.tetgen.org.    //
+//   It may be copied, modified, and redistributed for non-commercial use.   //
+//   Please consult the file LICENSE for the detailed copyright notices.     //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+// The following code of this file was automatically generated.  Do not edit!
+// TetGenBR -- The Boundary Recovery code of TetGen.
+
+#ifndef tetgenBRH
+#define tetgenBRH
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// tetgenbehavior                                                            //
+//                                                                           //
+// A structure for maintaining the switches and parameters used by TetGen's  //
+// mesh data structure and algorithms.                                       //
+//                                                                           //
+// All switches and parameters are initialized with default values. They can //
+// be set by the command line arguments (a list of strings) of TetGen.       //
+//                                                                           //
+// NOTE: Some of the switches are incompatible. While some may depend on     //
+// other switches.  The routine parse_commandline() sets the switches from   //
+// the command line (a list of strings) and checks the consistency of the    //
+// applied switches.                                                         //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+class tetgenbehavior {
+
+public:
+
+  // Switches of TetGen. 
+  int plc;                                                         // '-p', 0.
+  int psc;                                                         // '-s', 0.
+  int refine;                                                      // '-r', 0.
+  int quality;                                                     // '-q', 0.
+  int nobisect;                                                    // '-Y', 0.
+  int coarsen;                                                     // '-R', 0.
+  int weighted;                                                    // '-w', 0.
+  int brio_hilbert;                                                // '-b', 1.
+  int incrflip;                                                    // '-l', 0.
+  int flipinsert;                                                  // '-L', 0.
+  int metric;                                                      // '-m', 0.
+  int varvolume;                                                   // '-a', 0.
+  int fixedvolume;                                                 // '-a', 0.
+  int regionattrib;                                                // '-A', 0.
+  int cdtrefine;                                                   // '-D', 0.
+  int insertaddpoints;                                             // '-i', 0.
+  int diagnose;                                                    // '-d', 0.
+  int convex;                                                      // '-c', 0.
+  int nomergefacet;                                                // '-M', 0.
+  int nomergevertex;                                               // '-M', 0.
+  int noexact;                                                     // '-X', 0.
+  int nostaticfilter;                                              // '-X', 0.
+  int zeroindex;                                                   // '-z', 0.
+  int facesout;                                                    // '-f', 0.
+  int edgesout;                                                    // '-e', 0.
+  int neighout;                                                    // '-n', 0.
+  int voroout;                                                     // '-v', 0.
+  int meditview;                                                   // '-g', 0.
+  int vtkview;                                                     // '-k', 0.
+  int nobound;                                                     // '-B', 0.
+  int nonodewritten;                                               // '-N', 0.
+  int noelewritten;                                                // '-E', 0.
+  int nofacewritten;                                               // '-F', 0.
+  int noiterationnum;                                              // '-I', 0.
+  int nojettison;                                                  // '-J', 0.
+  int docheck;                                                     // '-C', 0.
+  int quiet;                                                       // '-Q', 0.
+  int verbose;                                                     // '-V', 0.
+
+  // Parameters of TetGen. 
+  int vertexperblock;                                           // '-x', 4092.
+  int tetrahedraperblock;                                       // '-x', 8188.
+  int shellfaceperblock;                                        // '-x', 2044.
+  int nobisect_nomerge;                                            // '-Y', 1.
+  int supsteiner_level;                                           // '-Y/', 2.
+  int addsteiner_algo;                                           // '-Y//', 1.
+  int coarsen_param;                                               // '-R', 0.
+  int weighted_param;                                              // '-w', 0.
+  int fliplinklevel;                                                    // -1.
+  int flipstarsize;                                                     // -1.
+  int fliplinklevelinc;                                                 //  1.
+  int reflevel;                                                    // '-D', 3.
+  int optlevel;                                                    // '-O', 2.
+  int optscheme;                                                   // '-O', 7.
+  int delmaxfliplevel;                                                   // 1.
+  int order;                                                       // '-o', 1.
+  int reversetetori;                                              // '-o/', 0.
+  int steinerleft;                                                 // '-S', 0.
+  int no_sort;                                                           // 0.
+  int hilbert_order;                                           // '-b///', 52.
+  int hilbert_limit;                                             // '-b//'  8.
+  int brio_threshold;                                              // '-b' 64.
+  REAL brio_ratio;                                             // '-b/' 0.125.
+  REAL facet_separate_ang_tol;                                 // '-p', 179.9.
+  REAL facet_overlap_ang_tol;                                  // '-p/',  0.1.
+  REAL facet_small_ang_tol;                                   // '-p//', 15.0.
+  REAL maxvolume;                                               // '-a', -1.0.
+  REAL minratio;                                                 // '-q', 0.0.
+  REAL mindihedral;                                              // '-q', 5.0.
+  REAL optmaxdihedral;                                               // 165.0.
+  REAL optminsmtdihed;                                               // 179.0.
+  REAL optminslidihed;                                               // 179.0.  
+  REAL epsilon;                                               // '-T', 1.0e-8.
+  REAL coarsen_percent;                                         // -R1/#, 1.0.
+
+  // Strings of command line arguments and input/output file names.
+  char commandline[1024];
+  char infilename[1024];
+  char outfilename[1024];
+  char addinfilename[1024];
+  char bgmeshfilename[1024];
+
+  // The input object of TetGen. They are recognized by either the input 
+  //   file extensions or by the specified options. 
+  // Currently the following objects are supported:
+  //   - NODES, a list of nodes (.node); 
+  //   - POLY, a piecewise linear complex (.poly or .smesh); 
+  //   - OFF, a polyhedron (.off, Geomview's file format); 
+  //   - PLY, a polyhedron (.ply, file format from gatech, only ASCII);
+  //   - STL, a surface mesh (.stl, stereolithography format);
+  //   - MEDIT, a surface mesh (.mesh, Medit's file format); 
+  //   - MESH, a tetrahedral mesh (.ele).
+  // If no extension is available, the imposed command line switch
+  //   (-p or -r) implies the object. 
+  enum objecttype {NODES, POLY, OFF, PLY, STL, MEDIT, VTK, MESH} object;
+
+
+  void syntax();
+  void usage();
+
+  // Command line parse routine.
+  bool parse_commandline(int argc, char **argv);
+  bool parse_commandline(char *switches) {
+    return parse_commandline(0, &switches);
+  }
+
+  // Initialize all variables.
+  tetgenbehavior()
+  {
+    plc = 0;
+    psc = 0;
+    refine = 0;
+    quality = 0;
+    nobisect = 0;
+    coarsen = 0;
+    metric = 0;
+    weighted = 0;
+    brio_hilbert = 1;
+    incrflip = 0;
+    flipinsert = 0;
+    varvolume = 0;
+    fixedvolume = 0;
+    noexact = 0;
+    nostaticfilter = 0;
+    insertaddpoints = 0;
+    regionattrib = 0;
+    cdtrefine = 0;
+    diagnose = 0;
+    convex = 0;
+    zeroindex = 0;
+    facesout = 0;
+    edgesout = 0;
+    neighout = 0;
+    voroout = 0;
+    meditview = 0;
+    vtkview = 0;
+    nobound = 0;
+    nonodewritten = 0;
+    noelewritten = 0;
+    nofacewritten = 0;
+    noiterationnum = 0;
+    nomergefacet = 0;
+    nomergevertex = 0;
+    nojettison = 0;
+    docheck = 0;
+    quiet = 0;
+    verbose = 0;
+
+    vertexperblock = 4092;
+    tetrahedraperblock = 8188;
+    shellfaceperblock = 4092;
+    nobisect_nomerge = 1;
+    supsteiner_level = 2;
+    addsteiner_algo = 1;
+    coarsen_param = 0;
+    weighted_param = 0;
+    fliplinklevel = -1; 
+    flipstarsize = -1;  
+    fliplinklevelinc = 1;
+    reflevel = 3;
+    optscheme = 7;  
+    optlevel = 2;
+    delmaxfliplevel = 1;
+    order = 1;
+    reversetetori = 0;
+    steinerleft = -1;
+    no_sort = 0;
+    hilbert_order = 52; //-1;
+    hilbert_limit = 8;
+    brio_threshold = 64;
+    brio_ratio = 0.125;
+    facet_separate_ang_tol = 179.9;
+    facet_overlap_ang_tol = 0.01;
+    facet_small_ang_tol = 15.0;
+    maxvolume = -1.0;
+    minratio = 2.0;
+    mindihedral = 0.0;
+    optmaxdihedral = 165.00;
+    optminsmtdihed = 179.00;
+    optminslidihed = 179.00;
+    epsilon = 1.0e-8;
+    coarsen_percent = 1.0;
+    object = NODES;
+
+    commandline[0] = '\0';
+    infilename[0] = '\0';
+    outfilename[0] = '\0';
+    addinfilename[0] = '\0';
+    bgmeshfilename[0] = '\0';
+
+  }
+
+}; // class tetgenbehavior
+
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// tetgenmesh                                                                //
+//                                                                           //
+// A structure for creating and updating tetrahedral meshes.                 //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+class tetgenmesh {
+
+public:
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// Mesh data structure                                                       //
+//                                                                           //
+// A tetrahedral mesh T of a 3D piecewise linear complex (PLC) X is a 3D     //
+// simplicial complex whose underlying space is equal to the space of X.  T  //
+// contains a 2D subcomplex S which is a triangular mesh of the boundary of  //
+// X. S contains a 1D subcomplex L which is a linear mesh of the boundary of //
+// S. Faces and edges in S and L are respectively called subfaces and segme- //
+// nts to distinguish them from others in T.                                 //
+//                                                                           //
+// TetGen stores the tetrahedra and vertices of T. The basic structure of a  //
+// tetrahedron contains pointers to its vertices and adjacent tetrahedra. A  //
+// vertex stores its x-, y-, and z-coordinates, and a pointer to a tetrahed- //
+// ron containing it. Both tetrahedra and vertices may contain user data.    // 
+//                                                                           //
+// Each face of T belongs to either two tetrahedra or one tetrahedron. In    //
+// the latter case, the face is an exterior boundary face of T.  TetGen adds //
+// fictitious tetrahedra (one-to-one) at such faces, and connects them to an //
+// "infinite vertex" (which has no geometric coordinates).  One can imagine  //
+// such a vertex lies in 4D space and is visible by all exterior boundary    //
+// faces.  The extended set of tetrahedra (including the infinite vertex) is //
+// a tetrahedralization of a 3-pseudomanifold without boundary.  It has the  //
+// property that every face is shared by exactly two tetrahedra.             // 
+//                                                                           //
+// The current version of TetGen stores explicitly the subfaces and segments //
+// (which are in surface mesh S and the linear mesh L), respectively.  Extra //
+// pointers are allocated in tetrahedra and subfaces to point each others.   //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+  // The tetrahedron data structure.  It includes the following fields:
+  //   - a list of four adjoining tetrahedra;
+  //   - a list of four vertices;
+  //   - a pointer to a list of four subfaces (optional, for -p switch);
+  //   - a pointer to a list of six segments  (optional, for -p switch);
+  //   - a list of user-defined floating-point attributes (optional);
+  //   - a volume constraint (optional, for -a switch);
+  //   - an integer of element marker (and flags);
+  // The structure of a tetrahedron is an array of pointers.  Its actual size
+  //   (the length of the array) is determined at runtime.
+
+  typedef REAL **tetrahedron;
+
+  // The subface data structure.  It includes the following fields:
+  //   - a list of three adjoining subfaces;
+  //   - a list of three vertices;
+  //   - a list of three adjoining segments;
+  //   - two adjoining tetrahedra;
+  //   - an area constraint (optional, for -q switch);
+  //   - an integer for boundary marker;
+  //   - an integer for type, flags, etc.
+
+  typedef REAL **shellface;
+
+  // The point data structure.  It includes the following fields:
+  //   - x, y and z coordinates;
+  //   - a list of user-defined point attributes (optional);
+  //   - u, v coordinates (optional, for -s switch);
+  //   - a metric tensor (optional, for -q or -m switch);
+  //   - a pointer to an adjacent tetrahedron;
+  //   - a pointer to a parent (or a duplicate) point;
+  //   - a pointer to an adjacent subface or segment (optional, -p switch);
+  //   - a pointer to a tet in background mesh (optional, for -m switch);
+  //   - an integer for boundary marker (point index);
+  //   - an integer for point type (and flags).
+  //   - an integer for geometry tag (optional, for -s switch).
+  // The structure of a point is an array of REALs.  Its acutal size is 
+  //   determined at the runtime.
+
+  typedef REAL *point;
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// Handles                                                                   //
+//                                                                           //
+// Navigation and manipulation in a tetrahedralization are accomplished by   //
+// operating on structures referred as ``handles". A handle is a pair (t,v), //
+// where t is a pointer to a tetrahedron, and v is a 4-bit integer, in the   //
+// range from 0 to 11. v is called the ``version'' of a tetrahedron, it rep- //
+// resents a directed edge of a specific face of the tetrahedron.            //
+//                                                                           //
+// There are 12 even permutations of the four vertices, each of them corres- //
+// ponds to a directed edge (a version) of the tetrahedron.  The 12 versions //
+// can be grouped into 4 distinct ``edge rings'' in 4 ``oriented faces'' of  //
+// this tetrahedron.  One can encode each version (a directed edge) into a   //
+// 4-bit integer such that the two upper bits encode the index (from 0 to 2) //
+// of this edge in the edge ring, and the two lower bits encode the index (  //
+// from 0 to 3) of the oriented face which contains this edge.               //  
+//                                                                           //
+// The four vertices of a tetrahedron are indexed from 0 to 3 (according to  //
+// their storage in the data structure).  Give each face the same index as   //
+// the node opposite it in the tetrahedron.  Denote the edge connecting face //
+// i to face j as i/j. We number the twelve versions as follows:             //
+//                                                                           //
+//           |   edge 0     edge 1     edge 2                                //
+//   --------|--------------------------------                               //
+//    face 0 |   0 (0/1)    4 (0/3)    8 (0/2)                               //
+//    face 1 |   1 (1/2)    5 (1/3)    9 (1/0)                               //
+//    face 2 |   2 (2/3)    6 (2/1)   10 (2/0)                               //
+//    face 3 |   3 (3/0)    7 (3/1)   11 (3/2)                               //
+//                                                                           //
+// Similarly, navigation and manipulation in a (boundary) triangulation are  //
+// done by using handles of triangles. Each handle is a pair (s, v), where s //
+// is a pointer to a triangle, and v is a version in the range from 0 to 5.  //
+// Each version corresponds to a directed edge of this triangle.             //
+//                                                                           //
+// Number the three vertices of a triangle from 0 to 2 (according to their   //
+// storage in the data structure). Give each edge the same index as the node //
+// opposite it in the triangle. The six versions of a triangle are:          //
+//                                                                           //
+//                 | edge 0   edge 1   edge 2                                //
+//  ---------------|--------------------------                               //
+//   ccw orieation |   0        2        4                                   //
+//    cw orieation |   1        3        5                                   //
+//                                                                           //
+// In the following, a 'triface' is a handle of tetrahedron, and a 'face' is //
+// a handle of a triangle.                                                   //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+  class triface {
+  public:
+    tetrahedron *tet;
+    int ver; // Range from 0 to 11.
+    triface() : tet(0), ver(0) {}
+    triface& operator=(const triface& t) {
+      tet = t.tet; ver = t.ver;
+      return *this;
+    }
+  };
+
+  class face {
+  public:
+    shellface *sh;
+    int shver; // Range from 0 to 5.
+    face() : sh(0), shver(0) {}
+    face& operator=(const face& s) {
+      sh = s.sh; shver = s.shver;
+      return *this;
+    }
+  };
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// Arraypool                                                                 //
+//                                                                           //
+// A dynamic linear array. (It is written by J. Shewchuk)                    //
+//                                                                           //
+// Each arraypool contains an array of pointers to a number of blocks.  Each //
+// block contains the same fixed number of objects.  Each index of the array //
+// addresses a particular object in the pool. The most significant bits add- //
+// ress the index of the block containing the object. The less significant   //
+// bits address this object within the block.                                //
+//                                                                           //
+// 'objectbytes' is the size of one object in blocks; 'log2objectsperblock'  //
+// is the base-2 logarithm of 'objectsperblock'; 'objects' counts the number //
+// of allocated objects; 'totalmemory' is the total memory in bytes.         //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+  class arraypool {
+
+  public:
+
+    int objectbytes;
+    int objectsperblock;
+    int log2objectsperblock;
+    int objectsperblockmark;
+    int toparraylen;
+    char **toparray;
+    long objects;
+    unsigned long totalmemory;
+
+    void restart();
+    void poolinit(int sizeofobject, int log2objperblk);
+    char* getblock(int objectindex);
+    void* lookup(int objectindex);
+    int newindex(void **newptr);
+
+    arraypool(int sizeofobject, int log2objperblk);
+    ~arraypool();
+  };
+
+// fastlookup() -- A fast, unsafe operation. Return the pointer to the object
+//   with a given index.  Note: The object's block must have been allocated,
+//   i.e., by the function newindex().
+
+#define fastlookup(pool, index) \
+  (void *) ((pool)->toparray[(index) >> (pool)->log2objectsperblock] + \
+            ((index) & (pool)->objectsperblockmark) * (pool)->objectbytes)
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// Memorypool                                                                //
+//                                                                           //
+// A structure for memory allocation. (It is written by J. Shewchuk)         //
+//                                                                           //
+// firstblock is the first block of items. nowblock is the block from which  //
+//   items are currently being allocated. nextitem points to the next slab   //
+//   of free memory for an item. deaditemstack is the head of a linked list  //
+//   (stack) of deallocated items that can be recycled.  unallocateditems is //
+//   the number of items that remain to be allocated from nowblock.          //
+//                                                                           //
+// Traversal is the process of walking through the entire list of items, and //
+//   is separate from allocation.  Note that a traversal will visit items on //
+//   the "deaditemstack" stack as well as live items.  pathblock points to   //
+//   the block currently being traversed.  pathitem points to the next item  //
+//   to be traversed.  pathitemsleft is the number of items that remain to   //
+//   be traversed in pathblock.                                              //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+  class memorypool {
+
+  public:
+
+    void **firstblock, **nowblock;
+    void *nextitem;
+    void *deaditemstack;
+    void **pathblock;
+    void *pathitem;
+    int  alignbytes;
+    int  itembytes, itemwords;
+    int  itemsperblock;
+    long items, maxitems;
+    int  unallocateditems;
+    int  pathitemsleft;
+
+    memorypool();
+    memorypool(int, int, int, int);
+    ~memorypool();
+    
+    void poolinit(int, int, int, int);
+    void restart();
+    void *alloc();
+    void dealloc(void*);
+    void traversalinit();
+    void *traverse();
+  };  
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// badface                                                                   //
+//                                                                           //
+// Despite of its name, a 'badface' can be used to represent one of the      //
+// following objects:                                                        //
+//   - a face of a tetrahedron which is (possibly) non-Delaunay;             //
+//   - an encroached subsegment or subface;                                  //
+//   - a bad-quality tetrahedron, i.e, has too large radius-edge ratio;      //
+//   - a sliver, i.e., has good radius-edge ratio but nearly zero volume;    //
+//   - a recently flipped face (saved for undoing the flip later).           //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+  class badface {
+  public:
+    triface tt; 
+    face ss;
+    REAL key, cent[6];  // circumcenter or cos(dihedral angles) at 6 edges.
+    point forg, fdest, fapex, foppo, noppo;
+    badface *nextitem; 
+    badface() : key(0), forg(0), fdest(0), fapex(0), foppo(0), noppo(0),
+      nextitem(0) {}
+  };
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// insertvertexflags                                                         //
+//                                                                           //
+// A collection of flags that pass to the routine insertvertex().            //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+  class insertvertexflags {
+
+  public:
+
+    int iloc;  // input/output.
+    int bowywat, lawson;
+    int splitbdflag, validflag, respectbdflag;
+    int rejflag, chkencflag, cdtflag;
+    int assignmeshsize;
+    int sloc, sbowywat;
+
+    // Used by Delaunay refinement.
+    int refineflag; // 0, 1, 2, 3
+    triface refinetet;
+    face refinesh;
+    int smlenflag; // for useinsertradius.
+    REAL smlen; // for useinsertradius.
+    point parentpt;
+
+    insertvertexflags() {
+      iloc = bowywat = lawson = 0;
+      splitbdflag = validflag = respectbdflag = 0;
+      rejflag = chkencflag = cdtflag = 0;
+      assignmeshsize = 0;
+      sloc = sbowywat = 0;
+
+      refineflag = 0;
+      refinetet.tet = NULL;
+      refinesh.sh = NULL;
+      smlenflag = 0;
+      smlen = 0.0;
+    }
+  };
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// flipconstraints                                                           //
+//                                                                           //
+// A structure of a collection of data (options and parameters) which pass   //
+// to the edge flip function flipnm().                                       //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+  class flipconstraints {
+
+  public:
+
+    // Elementary flip flags.
+    int enqflag; // (= flipflag)
+    int chkencflag;
+
+    // Control flags
+    int unflip;  // Undo the performed flips.
+    int collectnewtets; // Collect the new tets created by flips.
+    int collectencsegflag;
+
+    // Optimization flags.
+    int remove_ndelaunay_edge; // Remove a non-Delaunay edge.
+    REAL bak_tetprism_vol; // The value to be minimized.
+    REAL tetprism_vol_sum;
+    int remove_large_angle; // Remove a large dihedral angle at edge.
+    REAL cosdihed_in; // The input cosine of the dihedral angle (> 0).
+    REAL cosdihed_out; // The improved cosine of the dihedral angle.
+
+    // Boundary recovery flags.
+    int checkflipeligibility;
+    point seg[2];  // A constraining edge to be recovered.
+    point fac[3];  // A constraining face to be recovered.
+    point remvert; // A vertex to be removed.
+
+
+    flipconstraints() {
+      enqflag = 0; 
+      chkencflag = 0;
+
+      unflip = 0;
+      collectnewtets = 0;
+      collectencsegflag = 0;
+
+      remove_ndelaunay_edge = 0;
+      bak_tetprism_vol = 0.0;
+      tetprism_vol_sum = 0.0;
+      remove_large_angle = 0;
+      cosdihed_in = 0.0;
+      cosdihed_out = 0.0;
+
+      checkflipeligibility = 0;
+      seg[0] = NULL;
+      fac[0] = NULL;
+      remvert = NULL;
+    }
+  };
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// optparameters                                                             //
+//                                                                           //
+// Optimization options and parameters.                                      //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+  class optparameters {
+
+  public:
+
+    // The one of goals of optimization.
+    int max_min_volume;      // Maximize the minimum volume.
+        int min_max_aspectratio; // Minimize the maximum aspect ratio. 
+    int min_max_dihedangle;  // Minimize the maximum dihedral angle.
+
+    // The initial and improved value.
+    REAL initval, imprval;
+
+    int numofsearchdirs;
+    REAL searchstep;
+    int maxiter;  // Maximum smoothing iterations (disabled by -1).
+    int smthiter; // Performed iterations.
+
+
+    optparameters() {
+      max_min_volume = 0;
+      min_max_aspectratio = 0;
+      min_max_dihedangle = 0;
+
+      initval = imprval = 0.0;
+
+      numofsearchdirs = 10;
+      searchstep = 0.01;
+      maxiter = -1;   // Unlimited smoothing iterations.
+      smthiter = 0;
+
+    }
+  };
+
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// Labels (enumeration declarations) used by TetGen.                         //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+  // Labels that signify the type of a vertex. 
+  enum verttype {UNUSEDVERTEX, DUPLICATEDVERTEX, RIDGEVERTEX, ACUTEVERTEX,
+                 FACETVERTEX, VOLVERTEX, FREESEGVERTEX, FREEFACETVERTEX, 
+                 FREEVOLVERTEX, NREGULARVERTEX, DEADVERTEX};
+ 
+  // Labels that signify the result of triangle-triangle intersection test.
+  enum interresult {DISJOINT, INTERSECT, SHAREVERT, SHAREEDGE, SHAREFACE,
+                    TOUCHEDGE, TOUCHFACE, ACROSSVERT, ACROSSEDGE, ACROSSFACE};
+
+  // Labels that signify the result of point location.
+  enum locateresult {UNKNOWN, OUTSIDE, INTETRAHEDRON, ONFACE, ONEDGE, ONVERTEX,
+                     ENCVERTEX, ENCSEGMENT, ENCSUBFACE, NEARVERTEX, NONREGULAR,
+                     INSTAR, BADELEMENT};
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// Variables of TetGen                                                       //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+  // Pointer to the input data (a set of nodes, a PLC, or a mesh).
+  tetgenio *in, *addin;
+
+  // Pointer to the switches and parameters.
+  tetgenbehavior *b;
+
+  // Pointer to a background mesh (contains size specification map).
+  tetgenmesh *bgm;
+
+  // Memorypools to store mesh elements (points, tetrahedra, subfaces, and
+  //   segments) and extra pointers between tetrahedra, subfaces, and segments.
+  memorypool *tetrahedrons, *subfaces, *subsegs, *points;
+  memorypool *tet2subpool, *tet2segpool;
+
+  // Memorypools to store bad-quality (or encroached) elements.
+  memorypool *badtetrahedrons, *badsubfacs, *badsubsegs;
+
+  // A memorypool to store faces to be flipped.
+  memorypool *flippool;
+  arraypool *unflipqueue;
+  badface *flipstack; 
+
+  // Arrays used for point insertion (the Bowyer-Watson algorithm).
+  arraypool *cavetetlist, *cavebdrylist, *caveoldtetlist;
+  arraypool *cavetetshlist, *cavetetseglist, *cavetetvertlist;
+  arraypool *caveencshlist, *caveencseglist;
+  arraypool *caveshlist, *caveshbdlist, *cavesegshlist;
+
+  // Stacks used for CDT construction and boundary recovery.
+  arraypool *subsegstack, *subfacstack, *subvertstack;
+
+  // Arrays of encroached segments and subfaces (for mesh refinement).
+  arraypool *encseglist, *encshlist;
+
+  // The map between facets to their vertices (for mesh refinement).
+  int *idx2facetlist;
+  point *facetverticeslist;
+
+  // The map between segments to their endpoints (for mesh refinement).
+  point *segmentendpointslist;
+
+  // The infinite vertex.
+  point dummypoint;
+  // The recently visited tetrahedron, subface.
+  triface recenttet;
+  face recentsh;
+
+  // PI is the ratio of a circle's circumference to its diameter.
+  static REAL PI;
+
+  // Array (size = numberoftetrahedra * 6) for storing high-order nodes of
+  //   tetrahedra (only used when -o2 switch is selected).
+  point *highordertable;
+
+  // Various variables.
+  int numpointattrib;                          // Number of point attributes.
+  int numelemattrib;                     // Number of tetrahedron attributes.
+  int sizeoftensor;                     // Number of REALs per metric tensor.
+  int pointmtrindex;           // Index to find the metric tensor of a point.
+  int pointparamindex;       // Index to find the u,v coordinates of a point.
+  int point2simindex;         // Index to find a simplex adjacent to a point.
+  int pointmarkindex;            // Index to find boundary marker of a point.
+  int pointinsradiusindex;  // Index to find the insertion radius of a point.
+  int elemattribindex;          // Index to find attributes of a tetrahedron.
+  int volumeboundindex;       // Index to find volume bound of a tetrahedron.
+  int elemmarkerindex;              // Index to find marker of a tetrahedron.
+  int shmarkindex;             // Index to find boundary marker of a subface.
+  int areaboundindex;               // Index to find area bound of a subface.
+  int checksubsegflag;   // Are there segments in the tetrahedralization yet?
+  int checksubfaceflag;  // Are there subfaces in the tetrahedralization yet?
+  int checkconstraints;  // Are there variant (node, seg, facet) constraints?
+  int nonconvex;                               // Is current mesh non-convex?
+  int autofliplinklevel;        // The increase of link levels, default is 1.
+  int useinsertradius;       // Save the insertion radius for Steiner points.
+  long samples;               // Number of random samples for point location.
+  unsigned long randomseed;                    // Current random number seed.
+  REAL cosmaxdihed, cosmindihed;    // The cosine values of max/min dihedral.
+  REAL cossmtdihed;     // The cosine value of a bad dihedral to be smoothed.
+  REAL cosslidihed;      // The cosine value of the max dihedral of a sliver.
+  REAL minfaceang, minfacetdihed;     // The minimum input (dihedral) angles.
+  REAL tetprism_vol_sum;   // The total volume of tetrahedral-prisms (in 4D).
+  REAL longest;                          // The longest possible edge length.
+  REAL minedgelength;                               // = longest * b->epsion.
+  REAL xmax, xmin, ymax, ymin, zmax, zmin;         // Bounding box of points.
+
+  // Counters.
+  long insegments;                               // Number of input segments.
+  long hullsize;                        // Number of exterior boundary faces.
+  long meshedges;                                    // Number of mesh edges.
+  long meshhulledges;                       // Number of boundary mesh edges.
+  long steinerleft;                 // Number of Steiner points not yet used.
+  long dupverts;                            // Are there duplicated vertices?
+  long unuverts;                                // Are there unused vertices?
+  long nonregularcount;                    // Are there non-regular vertices?
+  long st_segref_count, st_facref_count, st_volref_count;  // Steiner points.
+  long fillregioncount, cavitycount, cavityexpcount;
+  long flip14count, flip26count, flipn2ncount;
+  long flip23count, flip32count, flip44count, flip41count;
+  long flip31count, flip22count;
+  unsigned long totalworkmemory;      // Total memory used by working arrays.
+
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// Mesh manipulation primitives                                              //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+  // Fast lookup tables for mesh manipulation primitives.
+  static int bondtbl[12][12], fsymtbl[12][12];
+  static int esymtbl[12], enexttbl[12], eprevtbl[12];
+  static int enextesymtbl[12], eprevesymtbl[12]; 
+  static int eorgoppotbl[12], edestoppotbl[12];
+  static int facepivot1[12], facepivot2[12][12];
+  static int orgpivot[12], destpivot[12], apexpivot[12], oppopivot[12];
+  static int tsbondtbl[12][6], stbondtbl[12][6];
+  static int tspivottbl[12][6], stpivottbl[12][6];
+  static int ver2edge[12], edge2ver[6], epivot[12];
+  static int sorgpivot [6], sdestpivot[6], sapexpivot[6];
+  static int snextpivot[6];
+
+  void inittables();
+
+  // Primitives for tetrahedra.
+  inline tetrahedron encode(triface& t);
+  inline tetrahedron encode2(tetrahedron* ptr, int ver);
+  inline void decode(tetrahedron ptr, triface& t);
+  inline void bond(triface& t1, triface& t2);
+  inline void dissolve(triface& t);
+  inline void esym(triface& t1, triface& t2);
+  inline void esymself(triface& t);
+  inline void enext(triface& t1, triface& t2);
+  inline void enextself(triface& t);
+  inline void eprev(triface& t1, triface& t2);
+  inline void eprevself(triface& t);
+  inline void enextesym(triface& t1, triface& t2);
+  inline void enextesymself(triface& t);
+  inline void eprevesym(triface& t1, triface& t2);
+  inline void eprevesymself(triface& t);
+  inline void eorgoppo(triface& t1, triface& t2);
+  inline void eorgoppoself(triface& t);
+  inline void edestoppo(triface& t1, triface& t2);
+  inline void edestoppoself(triface& t);
+  inline void fsym(triface& t1, triface& t2);
+  inline void fsymself(triface& t);
+  inline void fnext(triface& t1, triface& t2);
+  inline void fnextself(triface& t);
+  inline point org (triface& t);
+  inline point dest(triface& t);
+  inline point apex(triface& t);
+  inline point oppo(triface& t);
+  inline void setorg (triface& t, point p);
+  inline void setdest(triface& t, point p);
+  inline void setapex(triface& t, point p);
+  inline void setoppo(triface& t, point p);
+  inline REAL elemattribute(tetrahedron* ptr, int attnum);
+  inline void setelemattribute(tetrahedron* ptr, int attnum, REAL value);
+  inline REAL volumebound(tetrahedron* ptr);
+  inline void setvolumebound(tetrahedron* ptr, REAL value);
+  inline int  elemindex(tetrahedron* ptr);
+  inline void setelemindex(tetrahedron* ptr, int value);
+  inline int  elemmarker(tetrahedron* ptr);
+  inline void setelemmarker(tetrahedron* ptr, int value);
+  inline void infect(triface& t);
+  inline void uninfect(triface& t);
+  inline bool infected(triface& t);
+  inline void marktest(triface& t);
+  inline void unmarktest(triface& t);
+  inline bool marktested(triface& t);
+  inline void markface(triface& t);
+  inline void unmarkface(triface& t);
+  inline bool facemarked(triface& t);
+  inline void markedge(triface& t);
+  inline void unmarkedge(triface& t);
+  inline bool edgemarked(triface& t);
+  inline void marktest2(triface& t);
+  inline void unmarktest2(triface& t);
+  inline bool marktest2ed(triface& t);
+  inline int  elemcounter(triface& t);
+  inline void setelemcounter(triface& t, int value);
+  inline void increaseelemcounter(triface& t);
+  inline void decreaseelemcounter(triface& t);
+  inline bool ishulltet(triface& t);
+  inline bool isdeadtet(triface& t);
+ 
+  // Primitives for subfaces and subsegments.
+  inline void sdecode(shellface sptr, face& s);
+  inline shellface sencode(face& s);
+  inline shellface sencode2(shellface *sh, int shver);
+  inline void spivot(face& s1, face& s2);
+  inline void spivotself(face& s);
+  inline void sbond(face& s1, face& s2);
+  inline void sbond1(face& s1, face& s2);
+  inline void sdissolve(face& s);
+  inline point sorg(face& s);
+  inline point sdest(face& s);
+  inline point sapex(face& s);
+  inline void setsorg(face& s, point pointptr);
+  inline void setsdest(face& s, point pointptr);
+  inline void setsapex(face& s, point pointptr);
+  inline void sesym(face& s1, face& s2);
+  inline void sesymself(face& s);
+  inline void senext(face& s1, face& s2);
+  inline void senextself(face& s);
+  inline void senext2(face& s1, face& s2);
+  inline void senext2self(face& s);
+  inline REAL areabound(face& s);
+  inline void setareabound(face& s, REAL value);
+  inline int shellmark(face& s);
+  inline void setshellmark(face& s, int value);
+  inline void sinfect(face& s);
+  inline void suninfect(face& s);
+  inline bool sinfected(face& s);
+  inline void smarktest(face& s);
+  inline void sunmarktest(face& s);
+  inline bool smarktested(face& s);
+  inline void smarktest2(face& s);
+  inline void sunmarktest2(face& s);
+  inline bool smarktest2ed(face& s);
+  inline void smarktest3(face& s);
+  inline void sunmarktest3(face& s);
+  inline bool smarktest3ed(face& s);
+  inline void setfacetindex(face& f, int value);
+  inline int  getfacetindex(face& f);
+
+  // Primitives for interacting tetrahedra and subfaces.
+  inline void tsbond(triface& t, face& s);
+  inline void tsdissolve(triface& t);
+  inline void stdissolve(face& s);
+  inline void tspivot(triface& t, face& s);
+  inline void stpivot(face& s, triface& t);
+
+  // Primitives for interacting tetrahedra and segments.
+  inline void tssbond1(triface& t, face& seg);
+  inline void sstbond1(face& s, triface& t);
+  inline void tssdissolve1(triface& t);
+  inline void sstdissolve1(face& s);
+  inline void tsspivot1(triface& t, face& s);
+  inline void sstpivot1(face& s, triface& t);
+
+  // Primitives for interacting subfaces and segments.
+  inline void ssbond(face& s, face& edge);
+  inline void ssbond1(face& s, face& edge);
+  inline void ssdissolve(face& s);
+  inline void sspivot(face& s, face& edge);
+
+  // Primitives for points.
+  inline int  pointmark(point pt);
+  inline void setpointmark(point pt, int value);
+  inline enum verttype pointtype(point pt);
+  inline void setpointtype(point pt, enum verttype value);
+  inline int  pointgeomtag(point pt);
+  inline void setpointgeomtag(point pt, int value);
+  inline REAL pointgeomuv(point pt, int i);
+  inline void setpointgeomuv(point pt, int i, REAL value);
+  inline void pinfect(point pt);
+  inline void puninfect(point pt);
+  inline bool pinfected(point pt);
+  inline void pmarktest(point pt);
+  inline void punmarktest(point pt);
+  inline bool pmarktested(point pt);
+  inline void pmarktest2(point pt);
+  inline void punmarktest2(point pt);
+  inline bool pmarktest2ed(point pt);
+  inline void pmarktest3(point pt);
+  inline void punmarktest3(point pt);
+  inline bool pmarktest3ed(point pt);
+  inline tetrahedron point2tet(point pt);
+  inline void setpoint2tet(point pt, tetrahedron value);
+  inline shellface point2sh(point pt);
+  inline void setpoint2sh(point pt, shellface value);
+  inline point point2ppt(point pt);
+  inline void setpoint2ppt(point pt, point value);
+  inline tetrahedron point2bgmtet(point pt);
+  inline void setpoint2bgmtet(point pt, tetrahedron value);
+  inline void setpointinsradius(point pt, REAL value);
+  inline REAL getpointinsradius(point pt);
+  inline bool issteinerpoint(point pt);
+
+  // Advanced primitives.
+  inline void point2tetorg(point pt, triface& t);
+  inline void point2shorg(point pa, face& s);
+  inline point farsorg(face& seg);
+  inline point farsdest(face& seg);
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+//  Memory managment                                                         //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+  void tetrahedrondealloc(tetrahedron*);
+  tetrahedron *tetrahedrontraverse();
+  tetrahedron *alltetrahedrontraverse();
+  void shellfacedealloc(memorypool*, shellface*);
+  shellface *shellfacetraverse(memorypool*);
+  void pointdealloc(point);
+  point pointtraverse();
+
+  void makeindex2pointmap(point*&);
+  void makepoint2submap(memorypool*, int*&, face*&);
+  void maketetrahedron(triface*);
+  void makeshellface(memorypool*, face*);
+  void makepoint(point*, enum verttype);
+
+  void initializepools();
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// Advanced geometric predicates and calculations                            //
+//                                                                           //
+// TetGen uses a simplified symbolic perturbation scheme from Edelsbrunner,  //
+// et al [*].  Hence the point-in-sphere test never returns a zero. The idea //
+// is to perturb the weights of vertices in the fourth dimension.  TetGen    //
+// uses the indices of the vertices decide the amount of perturbation. It is //
+// implemented in the routine insphere_s().
+//                                                                           //
+// The routine tri_edge_test() determines whether or not a triangle and an   //
+// edge intersect in 3D. If they intersect, their intersection type is also  //
+// reported. This test is a combination of n 3D orientation tests (n is bet- //
+// ween 3 and 9). It uses the robust orient3d() test to make the branch dec- //
+// isions.  The routine tri_tri_test() determines whether or not two triang- //
+// les intersect in 3D. It also uses the robust orient3d() test.             //
+//                                                                           //
+// There are a number of routines to calculate geometrical quantities, e.g., //
+// circumcenters, angles, dihedral angles, face normals, face areas, etc.    //
+// They are so far done by the default floating-point arithmetics which are  //
+// non-robust. They should be improved in the future.                        //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+  // Symbolic perturbations (robust)
+  REAL insphere_s(REAL*, REAL*, REAL*, REAL*, REAL*);
+  REAL orient4d_s(REAL*, REAL*, REAL*, REAL*, REAL*, 
+                  REAL, REAL, REAL, REAL, REAL);
+
+  // Triangle-edge intersection test (robust)
+  int tri_edge_2d(point, point, point, point, point, point, int, int*, int*);
+  int tri_edge_tail(point, point, point, point, point, point, REAL, REAL, int,
+                    int*, int*);
+  int tri_edge_test(point, point, point, point, point, point, int, int*, int*);
+
+  // Triangle-triangle intersection test (robust)
+  int tri_edge_inter_tail(point, point, point, point, point, REAL, REAL);
+  int tri_tri_inter(point, point, point, point, point, point);
+
+  // Linear algebra functions
+  inline REAL dot(REAL* v1, REAL* v2);
+  inline void cross(REAL* v1, REAL* v2, REAL* n);
+  bool lu_decmp(REAL lu[4][4], int n, int* ps, REAL* d, int N);
+  void lu_solve(REAL lu[4][4], int n, int* ps, REAL* b, int N);
+
+  // An embedded 2-dimensional geometric predicate (non-robust)
+  REAL incircle3d(point pa, point pb, point pc, point pd);
+
+  // Geometric calculations (non-robust)
+  REAL orient3dfast(REAL *pa, REAL *pb, REAL *pc, REAL *pd);
+  inline REAL norm2(REAL x, REAL y, REAL z);
+  inline REAL distance(REAL* p1, REAL* p2);
+  void facenormal(point pa, point pb, point pc, REAL *n, int pivot, REAL *lav);
+  REAL shortdistance(REAL* p, REAL* e1, REAL* e2);
+  REAL triarea(REAL* pa, REAL* pb, REAL* pc);
+  REAL interiorangle(REAL* o, REAL* p1, REAL* p2, REAL* n);
+  void projpt2edge(REAL* p, REAL* e1, REAL* e2, REAL* prj);
+  void projpt2face(REAL* p, REAL* f1, REAL* f2, REAL* f3, REAL* prj);
+  bool tetalldihedral(point, point, point, point, REAL*, REAL*, REAL*);
+  void tetallnormal(point, point, point, point, REAL N[4][3], REAL* volume);
+  REAL tetaspectratio(point, point, point, point);
+  bool circumsphere(REAL*, REAL*, REAL*, REAL*, REAL* cent, REAL* radius);
+  bool orthosphere(REAL*,REAL*,REAL*,REAL*,REAL,REAL,REAL,REAL,REAL*,REAL*);
+  void planelineint(REAL*, REAL*, REAL*, REAL*, REAL*, REAL*, REAL*);
+  int linelineint(REAL*, REAL*, REAL*, REAL*, REAL*, REAL*, REAL*, REAL*);
+  REAL tetprismvol(REAL* pa, REAL* pb, REAL* pc, REAL* pd);
+  bool calculateabovepoint(arraypool*, point*, point*, point*);
+  void calculateabovepoint4(point, point, point, point);
+
+  // PLC error reports.
+  void report_overlapping_facets(face*, face*, REAL dihedang = 0.0);
+  int report_selfint_edge(point, point, face* sedge, triface* searchtet, 
+                          enum interresult);
+  int report_selfint_face(point, point, point, face* sface, triface* iedge, 
+                          int intflag, int* types, int* poss);
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// Local mesh transformations                                                //
+//                                                                           //
+// A local transformation replaces a small set of tetrahedra with another    //
+// set of tetrahedra which fills the same space and the same boundaries.     //
+//   In 3D, the most simplest local transformations are the elementary flips //
+// performed within the convex hull of five vertices: 2-to-3, 3-to-2, 1-to-4,//
+// and 4-to-1 flips,  where the numbers indicate the number of tetrahedra    //
+// before and after each flip.  The 1-to-4 and 4-to-1 flip involve inserting //
+// or deleting a vertex, respectively.                                       //
+//   There are complex local transformations which can be decomposed as a    //
+// combination of elementary flips. For example,a 4-to-4 flip which replaces //
+// two coplanar edges can be regarded by a 2-to-3 flip and a 3-to-2 flip.    //
+// Note that the first 2-to-3 flip will temporarily create a degenerate tet- //
+// rahedron which is removed immediately by the followed 3-to-2 flip.  More  //
+// generally, a n-to-m flip, where n > 3, m = (n - 2) * 2, which removes an  //
+// edge can be done by first performing a sequence of (n - 3) 2-to-3 flips   //
+// followed by a 3-to-2 flip.                                                //
+//                                                                           //
+// The routines flip23(), flip32(), and flip41() perform the three element-  //
+// ray flips. The flip14() is available inside the routine insertpoint().    //
+//                                                                           //
+// The routines flipnm() and flipnm_post() implement a generalized edge flip //
+// algorithm which uses a combination of elementary flips.                   //
+//                                                                           //
+// The routine insertpoint() implements a variant of Bowyer-Watson's cavity  //
+// algorithm to insert a vertex. It works for arbitrary tetrahedralization,  //
+// either Delaunay, or constrained Delaunay, or non-Delaunay.                //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+  // The elementary flips.
+  void flip23(triface*, int, flipconstraints* fc);
+  void flip32(triface*, int, flipconstraints* fc);
+  void flip41(triface*, int, flipconstraints* fc);
+
+  // A generalized edge flip.
+  int flipnm(triface*, int n, int level, int, flipconstraints* fc);
+  int flipnm_post(triface*, int n, int nn, int, flipconstraints* fc);
+
+  // Point insertion.
+  int  insertpoint(point, triface*, face*, face*, insertvertexflags*);
+  void insertpoint_abort(face*, insertvertexflags*);
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// Delaunay tetrahedralization                                               //
+//                                                                           //
+// The routine incrementaldelaunay() implemented two incremental algorithms  //
+// for constructing Delaunay tetrahedralizations (DTs):  the Bowyer-Watson   //
+// (B-W) algorithm and the incremental flip algorithm of Edelsbrunner and    //
+// Shah, "Incremental topological flipping works for regular triangulation," //
+// Algorithmica, 15:233-241, 1996.                                           //
+//                                                                           //
+// The routine incrementalflip() implements the flip algorithm of [Edelsbru- //
+// nner and Shah, 1996].  It flips a queue of locally non-Delaunay faces (in //
+// an arbitrary order).  The success is guaranteed when the Delaunay tetrah- //
+// edralization is constructed incrementally by adding one vertex at a time. //
+//                                                                           //
+// The routine locate() finds a tetrahedron contains a new point in current  //
+// DT.  It uses a simple stochastic walk algorithm: starting from an arbitr- //
+// ary tetrahedron in DT, it finds the destination by visit one tetrahedron  //
+// at a time, randomly chooses a tetrahedron if there are more than one      //
+// choices. This algorithm terminates due to Edelsbrunner's acyclic theorem. //
+//   Choose a good starting tetrahedron is crucial to the speed of the walk. //
+// TetGen originally uses the "jump-and-walk" algorithm of Muecke, E.P.,     //
+// Saias, I., and Zhu, B. "Fast Randomized Point Location Without Preproces- //
+// sing." In Proceedings of the 12th ACM Symposium on Computational Geometry,//
+// 274-283, 1996.  It first randomly samples several tetrahedra in the DT    //
+// and then choosing the closet one to start walking.                        //
+//   The above algorithm slows download dramatically as the number of points //
+// grows -- reported in Amenta, N., Choi, S. and Rote, G., "Incremental      //
+// construction con {BRIO}," In Proceedings of 19th ACM Symposium on         //
+// Computational Geometry, 211-219, 2003.  On the other hand, Liu and        //
+// Snoeyink showed that the point location can be made in constant time if   //
+// the points are pre-sorted so that the nearby points in space have nearby  //
+// indices, then adding the points in this order. They sorted the points     //
+// along the 3D Hilbert curve.                                               //
+//                                                                           //
+// The routine hilbert_sort3() sorts a set of 3D points along the 3D Hilbert //
+// curve. It recursively splits a point set according to the Hilbert indices //
+// mapped to the subboxes of the bounding box of the point set.              //
+//   The Hilbert indices is calculated by Butz's algorithm in 1971.  A nice  //
+// exposition of this algorithm can be found in the paper of Hamilton, C.,   //
+// "Compact Hilbert Indices", Technical Report CS-2006-07, Computer Science, //
+// Dalhousie University, 2006 (the Section 2). My implementation also refer- //
+// enced Steven Witham's implementation of "Hilbert walk" (hopefully, it is  //
+// still available at: http://www.tiac.net/~sw/2008/10/Hilbert/).            //
+//                                                                           //
+// TetGen sorts the points using the method in the paper of Boissonnat,J.-D.,//
+// Devillers, O. and Hornus, S. "Incremental Construction of the Delaunay    //
+// Triangulation and the Delaunay Graph in Medium Dimension," In Proceedings //
+// of the 25th ACM Symposium on Computational Geometry, 2009.                //
+//   It first randomly sorts the points into subgroups using the Biased Rand-//
+// omized Insertion Ordering (BRIO) of Amenta et al 2003, then sorts the     //
+// points in each subgroup along the 3D Hilbert curve.  Inserting points in  //
+// this order ensures a randomized "sprinkling" of the points over the       //
+// domain, while sorting of each subset ensures locality.                    //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+  void transfernodes();
+
+  // Point sorting.
+  int  transgc[8][3][8], tsb1mod3[8];
+  void hilbert_init(int n);
+  int  hilbert_split(point* vertexarray, int arraysize, int gc0, int gc1,
+                     REAL, REAL, REAL, REAL, REAL, REAL);
+  void hilbert_sort3(point* vertexarray, int arraysize, int e, int d,
+                     REAL, REAL, REAL, REAL, REAL, REAL, int depth);
+  void brio_multiscale_sort(point*,int,int threshold,REAL ratio,int* depth);
+
+  // Point location.
+  unsigned long randomnation(unsigned int choices);
+  void randomsample(point searchpt, triface *searchtet);
+  enum locateresult locate(point searchpt, triface *searchtet, 
+                           int chkencflag = 0);
+
+  // Incremental flips.
+  void flippush(badface*&, triface*);
+  int  incrementalflip(point newpt, int, flipconstraints *fc);
+
+  // Incremental Delaunay construction.
+  void initialdelaunay(point pa, point pb, point pc, point pd);
+  void incrementaldelaunay(clock_t&);
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// Surface triangulation                                                     //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+  void flipshpush(face*);
+  void flip22(face*, int, int);
+  void flip31(face*, int);
+  long lawsonflip();
+  int sinsertvertex(point newpt, face*, face*, int iloc, int bowywat, int);
+  int sremovevertex(point delpt, face*, face*, int lawson);
+
+  enum locateresult slocate(point, face*, int, int, int);
+  enum interresult sscoutsegment(face*, point, int, int, int);
+  void scarveholes(int, REAL*);
+
+  void unifysegments();
+  void identifyinputedges(point*);
+  void mergefacets();
+
+  enum interresult finddirection(triface* searchtet, point endpt);
+
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// Constrained tetrahedralizations.                                          //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+  int checkflipeligibility(int fliptype, point, point, point, point, point,
+                           int level, int edgepivot, flipconstraints* fc);
+
+  int removeedgebyflips(triface*, flipconstraints*);
+  int removefacebyflips(triface*, flipconstraints*);
+
+  int recoveredgebyflips(point, point, face*, triface*, int fullsearch);
+  int add_steinerpt_in_schoenhardtpoly(triface*, int, int chkencflag);
+  int add_steinerpt_in_segment(face*, int searchlevel); 
+  int addsteiner4recoversegment(face*, int);
+  int recoversegments(arraypool*, int fullsearch, int steinerflag);
+
+  int recoverfacebyflips(point, point, point, face*, triface*);
+  int recoversubfaces(arraypool*, int steinerflag);
+
+  int getvertexstar(int, point searchpt, arraypool*, arraypool*, arraypool*);
+  int getedge(point, point, triface*);
+  int reduceedgesatvertex(point startpt, arraypool* endptlist);
+  int removevertexbyflips(point steinerpt);
+
+  int suppressbdrysteinerpoint(point steinerpt);
+  int suppresssteinerpoints();
+
+  void recoverboundary(clock_t&);
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// Mesh reconstruction                                                       //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+  void carveholes();
+
+  // Comment: These three functions are implemented directly in:
+  //   gmsh_wrk/Mesh/meshGRegionBoundaryRecovery.cpp
+  bool reconstructmesh(void *);
+  void outsurfacemesh(const char* mfilename);
+  void outmesh2medit(const char* mfilename);
+
+  void enqueuesubface(memorypool*, face*);
+  void enqueuetetrahedron(triface*);
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// Mesh optimization                                                         //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+  long lawsonflip3d(flipconstraints *fc);
+  void recoverdelaunay();
+
+  int  gettetrahedron(point, point, point, point, triface *);
+  long improvequalitybyflips();
+
+  int  smoothpoint(point smtpt, arraypool*, int ccw, optparameters *opm);
+  long improvequalitybysmoothing(optparameters *opm);
+
+  int  splitsliver(triface *, REAL, int);
+  long removeslivers(int);
+
+  void optimizemesh();
+
+  void jettisonnodes();
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// Constructor & destructor                                                  //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+  void initializetetgenmesh()
+  {
+    in  = addin = NULL;
+    b   = NULL;
+    bgm = NULL;
+
+    tetrahedrons = subfaces = subsegs = points = NULL;
+    badtetrahedrons = badsubfacs = badsubsegs = NULL;
+    tet2segpool = tet2subpool = NULL;
+    flippool = NULL;
+
+    dummypoint = NULL;
+    flipstack = NULL;
+    unflipqueue = NULL;
+
+    cavetetlist = cavebdrylist = caveoldtetlist = NULL;
+    cavetetshlist = cavetetseglist = cavetetvertlist = NULL;
+    caveencshlist = caveencseglist = NULL;
+    caveshlist = caveshbdlist = cavesegshlist = NULL;
+
+    subsegstack = subfacstack = subvertstack = NULL;
+    encseglist = encshlist = NULL;
+    idx2facetlist = NULL;
+    facetverticeslist = NULL;
+    segmentendpointslist = NULL;
+
+    highordertable = NULL;
+
+    numpointattrib = numelemattrib = 0;
+    sizeoftensor = 0;
+    pointmtrindex = 0;
+    pointparamindex = 0;
+    pointmarkindex = 0;
+    point2simindex = 0;
+    pointinsradiusindex = 0;
+    elemattribindex = 0;
+    volumeboundindex = 0;
+    shmarkindex = 0;
+    areaboundindex = 0;
+    checksubsegflag = 0;
+    checksubfaceflag = 0;
+    checkconstraints = 0;
+    nonconvex = 0;
+    autofliplinklevel = 1;
+    useinsertradius = 0;
+    samples = 0l;
+    randomseed = 1l;
+    minfaceang = minfacetdihed = PI;
+    tetprism_vol_sum = 0.0;
+    longest = minedgelength = 0.0;
+    xmax = xmin = ymax = ymin = zmax = zmin = 0.0; 
+
+    insegments = 0l;
+    hullsize = 0l;
+    meshedges = meshhulledges = 0l;
+    steinerleft = -1;
+    dupverts = 0l;
+    unuverts = 0l;
+    nonregularcount = 0l;
+    st_segref_count = st_facref_count = st_volref_count = 0l;
+    fillregioncount = cavitycount = cavityexpcount = 0l;
+    flip14count = flip26count = flipn2ncount = 0l;
+    flip23count = flip32count = flip44count = flip41count = 0l;
+    flip22count = flip31count = 0l;
+    totalworkmemory = 0l;
+
+
+  } // tetgenmesh()
+
+  void freememory()
+  {
+    if (bgm != NULL) {
+      delete bgm;
+    }
+
+    if (points != (memorypool *) NULL) {
+      delete points;
+      delete [] dummypoint;
+    }
+    if (tetrahedrons != (memorypool *) NULL) {
+      delete tetrahedrons;
+    }
+    if (subfaces != (memorypool *) NULL) {
+      delete subfaces;
+      delete subsegs;
+    }
+    if (tet2segpool != NULL) {
+      delete tet2segpool;
+      delete tet2subpool;
+    }
+
+    if (badtetrahedrons) {
+      delete badtetrahedrons;
+    }
+    if (badsubfacs) {
+      delete badsubfacs;
+    }
+    if (badsubsegs) {
+      delete badsubsegs;
+    }
+    if (encseglist) {
+      delete encseglist;
+    }
+    if (encshlist) {
+      delete encshlist;
+    }
+
+    if (flippool != NULL) {
+      delete flippool;
+      delete unflipqueue;
+    }
+
+    if (cavetetlist != NULL) {
+      delete cavetetlist;
+      delete cavebdrylist;
+      delete caveoldtetlist;
+      delete cavetetvertlist;
+    }
+
+    if (caveshlist != NULL) {
+      delete caveshlist;
+      delete caveshbdlist;
+      delete cavesegshlist;
+      delete cavetetshlist;
+      delete cavetetseglist;
+      delete caveencshlist;
+      delete caveencseglist;
+    }
+
+    if (subsegstack != NULL) {
+      delete subsegstack;
+      delete subfacstack;
+      delete subvertstack;
+    }
+
+    if (idx2facetlist != NULL) {
+      delete [] idx2facetlist;
+      delete [] facetverticeslist;
+    }
+
+    if (segmentendpointslist != NULL) {
+      delete [] segmentendpointslist;
+    }
+
+    if (highordertable != NULL) {
+      delete [] highordertable;
+    }
+
+    initializetetgenmesh();
+  }
+
+  tetgenmesh()
+  {
+    initializetetgenmesh();
+  }
+
+  ~tetgenmesh()
+  {
+    freememory();
+  } // ~tetgenmesh()
+
+};                                               // End of class tetgenmesh.
+
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// terminatetetgen()    Terminate TetGen with a given exit code.             //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+// selfint_event, a structure to report self-intersections.
+//
+//   - e_type,  report the type of self-intersections, 
+//     it may be one of:
+//     0, reserved.
+//     1, two edges intersect,
+//     2, an edge and a triangle intersect,
+//     3, two triangles intersect,
+//     4, two edges are overlapping,
+//     5, an edge and a triangle are overlapping,
+//     6, two triangles are overlapping,
+//     7, a vertex lies in an edge,
+//     8, a vertex lies in a facet,         
+
+class selfint_event {
+public:
+  int e_type;
+  int f_marker1; // Tag of the 1st facet.
+  int s_marker1; // Tag of the 1st segment.
+  int f_vertices1[3];
+  int f_marker2; // Tag of the 2nd facet.
+  int s_marker2; // Tag of the 2nd segment.
+  int f_vertices2[3];
+  REAL int_point[3];
+  selfint_event() {
+    e_type = 0;
+    f_marker1 = f_marker2 = 0; 
+    s_marker1 = s_marker2 = 0;
+  }
+};
+
+static selfint_event sevent;
+
+inline void terminatetetgen(tetgenmesh *m, int x)
+{
+#ifdef TETLIBRARY
+  throw x;
+#else
+  switch (x) {
+  case 1: // Out of memory.
+    printf("Error:  Out of memory.\n"); 
+    break;
+  case 2: // Encounter an internal error.
+    printf("Please report this bug to Hang.Si@wias-berlin.de. Include\n");
+    printf("  the message above, your input data set, and the exact\n");
+    printf("  command line you used to run this program, thank you.\n");
+    break;
+  case 3:
+    printf("A self-intersection was detected. Program stopped.\n");
+    printf("Hint: use -d option to detect all self-intersections.\n"); 
+    break;
+  case 4:
+    printf("A very small input feature size was detected. Program stopped.\n");
+    if (m) {
+      printf("Hint: use -T option to set a smaller tolerance. Current is %g\n",
+             m->b->epsilon);
+    }
+    break;
+  case 5:
+    printf("Two very close input facets were detected. Program stopped.\n");
+    printf("Hint: use -Y option to avoid adding Steiner points in boundary.\n");
+    break;
+  case 10: 
+    printf("An input error was detected. Program stopped.\n"); 
+    break;
+  } // switch (x)
+  exit(x);
+#endif // #ifdef TETLIBRARY
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// Primitives for tetrahedra                                                 //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+// encode()  compress a handle into a single pointer.  It relies on the 
+//   assumption that all addresses of tetrahedra are aligned to sixteen-
+//   byte boundaries, so that the last four significant bits are zero.
+
+inline tetgenmesh::tetrahedron tetgenmesh::encode(triface& t) {
+  return (tetrahedron) ((uintptr_t) (t).tet | (uintptr_t) (t).ver);
+}
+
+inline tetgenmesh::tetrahedron tetgenmesh::encode2(tetrahedron* ptr, int ver) {
+  return (tetrahedron) ((uintptr_t) (ptr) | (uintptr_t) (ver));
+}
+
+// decode()  converts a pointer to a handle. The version is extracted from
+//   the four least significant bits of the pointer.
+
+inline void tetgenmesh::decode(tetrahedron ptr, triface& t) {
+  (t).ver = (int) ((uintptr_t) (ptr) & (uintptr_t) 15);
+  (t).tet = (tetrahedron *) ((uintptr_t) (ptr) ^ (uintptr_t) (t).ver);
+}
+
+// bond()  connects two tetrahedra together. (t1,v1) and (t2,v2) must 
+//   refer to the same face and the same edge. 
+
+inline void tetgenmesh::bond(triface& t1, triface& t2) {
+  //  printf("%d %d %d\n",t1.ver,t2.ver,bondtbl[t1.ver][t2.ver]);
+  t1.tet[t1.ver & 3] = encode2(t2.tet, bondtbl[t1.ver][t2.ver]);
+  t2.tet[t2.ver & 3] = encode2(t1.tet, bondtbl[t2.ver][t1.ver]);
+}
+
+
+// dissolve()  a bond (from one side).
+
+inline void tetgenmesh::dissolve(triface& t) {
+  t.tet[t.ver & 3] = NULL;
+}
+
+// enext()  finds the next edge (counterclockwise) in the same face.
+
+inline void tetgenmesh::enext(triface& t1, triface& t2) {
+  t2.tet = t1.tet;
+  t2.ver = enexttbl[t1.ver];
+}
+
+inline void tetgenmesh::enextself(triface& t) {
+  t.ver = enexttbl[t.ver];
+}
+
+// eprev()   finds the next edge (clockwise) in the same face.
+
+inline void tetgenmesh::eprev(triface& t1, triface& t2) {
+  t2.tet = t1.tet;
+  t2.ver = eprevtbl[t1.ver];
+}
+
+inline void tetgenmesh::eprevself(triface& t) {
+  t.ver = eprevtbl[t.ver];
+}
+
+// esym()  finds the reversed edge.  It is in the other face of the
+//   same tetrahedron.
+
+inline void tetgenmesh::esym(triface& t1, triface& t2) {
+  (t2).tet = (t1).tet;
+  (t2).ver = esymtbl[(t1).ver];
+}
+
+inline void tetgenmesh::esymself(triface& t) {
+  (t).ver = esymtbl[(t).ver];
+}
+
+// enextesym()  finds the reversed edge of the next edge. It is in the other
+//   face of the same tetrahedron. It is the combination esym() * enext(). 
+
+inline void tetgenmesh::enextesym(triface& t1, triface& t2) {
+  t2.tet = t1.tet;
+  t2.ver = enextesymtbl[t1.ver];
+}
+
+inline void tetgenmesh::enextesymself(triface& t) {
+  t.ver = enextesymtbl[t.ver];
+}
+
+// eprevesym()  finds the reversed edge of the previous edge.
+
+inline void tetgenmesh::eprevesym(triface& t1, triface& t2) {
+  t2.tet = t1.tet;
+  t2.ver = eprevesymtbl[t1.ver];
+}
+
+inline void tetgenmesh::eprevesymself(triface& t) {
+  t.ver = eprevesymtbl[t.ver];
+}
+
+// eorgoppo()    Finds the opposite face of the origin of the current edge.
+//               Return the opposite edge of the current edge.
+
+inline void tetgenmesh::eorgoppo(triface& t1, triface& t2) {
+  t2.tet = t1.tet;
+  t2.ver = eorgoppotbl[t1.ver];
+}
+
+inline void tetgenmesh::eorgoppoself(triface& t) {
+  t.ver = eorgoppotbl[t.ver];
+}
+
+// edestoppo()    Finds the opposite face of the destination of the current 
+//                edge. Return the opposite edge of the current edge.
+
+inline void tetgenmesh::edestoppo(triface& t1, triface& t2) {
+  t2.tet = t1.tet;
+  t2.ver = edestoppotbl[t1.ver];
+}
+
+inline void tetgenmesh::edestoppoself(triface& t) {
+  t.ver = edestoppotbl[t.ver];
+}
+
+// fsym()  finds the adjacent tetrahedron at the same face and the same edge.
+
+inline void tetgenmesh::fsym(triface& t1, triface& t2) {
+  decode((t1).tet[(t1).ver & 3], t2);
+  t2.ver = fsymtbl[t1.ver][t2.ver];
+}
+
+
+#define fsymself(t) \
+  t1ver = (t).ver; \
+  decode((t).tet[(t).ver & 3], (t));\
+  (t).ver = fsymtbl[t1ver][(t).ver]
+
+// fnext()  finds the next face while rotating about an edge according to
+//   a right-hand rule. The face is in the adjacent tetrahedron.  It is
+//   the combination: fsym() * esym().
+
+inline void tetgenmesh::fnext(triface& t1, triface& t2) {
+  decode(t1.tet[facepivot1[t1.ver]], t2);
+  t2.ver = facepivot2[t1.ver][t2.ver];
+}
+
+
+#define fnextself(t) \
+  t1ver = (t).ver; \
+  decode((t).tet[facepivot1[(t).ver]], (t)); \
+  (t).ver = facepivot2[t1ver][(t).ver]
+
+
+// The following primtives get or set the origin, destination, face apex,
+//   or face opposite of an ordered tetrahedron.
+
+inline tetgenmesh::point tetgenmesh::org(triface& t) {
+  return (point) (t).tet[orgpivot[(t).ver]];
+}
+
+inline tetgenmesh::point tetgenmesh:: dest(triface& t) {
+  return (point) (t).tet[destpivot[(t).ver]];
+}
+
+inline tetgenmesh::point tetgenmesh:: apex(triface& t) {
+  return (point) (t).tet[apexpivot[(t).ver]];
+}
+
+inline tetgenmesh::point tetgenmesh:: oppo(triface& t) {
+  return (point) (t).tet[oppopivot[(t).ver]];
+}
+
+inline void tetgenmesh:: setorg(triface& t, point p) {
+  (t).tet[orgpivot[(t).ver]] = (tetrahedron) (p);
+}
+
+inline void tetgenmesh:: setdest(triface& t, point p) {
+  (t).tet[destpivot[(t).ver]] = (tetrahedron) (p);
+}
+
+inline void tetgenmesh:: setapex(triface& t, point p) {
+  (t).tet[apexpivot[(t).ver]] = (tetrahedron) (p);
+}
+
+inline void tetgenmesh:: setoppo(triface& t, point p) {
+  (t).tet[oppopivot[(t).ver]] = (tetrahedron) (p);
+}
+
+#define setvertices(t, torg, tdest, tapex, toppo) \
+  (t).tet[orgpivot[(t).ver]] = (tetrahedron) (torg);\
+  (t).tet[destpivot[(t).ver]] = (tetrahedron) (tdest); \
+  (t).tet[apexpivot[(t).ver]] = (tetrahedron) (tapex); \
+  (t).tet[oppopivot[(t).ver]] = (tetrahedron) (toppo)
+
+// Check or set a tetrahedron's attributes.
+
+inline REAL tetgenmesh::elemattribute(tetrahedron* ptr, int attnum) {
+  return ((REAL *) (ptr))[elemattribindex + attnum];
+}
+
+inline void tetgenmesh::setelemattribute(tetrahedron* ptr, int attnum, 
+  REAL value) {
+  ((REAL *) (ptr))[elemattribindex + attnum] = value;
+}
+
+// Check or set a tetrahedron's maximum volume bound.
+
+inline REAL tetgenmesh::volumebound(tetrahedron* ptr) {
+  return ((REAL *) (ptr))[volumeboundindex];
+}
+
+inline void tetgenmesh::setvolumebound(tetrahedron* ptr, REAL value) {
+  ((REAL *) (ptr))[volumeboundindex] = value;
+}
+
+// Get or set a tetrahedron's index (only used for output).
+//    These two routines use the reserved slot ptr[10].
+
+inline int tetgenmesh::elemindex(tetrahedron* ptr) {
+  int *iptr = (int *) &(ptr[10]);
+  return iptr[0];
+}
+
+inline void tetgenmesh::setelemindex(tetrahedron* ptr, int value) {
+  int *iptr = (int *) &(ptr[10]);
+  iptr[0] = value;
+}
+
+// Get or set a tetrahedron's marker. 
+//   Set 'value = 0' cleans all the face/edge flags.
+
+inline int tetgenmesh::elemmarker(tetrahedron* ptr) {
+  return ((int *) (ptr))[elemmarkerindex];
+}
+
+inline void tetgenmesh::setelemmarker(tetrahedron* ptr, int value) {
+  ((int *) (ptr))[elemmarkerindex] = value;
+}
+
+// infect(), infected(), uninfect() -- primitives to flag or unflag a
+//   tetrahedron. The last bit of the element marker is flagged (1)
+//   or unflagged (0).
+
+inline void tetgenmesh::infect(triface& t) {
+  ((int *) (t.tet))[elemmarkerindex] |= 1;
+}
+
+inline void tetgenmesh::uninfect(triface& t) {
+  ((int *) (t.tet))[elemmarkerindex] &= ~1;
+}
+
+inline bool tetgenmesh::infected(triface& t) {
+  return (((int *) (t.tet))[elemmarkerindex] & 1) != 0;
+}
+
+// marktest(), marktested(), unmarktest() -- primitives to flag or unflag a
+//   tetrahedron.  Use the second lowerest bit of the element marker.
+
+inline void tetgenmesh::marktest(triface& t) {
+  ((int *) (t.tet))[elemmarkerindex] |= 2;
+}
+
+inline void tetgenmesh::unmarktest(triface& t) {
+  ((int *) (t.tet))[elemmarkerindex] &= ~2;
+}
+    
+inline bool tetgenmesh::marktested(triface& t) {
+  return (((int *) (t.tet))[elemmarkerindex] & 2) != 0;
+}
+
+// markface(), unmarkface(), facemarked() -- primitives to flag or unflag a
+//   face of a tetrahedron.  From the last 3rd to 6th bits are used for
+//   face markers, e.g., the last third bit corresponds to loc = 0. 
+
+inline void tetgenmesh::markface(triface& t) {
+  ((int *) (t.tet))[elemmarkerindex] |= (4 << (t.ver & 3));
+}
+
+inline void tetgenmesh::unmarkface(triface& t) {
+  ((int *) (t.tet))[elemmarkerindex] &= ~(4 << (t.ver & 3));
+}
+
+inline bool tetgenmesh::facemarked(triface& t) {
+  return (((int *) (t.tet))[elemmarkerindex] & (4 << (t.ver & 3))) != 0;
+}
+
+// markedge(), unmarkedge(), edgemarked() -- primitives to flag or unflag an
+//   edge of a tetrahedron.  From the last 7th to 12th bits are used for
+//   edge markers, e.g., the last 7th bit corresponds to the 0th edge, etc. 
+//   Remark: The last 7th bit is marked by 2^6 = 64.
+
+inline void tetgenmesh::markedge(triface& t) {
+  ((int *) (t.tet))[elemmarkerindex] |= (int) (64 << ver2edge[(t).ver]);
+}
+
+inline void tetgenmesh::unmarkedge(triface& t) {
+  ((int *) (t.tet))[elemmarkerindex] &= ~(int) (64 << ver2edge[(t).ver]);
+}
+
+inline bool tetgenmesh::edgemarked(triface& t) {
+  return (((int *) (t.tet))[elemmarkerindex] & 
+           (int) (64 << ver2edge[(t).ver])) != 0;
+}
+
+// marktest2(), unmarktest2(), marktest2ed() -- primitives to flag and unflag
+//   a tetrahedron. The 13th bit (2^12 = 4096) is used for this flag.
+
+inline void tetgenmesh::marktest2(triface& t) {
+  ((int *) (t.tet))[elemmarkerindex] |= (int) (4096);
+}
+
+inline void tetgenmesh::unmarktest2(triface& t) {
+  ((int *) (t.tet))[elemmarkerindex] &= ~(int) (4096);
+}
+
+inline bool tetgenmesh::marktest2ed(triface& t) {
+  return (((int *) (t.tet))[elemmarkerindex] & (int) (4096)) != 0;
+}
+
+// elemcounter(), setelemcounter() -- primitives to read or ser a (small)
+//   integer counter in this tet. It is saved from the 16th bit. On 32 bit
+//   system, the range of the counter is [0, 2^15 = 32768]. 
+
+inline int tetgenmesh::elemcounter(triface& t) {
+  return (((int *) (t.tet))[elemmarkerindex]) >> 16;
+}
+
+inline void tetgenmesh::setelemcounter(triface& t, int value) {
+  int c = ((int *) (t.tet))[elemmarkerindex];
+  // Clear the old counter while keep the other flags.
+  c &= 65535; // sum_{i=0^15} 2^i
+  c |= (value << 16);
+  ((int *) (t.tet))[elemmarkerindex] = c;
+}
+
+inline void tetgenmesh::increaseelemcounter(triface& t) {
+  int c = elemcounter(t);
+  setelemcounter(t, c + 1);
+}
+
+inline void tetgenmesh::decreaseelemcounter(triface& t) {
+  int c = elemcounter(t);
+  setelemcounter(t, c - 1);
+}
+
+// ishulltet()  tests if t is a hull tetrahedron.
+
+inline bool tetgenmesh::ishulltet(triface& t) {
+  return (point) (t).tet[7] == dummypoint;
+}
+
+// isdeadtet()  tests if t is a tetrahedron is dead.
+
+inline bool tetgenmesh::isdeadtet(triface& t) {
+  return ((t.tet == NULL) || (t.tet[4] == NULL));
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// Primitives for subfaces and subsegments                                   //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+// Each subface contains three pointers to its neighboring subfaces, with
+//   edge versions.  To save memory, both information are kept in a single
+//   pointer. To make this possible, all subfaces are aligned to eight-byte
+//   boundaries, so that the last three bits of each pointer are zeros. An
+//   edge version (in the range 0 to 5) is compressed into the last three
+//   bits of each pointer by 'sencode()'.  'sdecode()' decodes a pointer,
+//   extracting an edge version and a pointer to the beginning of a subface.
+
+inline void tetgenmesh::sdecode(shellface sptr, face& s) {
+  s.shver = (int) ((uintptr_t) (sptr) & (uintptr_t) 7);
+  s.sh = (shellface *) ((uintptr_t) (sptr) ^ (uintptr_t) (s.shver));
+}
+
+inline tetgenmesh::shellface tetgenmesh::sencode(face& s) {
+  return (shellface) ((uintptr_t) s.sh | (uintptr_t) s.shver);
+}
+
+inline tetgenmesh::shellface tetgenmesh::sencode2(shellface *sh, int shver) {
+  return (shellface) ((uintptr_t) sh | (uintptr_t) shver);
+}
+
+// sbond() bonds two subfaces (s1) and (s2) together. s1 and s2 must refer
+//   to the same edge. No requirement is needed on their orientations.
+
+inline void tetgenmesh::sbond(face& s1, face& s2) 
+{
+  s1.sh[s1.shver >> 1] = sencode(s2);
+  s2.sh[s2.shver >> 1] = sencode(s1);
+}
+
+// sbond1() bonds s1 <== s2, i.e., after bonding, s1 is pointing to s2,
+//   but s2 is not pointing to s1.  s1 and s2 must refer to the same edge.
+//   No requirement is needed on their orientations.
+
+inline void tetgenmesh::sbond1(face& s1, face& s2) 
+{
+  s1.sh[s1.shver >> 1] = sencode(s2);
+}
+
+// Dissolve a subface bond (from one side).  Note that the other subface
+//   will still think it's connected to this subface.
+
+inline void tetgenmesh::sdissolve(face& s)
+{
+  s.sh[s.shver >> 1] = NULL;
+}
+
+// spivot() finds the adjacent subface (s2) for a given subface (s1).
+//   s1 and s2 share at the same edge.
+
+inline void tetgenmesh::spivot(face& s1, face& s2) 
+{
+  shellface sptr = s1.sh[s1.shver >> 1];
+  sdecode(sptr, s2);
+}
+
+inline void tetgenmesh::spivotself(face& s) 
+{
+  shellface sptr = s.sh[s.shver >> 1];
+  sdecode(sptr, s);
+}
+
+// These primitives determine or set the origin, destination, or apex
+//   of a subface with respect to the edge version.
+
+inline tetgenmesh::point tetgenmesh::sorg(face& s) 
+{
+  return (point) s.sh[sorgpivot[s.shver]];
+}
+
+inline tetgenmesh::point tetgenmesh::sdest(face& s) 
+{
+  return (point) s.sh[sdestpivot[s.shver]];
+}
+
+inline tetgenmesh::point tetgenmesh::sapex(face& s) 
+{
+  return (point) s.sh[sapexpivot[s.shver]];
+}
+
+inline void tetgenmesh::setsorg(face& s, point pointptr) 
+{
+  s.sh[sorgpivot[s.shver]] = (shellface) pointptr;
+}
+
+inline void tetgenmesh::setsdest(face& s, point pointptr) 
+{
+  s.sh[sdestpivot[s.shver]] = (shellface) pointptr;
+}
+
+inline void tetgenmesh::setsapex(face& s, point pointptr) 
+{
+  s.sh[sapexpivot[s.shver]] = (shellface) pointptr;
+}
+
+#define setshvertices(s, pa, pb, pc)\
+  setsorg(s, pa);\
+  setsdest(s, pb);\
+  setsapex(s, pc)
+
+// sesym()  reserves the direction of the lead edge.
+
+inline void tetgenmesh::sesym(face& s1, face& s2) 
+{
+  s2.sh = s1.sh;
+  s2.shver = (s1.shver ^ 1);  // Inverse the last bit.
+}
+
+inline void tetgenmesh::sesymself(face& s) 
+{
+  s.shver ^= 1;
+}
+
+// senext()  finds the next edge (counterclockwise) in the same orientation
+//   of this face.
+
+inline void tetgenmesh::senext(face& s1, face& s2) 
+{
+  s2.sh = s1.sh;
+  s2.shver = snextpivot[s1.shver];
+}
+
+inline void tetgenmesh::senextself(face& s) 
+{
+  s.shver = snextpivot[s.shver];
+}
+
+inline void tetgenmesh::senext2(face& s1, face& s2) 
+{
+  s2.sh = s1.sh;
+  s2.shver = snextpivot[snextpivot[s1.shver]];
+}
+
+inline void tetgenmesh::senext2self(face& s) 
+{
+  s.shver = snextpivot[snextpivot[s.shver]];
+}
+
+
+// Check or set a subface's maximum area bound.
+
+inline REAL tetgenmesh::areabound(face& s) 
+{
+  return ((REAL *) (s.sh))[areaboundindex];
+}
+
+inline void tetgenmesh::setareabound(face& s, REAL value) 
+{
+  ((REAL *) (s.sh))[areaboundindex] = value;
+}
+
+// These two primitives read or set a shell marker.  Shell markers are used
+//   to hold user boundary information.
+
+inline int tetgenmesh::shellmark(face& s) 
+{
+  return ((int *) (s.sh))[shmarkindex];
+}
+
+inline void tetgenmesh::setshellmark(face& s, int value) 
+{
+  ((int *) (s.sh))[shmarkindex] = value;
+}
+
+
+
+// sinfect(), sinfected(), suninfect() -- primitives to flag or unflag a
+//   subface. The last bit of ((int *) ((s).sh))[shmarkindex+1] is flagged.
+
+inline void tetgenmesh::sinfect(face& s) 
+{
+  ((int *) ((s).sh))[shmarkindex+1] = 
+    (((int *) ((s).sh))[shmarkindex+1] | (int) 1);
+}
+
+inline void tetgenmesh::suninfect(face& s) 
+{
+  ((int *) ((s).sh))[shmarkindex+1] = 
+    (((int *) ((s).sh))[shmarkindex+1] & ~(int) 1);
+}
+
+// Test a subface for viral infection.
+
+inline bool tetgenmesh::sinfected(face& s) 
+{
+  return (((int *) ((s).sh))[shmarkindex+1] & (int) 1) != 0;
+}
+
+// smarktest(), smarktested(), sunmarktest() -- primitives to flag or unflag
+//   a subface. The last 2nd bit of the integer is flagged.
+
+inline void tetgenmesh::smarktest(face& s) 
+{
+  ((int *) ((s).sh))[shmarkindex+1] = 
+    (((int *)((s).sh))[shmarkindex+1] | (int) 2);
+}
+
+inline void tetgenmesh::sunmarktest(face& s) 
+{
+  ((int *) ((s).sh))[shmarkindex+1] = 
+    (((int *)((s).sh))[shmarkindex+1] & ~(int)2);
+}
+
+inline bool tetgenmesh::smarktested(face& s) 
+{
+  return ((((int *) ((s).sh))[shmarkindex+1] & (int) 2) != 0);
+}
+
+// smarktest2(), smarktest2ed(), sunmarktest2() -- primitives to flag or 
+//   unflag a subface. The last 3rd bit of the integer is flagged.
+
+inline void tetgenmesh::smarktest2(face& s) 
+{
+  ((int *) ((s).sh))[shmarkindex+1] = 
+    (((int *)((s).sh))[shmarkindex+1] | (int) 4);
+}
+
+inline void tetgenmesh::sunmarktest2(face& s) 
+{
+  ((int *) ((s).sh))[shmarkindex+1] = 
+    (((int *)((s).sh))[shmarkindex+1] & ~(int)4);
+}
+
+inline bool tetgenmesh::smarktest2ed(face& s) 
+{
+  return ((((int *) ((s).sh))[shmarkindex+1] & (int) 4) != 0);
+}
+
+// The last 4th bit of ((int *) ((s).sh))[shmarkindex+1] is flagged.
+
+inline void tetgenmesh::smarktest3(face& s) 
+{
+  ((int *) ((s).sh))[shmarkindex+1] = 
+    (((int *)((s).sh))[shmarkindex+1] | (int) 8);
+}
+
+inline void tetgenmesh::sunmarktest3(face& s) 
+{
+  ((int *) ((s).sh))[shmarkindex+1] = 
+    (((int *)((s).sh))[shmarkindex+1] & ~(int)8);
+}
+
+inline bool tetgenmesh::smarktest3ed(face& s) 
+{
+  return ((((int *) ((s).sh))[shmarkindex+1] & (int) 8) != 0);
+}
+
+
+// Each facet has a unique index (automatically indexed). Starting from '0'.
+// We save this index in the same field of the shell type. 
+
+inline void tetgenmesh::setfacetindex(face& s, int value)
+{
+  ((int *) (s.sh))[shmarkindex + 2] = value;
+}
+
+inline int tetgenmesh::getfacetindex(face& s)
+{
+  return ((int *) (s.sh))[shmarkindex + 2];
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// Primitives for interacting between tetrahedra and subfaces                //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+// tsbond() bond a tetrahedron (t) and a subface (s) together.
+// Note that t and s must be the same face and the same edge. Moreover,
+//   t and s have the same orientation. 
+// Since the edge number in t and in s can be any number in {0,1,2}. We bond
+//   the edge in s which corresponds to t's 0th edge, and vice versa.
+
+inline void tetgenmesh::tsbond(triface& t, face& s)
+{
+  if ((t).tet[9] == NULL) {
+    // Allocate space for this tet.
+    (t).tet[9] = (tetrahedron) tet2subpool->alloc();
+    // Initialize.
+    for (int i = 0; i < 4; i++) {
+      ((shellface *) (t).tet[9])[i] = NULL;
+    }
+  }
+  // Bond t <== s.
+  ((shellface *) (t).tet[9])[(t).ver & 3] = 
+    sencode2((s).sh, tsbondtbl[t.ver][s.shver]);
+  // Bond s <== t.
+  s.sh[9 + ((s).shver & 1)] = 
+    (shellface) encode2((t).tet, stbondtbl[t.ver][s.shver]);
+}
+
+// tspivot() finds a subface (s) abutting on the given tetrahdera (t).
+//   Return s.sh = NULL if there is no subface at t. Otherwise, return
+//   the subface s, and s and t must be at the same edge wth the same
+//   orientation.
+
+inline void tetgenmesh::tspivot(triface& t, face& s) 
+{
+  if ((t).tet[9] == NULL) {
+    (s).sh = NULL;
+    return;
+  }
+  // Get the attached subface s.
+  sdecode(((shellface *) (t).tet[9])[(t).ver & 3], (s));
+  (s).shver = tspivottbl[t.ver][s.shver];
+}
+
+// Quickly check if the handle (t, v) is a subface.
+#define issubface(t) \
+  ((t).tet[9] && ((t).tet[9])[(t).ver & 3])
+
+// stpivot() finds a tetrahedron (t) abutting a given subface (s).
+//   Return the t (if it exists) with the same edge and the same
+//   orientation of s.
+
+inline void tetgenmesh::stpivot(face& s, triface& t) 
+{
+  decode((tetrahedron) s.sh[9 + (s.shver & 1)], t);
+  if ((t).tet == NULL) {
+    return;
+  }
+  (t).ver = stpivottbl[t.ver][s.shver];
+}
+
+// Quickly check if this subface is attached to a tetrahedron.
+
+#define isshtet(s) \
+  ((s).sh[9 + ((s).shver & 1)])
+
+// tsdissolve() dissolve a bond (from the tetrahedron side).
+
+inline void tetgenmesh::tsdissolve(triface& t) 
+{
+  if ((t).tet[9] != NULL) {
+    ((shellface *) (t).tet[9])[(t).ver & 3] = NULL;
+  }
+}
+
+// stdissolve() dissolve a bond (from the subface side).
+
+inline void tetgenmesh::stdissolve(face& s) 
+{
+  (s).sh[9] = NULL;
+  (s).sh[10] = NULL;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// Primitives for interacting between subfaces and segments                  //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+// ssbond() bond a subface to a subsegment.
+
+inline void tetgenmesh::ssbond(face& s, face& edge) 
+{
+  s.sh[6 + (s.shver >> 1)] = sencode(edge);
+  edge.sh[0] = sencode(s);
+}
+
+inline void tetgenmesh::ssbond1(face& s, face& edge) 
+{
+  s.sh[6 + (s.shver >> 1)] = sencode(edge);
+  //edge.sh[0] = sencode(s);
+}
+
+// ssdisolve() dissolve a bond (from the subface side)
+
+inline void tetgenmesh::ssdissolve(face& s) 
+{
+  s.sh[6 + (s.shver >> 1)] = NULL;
+}
+
+// sspivot() finds a subsegment abutting a subface.
+
+inline void tetgenmesh::sspivot(face& s, face& edge) 
+{
+  sdecode((shellface) s.sh[6 + (s.shver >> 1)], edge);
+}
+
+// Quickly check if the edge is a subsegment.
+
+#define isshsubseg(s) \
+  ((s).sh[6 + ((s).shver >> 1)])
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// Primitives for interacting between tetrahedra and segments                //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+inline void tetgenmesh::tssbond1(triface& t, face& s)
+{
+  if ((t).tet[8] == NULL) {
+    // Allocate space for this tet.
+    (t).tet[8] = (tetrahedron) tet2segpool->alloc();
+    // Initialization.
+    for (int i = 0; i < 6; i++) {
+      ((shellface *) (t).tet[8])[i] = NULL;
+    }
+  }
+  ((shellface *) (t).tet[8])[ver2edge[(t).ver]] = sencode((s)); 
+}
+
+inline void tetgenmesh::sstbond1(face& s, triface& t) 
+{
+  ((tetrahedron *) (s).sh)[9] = encode(t);
+}
+
+inline void tetgenmesh::tssdissolve1(triface& t)
+{
+  if ((t).tet[8] != NULL) {
+    ((shellface *) (t).tet[8])[ver2edge[(t).ver]] = NULL;
+  }
+}
+
+inline void tetgenmesh::sstdissolve1(face& s) 
+{
+  ((tetrahedron *) (s).sh)[9] = NULL;
+}
+
+inline void tetgenmesh::tsspivot1(triface& t, face& s)
+{
+  if ((t).tet[8] != NULL) {
+    sdecode(((shellface *) (t).tet[8])[ver2edge[(t).ver]], s);
+  } else {
+    (s).sh = NULL;
+  }
+}
+
+// Quickly check whether 't' is a segment or not.
+
+#define issubseg(t) \
+  ((t).tet[8] && ((t).tet[8])[ver2edge[(t).ver]])
+
+inline void tetgenmesh::sstpivot1(face& s, triface& t) 
+{
+  decode((tetrahedron) s.sh[9], t);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// Primitives for points                                                     //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+inline int tetgenmesh::pointmark(point pt) { 
+  return ((int *) (pt))[pointmarkindex]; 
+}
+
+inline void tetgenmesh::setpointmark(point pt, int value) {
+  ((int *) (pt))[pointmarkindex] = value;
+}
+
+
+// These two primitives set and read the type of the point.
+
+inline enum tetgenmesh::verttype tetgenmesh::pointtype(point pt) {
+  return (enum verttype) (((int *) (pt))[pointmarkindex + 1] >> (int) 8);
+}
+
+inline void tetgenmesh::setpointtype(point pt, enum verttype value) {
+  ((int *) (pt))[pointmarkindex + 1] = 
+    ((int) value << 8) + (((int *) (pt))[pointmarkindex + 1] & (int) 255);
+}
+
+// Read and set the geometry tag of the point (used by -s option).
+
+inline int tetgenmesh::pointgeomtag(point pt) { 
+  return ((int *) (pt))[pointmarkindex + 2]; 
+}
+
+inline void tetgenmesh::setpointgeomtag(point pt, int value) {
+  ((int *) (pt))[pointmarkindex + 2] = value;
+}
+
+// Read and set the u,v coordinates of the point (used by -s option).
+
+inline REAL tetgenmesh::pointgeomuv(point pt, int i) {
+  return pt[pointparamindex + i];
+}
+
+inline void tetgenmesh::setpointgeomuv(point pt, int i, REAL value) {
+  pt[pointparamindex + i] = value;
+}
+
+// pinfect(), puninfect(), pinfected() -- primitives to flag or unflag
+//   a point. The last bit of the integer '[pointindex+1]' is flagged.
+
+inline void tetgenmesh::pinfect(point pt) {
+  ((int *) (pt))[pointmarkindex + 1] |= (int) 1;
+}
+
+inline void tetgenmesh::puninfect(point pt) {
+  ((int *) (pt))[pointmarkindex + 1] &= ~(int) 1;
+}
+
+inline bool tetgenmesh::pinfected(point pt) {
+  return (((int *) (pt))[pointmarkindex + 1] & (int) 1) != 0;
+}
+
+// pmarktest(), punmarktest(), pmarktested() -- more primitives to 
+//   flag or unflag a point. 
+
+inline void tetgenmesh::pmarktest(point pt) {
+  ((int *) (pt))[pointmarkindex + 1] |= (int) 2;
+}
+
+inline void tetgenmesh::punmarktest(point pt) {
+  ((int *) (pt))[pointmarkindex + 1] &= ~(int) 2;
+}
+
+inline bool tetgenmesh::pmarktested(point pt) {
+  return (((int *) (pt))[pointmarkindex + 1] & (int) 2) != 0;
+}
+
+inline void tetgenmesh::pmarktest2(point pt) {
+  ((int *) (pt))[pointmarkindex + 1] |= (int) 4;
+}
+
+inline void tetgenmesh::punmarktest2(point pt) {
+  ((int *) (pt))[pointmarkindex + 1] &= ~(int) 4;
+}
+
+inline bool tetgenmesh::pmarktest2ed(point pt) {
+  return (((int *) (pt))[pointmarkindex + 1] & (int) 4) != 0;
+}
+
+inline void tetgenmesh::pmarktest3(point pt) {
+  ((int *) (pt))[pointmarkindex + 1] |= (int) 8;
+}
+
+inline void tetgenmesh::punmarktest3(point pt) {
+  ((int *) (pt))[pointmarkindex + 1] &= ~(int) 8;
+}
+
+inline bool tetgenmesh::pmarktest3ed(point pt) {
+  return (((int *) (pt))[pointmarkindex + 1] & (int) 8) != 0;
+}
+
+// These following primitives set and read a pointer to a tetrahedron
+//   a subface/subsegment, a point, or a tet of background mesh.
+
+inline tetgenmesh::tetrahedron tetgenmesh::point2tet(point pt) {
+  return ((tetrahedron *) (pt))[point2simindex];
+}
+
+inline void tetgenmesh::setpoint2tet(point pt, tetrahedron value) {
+  ((tetrahedron *) (pt))[point2simindex] = value;
+}
+
+inline tetgenmesh::point tetgenmesh::point2ppt(point pt) {
+  return (point) ((tetrahedron *) (pt))[point2simindex + 1];
+}
+
+inline void tetgenmesh::setpoint2ppt(point pt, point value) {
+  ((tetrahedron *) (pt))[point2simindex + 1] = (tetrahedron) value;
+}
+
+inline tetgenmesh::shellface tetgenmesh::point2sh(point pt) {
+  return (shellface) ((tetrahedron *) (pt))[point2simindex + 2];
+}
+
+inline void tetgenmesh::setpoint2sh(point pt, shellface value) {
+  ((tetrahedron *) (pt))[point2simindex + 2] = (tetrahedron) value;
+}
+
+
+inline tetgenmesh::tetrahedron tetgenmesh::point2bgmtet(point pt) {
+  return ((tetrahedron *) (pt))[point2simindex + 3];
+}
+
+inline void tetgenmesh::setpoint2bgmtet(point pt, tetrahedron value) {
+  ((tetrahedron *) (pt))[point2simindex + 3] = value;
+}
+
+
+// The primitives for saving and getting the insertion radius.
+inline void tetgenmesh::setpointinsradius(point pt, REAL value)
+{
+  pt[pointinsradiusindex] = value;
+}
+
+inline REAL tetgenmesh::getpointinsradius(point pt)
+{
+  return pt[pointinsradiusindex];
+}
+
+inline bool tetgenmesh::issteinerpoint(point pt) {
+ return (pointtype(pt) == FREESEGVERTEX) || (pointtype(pt) == FREEFACETVERTEX)
+        || (pointtype(pt) == FREEVOLVERTEX);
+}
+
+// point2tetorg()    Get the tetrahedron whose origin is the point.
+
+inline void tetgenmesh::point2tetorg(point pa, triface& searchtet)
+{
+  decode(point2tet(pa), searchtet);
+  if ((point) searchtet.tet[4] == pa) {
+    searchtet.ver = 11;
+  } else if ((point) searchtet.tet[5] == pa) {
+    searchtet.ver = 3;
+  } else if ((point) searchtet.tet[6] == pa) {
+    searchtet.ver = 7;
+  } else {
+    searchtet.ver = 0;
+  }
+}
+
+// point2shorg()    Get the subface/segment whose origin is the point.
+
+inline void tetgenmesh::point2shorg(point pa, face& searchsh)
+{
+  sdecode(point2sh(pa), searchsh);
+  if ((point) searchsh.sh[3] == pa) {
+    searchsh.shver = 0;
+  } else if ((point) searchsh.sh[4] == pa) {
+    searchsh.shver = (searchsh.sh[5] != NULL ? 2 : 1); 
+  } else {
+    searchsh.shver = 4;
+  }
+}
+
+// farsorg()    Return the origin of the subsegment.
+// farsdest()   Return the destination of the subsegment.
+
+inline tetgenmesh::point tetgenmesh::farsorg(face& s)
+{
+  face travesh, neighsh;
+
+  travesh = s;
+  while (1) {
+    senext2(travesh, neighsh);
+    spivotself(neighsh); 
+    if (neighsh.sh == NULL) break;
+    if (sorg(neighsh) != sorg(travesh)) sesymself(neighsh);
+    senext2(neighsh, travesh); 
+  }
+  return sorg(travesh);
+}
+
+inline tetgenmesh::point tetgenmesh::farsdest(face& s) 
+{
+  face travesh, neighsh;
+
+  travesh = s;
+  while (1) {
+    senext(travesh, neighsh);
+    spivotself(neighsh); 
+    if (neighsh.sh == NULL) break;
+    if (sdest(neighsh) != sdest(travesh)) sesymself(neighsh);
+    senext(neighsh, travesh); 
+  }
+  return sdest(travesh);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// Linear algebra operators.                                                 //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+// dot() returns the dot product: v1 dot v2.
+inline REAL tetgenmesh::dot(REAL* v1, REAL* v2) 
+{
+  return v1[0] * v2[0] + v1[1] * v2[1] + v1[2] * v2[2];
+}
+
+// cross() computes the cross product: n = v1 cross v2.
+inline void tetgenmesh::cross(REAL* v1, REAL* v2, REAL* n) 
+{
+  n[0] =   v1[1] * v2[2] - v2[1] * v1[2];
+  n[1] = -(v1[0] * v2[2] - v2[0] * v1[2]);
+  n[2] =   v1[0] * v2[1] - v2[0] * v1[1];
+}
+
+// distance() computes the Euclidean distance between two points.
+inline REAL tetgenmesh::distance(REAL* p1, REAL* p2)
+{
+  return sqrt((p2[0] - p1[0]) * (p2[0] - p1[0]) +
+              (p2[1] - p1[1]) * (p2[1] - p1[1]) +
+              (p2[2] - p1[2]) * (p2[2] - p1[2]));
+}
+
+inline REAL tetgenmesh::norm2(REAL x, REAL y, REAL z)
+{
+  return (x) * (x) + (y) * (y) + (z) * (z);
+}
+
+#endif // tetgenBRH